More javadoc
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 7 Nov 2007 06:40:04 +0000 (06:40 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 7 Nov 2007 06:40:04 +0000 (06:40 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@13 59b500cc-1b3d-0410-9834-0bbf25fbcc57

core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
core/src/com/google/zxing/qrcode/decoder/BitSource.java
core/src/com/google/zxing/qrcode/decoder/DataBlock.java
core/src/com/google/zxing/qrcode/decoder/DataMask.java
core/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
core/src/com/google/zxing/qrcode/decoder/Decoder.java
core/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
core/src/com/google/zxing/qrcode/decoder/FormatInformation.java
core/src/com/google/zxing/qrcode/decoder/Mode.java
core/src/com/google/zxing/qrcode/decoder/Version.java

index 7e20d75..dffa6c6 100644 (file)
@@ -29,8 +29,8 @@ final class BitMatrixParser {
   private FormatInformation parsedFormatInfo;\r
 \r
   /**\r
-   * @throws com.google.zxing.ReaderException\r
-   *          if dimension is not >= 21 and 1 mod 4\r
+   * @param bitMatrix {@link BitMatrix} to parse\r
+   * @throws ReaderException if dimension is not >= 21 and 1 mod 4\r
    */\r
   BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {\r
     int dimension = bitMatrix.getDimension();\r
@@ -40,6 +40,13 @@ final class BitMatrixParser {
     this.bitMatrix = bitMatrix;\r
   }\r
 \r
+  /**\r
+   * <p>Reads format information from one of its two locations within the QR Code.</p>\r
+   *\r
+   * @return {@link FormatInformation} encapsulating the QR Code's format info\r
+   * @throws ReaderException if both format information locations cannot be parsed as\r
+   *  the valid encoding of format information\r
+   */\r
   FormatInformation readFormatInformation() throws ReaderException {\r
 \r
     if (parsedFormatInfo != null) {\r
@@ -83,6 +90,13 @@ final class BitMatrixParser {
     throw new ReaderException("Could not decode format information");\r
   }\r
 \r
+  /**\r
+   * <p>Reads version information from one of its two locations within the QR Code.</p>\r
+   *\r
+   * @return {@link Version} encapsulating the QR Code's version\r
+   * @throws ReaderException if both version information locations cannot be parsed as\r
+   *  the valid encoding of version information\r
+   */\r
   Version readVersion() throws ReaderException {\r
 \r
     if (parsedVersion != null) {\r
@@ -130,11 +144,21 @@ final class BitMatrixParser {
     return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;\r
   }\r
 \r
+  /**\r
+   * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the\r
+   * correct order in order to reconstitute the codewords bytes contained within the\r
+   * QR Code.</p>\r
+   *\r
+   * @return bytes encoded within the QR Code\r
+   * @throws ReaderException if the exact number of bytes expected is not read\r
+   */\r
   byte[] readCodewords() throws ReaderException {\r
 \r
     FormatInformation formatInfo = readFormatInformation();\r
     Version version = readVersion();\r
 \r
+    // Get the data mask for the format used in this QR Code. This will exclude\r
+    // some bits from reading as we wind through the bit matrix.\r
     DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());\r
     int dimension = bitMatrix.getDimension();\r
     dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);\r
@@ -146,21 +170,26 @@ final class BitMatrixParser {
     int resultOffset = 0;\r
     int currentByte = 0;\r
     int bitsRead = 0;\r
+    // Read columns in pairs, from right to left\r
     for (int j = dimension - 1; j > 0; j -= 2) {\r
       if (j == 6) {\r
         // Skip whole column with vertical alignment pattern;\r
         // saves time and makes the other code proceed more cleanly\r
         j--;\r
       }\r
+      // Read alternatingly from bottom to top then top to bottom\r
       for (int count = 0; count < dimension; count++) {\r
         int i = readingUp ? dimension - 1 - count : count;\r
         for (int col = 0; col < 2; col++) {\r
+          // Ignore bits covered by the function pattern\r
           if (!functionPattern.get(i, j - col)) {\r
+            // Read a bit\r
             bitsRead++;\r
             currentByte <<= 1;\r
             if (bitMatrix.get(i, j - col)) {\r
               currentByte |= 1;\r
             }\r
+            // If we've made a whole byte, save it off\r
             if (bitsRead == 8) {\r
               result[resultOffset++] = (byte) currentByte;\r
               bitsRead = 0;\r
index b58ec92..7fc077d 100755 (executable)
 package com.google.zxing.qrcode.decoder;\r
 \r
 /**\r
- * This provides an easy abstraction to read bits at a time from a sequence of bytes, where the\r
- * number of bits read is not often a multiple of 8.\r
+ * <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the\r
+ * number of bits read is not often a multiple of 8.</p>\r
+ *\r
+ * <p>This class is not thread-safe.</p>\r
  *\r
  * @author srowen@google.com (Sean Owen)\r
  */\r
@@ -28,11 +30,18 @@ final class BitSource {
   private int byteOffset;\r
   private int bitOffset;\r
 \r
+  /**\r
+   * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.\r
+   *  Bits are read within a byte from most-significant to least-significant bit.\r
+   */\r
   BitSource(byte[] bytes) {\r
     this.bytes = bytes;\r
   }\r
 \r
   /**\r
+   * @param numBits number of bits to read\r
+   * @return int representing the bits read. The bits will appear as the least-significant\r
+   *  bits of the int\r
    * @throws IllegalArgumentException if numBits isn't in [1,32]\r
    */\r
   int readBits(int numBits) {\r
@@ -77,6 +86,9 @@ final class BitSource {
     return result;\r
   }\r
 \r
+  /**\r
+   * @return number of bits that can be read successfully\r
+   */\r
   int available() {\r
     return 8 * (bytes.length - byteOffset) - bitOffset;\r
   }\r
index 2737d80..ca146cc 100755 (executable)
 package com.google.zxing.qrcode.decoder;\r
 \r
 /**\r
+ * <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into\r
+ * multiple blocks, each of which is a unit of data and error-correction codewords. Each\r
+ * is represented by an instance of this class.</p>\r
+ *\r
  * @author srowen@google.com (Sean Owen)\r
  */\r
 final class DataBlock {\r
@@ -29,15 +33,32 @@ final class DataBlock {
     this.codewords = codewords;\r
   }\r
 \r
+  /**\r
+   * <p>When QR Codes use multiple data blocks, they are actually interleave the bytes of each of them.\r
+   * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This\r
+   * method will separate the data into original blocks.</p>\r
+   *\r
+   * @param rawCodewords bytes as read directly from the QR Code\r
+   * @param version version of the QR Code\r
+   * @param ecLevel error-correction level of the QR Code\r
+   * @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the\r
+   *  QR Code\r
+   */\r
   static DataBlock[] getDataBlocks(byte[] rawCodewords,\r
                                    Version version,\r
                                    ErrorCorrectionLevel ecLevel) {\r
+    // Figure out the number and size of data blocks used by this version and\r
+    // error correction level\r
     Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\r
+\r
+    // First count the total number of data blocks\r
     int totalBlocks = 0;\r
     Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();\r
     for (int i = 0; i < ecBlockArray.length; i++) {\r
       totalBlocks += ecBlockArray[i].getCount();\r
     }\r
+\r
+    // Now establish DataBlocks of the appropriate size and number of data codewords\r
     DataBlock[] result = new DataBlock[totalBlocks];\r
     int numResultBlocks = 0;\r
     for (int j = 0; j < ecBlockArray.length; j++) {\r
@@ -45,8 +66,7 @@ final class DataBlock {
       for (int i = 0; i < ecBlock.getCount(); i++) {\r
         int numDataCodewords = ecBlock.getDataCodewords();\r
         int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;\r
-        result[numResultBlocks++] =\r
-            new DataBlock(numDataCodewords, new byte[numBlockCodewords]);\r
+        result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);\r
       }\r
     }\r
 \r
@@ -55,21 +75,18 @@ final class DataBlock {
     int shorterBlocksTotalCodewords = result[0].codewords.length;\r
     int longerBlocksStartAt = result.length - 1;\r
     while (longerBlocksStartAt >= 0) {\r
-      int numCodewords =\r
-          result[longerBlocksStartAt].codewords.length;\r
+      int numCodewords = result[longerBlocksStartAt].codewords.length;\r
       if (numCodewords == shorterBlocksTotalCodewords) {\r
         break;\r
       }\r
       if (numCodewords != shorterBlocksTotalCodewords + 1) {\r
-        throw new IllegalStateException(\r
-            "Data block sizes differ by more than 1");\r
+        throw new IllegalStateException("Data block sizes differ by more than 1");\r
       }\r
       longerBlocksStartAt--;\r
     }\r
     longerBlocksStartAt++;\r
 \r
-    int shorterBlocksNumDataCodewords =\r
-        shorterBlocksTotalCodewords - ecBlocks.getECCodewords();\r
+    int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords();\r
     // The last elements of result may be 1 element longer;\r
     // first fill out as many elements as all of them have\r
     int rawCodewordsOffset = 0;\r
@@ -80,8 +97,7 @@ final class DataBlock {
     }\r
     // Fill out the last data block in the longer ones\r
     for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {\r
-      result[j].codewords[shorterBlocksNumDataCodewords] =\r
-          rawCodewords[rawCodewordsOffset++];\r
+      result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];\r
     }\r
     // Now add in error correction blocks\r
     int max = result[0].codewords.length;\r
index 68f1b67..225d463 100755 (executable)
 package com.google.zxing.qrcode.decoder;\r
 \r
 /**\r
- * Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations\r
+ * <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations\r
  * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,\r
  * including areas used for finder patterns, timing patterns, etc. These areas should be unused\r
- * after the point they are unmasked anyway.\r
+ * after the point they are unmasked anyway.</p>\r
  *\r
- * Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position\r
- * and j is row position. In fact, as the text says, i is row position and j is column position.\r
+ * <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position\r
+ * and j is row position. In fact, as the text says, i is row position and j is column position.</p>\r
  *\r
  * @author srowen@google.com (Sean Owen)\r
  */\r
@@ -46,8 +46,20 @@ abstract class DataMask {
   private DataMask() {\r
   }\r
 \r
+  /**\r
+   * <p>Implementations of this method reverse the data masking process applied to a QR Code and\r
+   * make its bits ready to read.</p>\r
+   *\r
+   * @param bits representation of QR Code bits from {@link com.google.zxing.common.BitMatrix#getBits()}\r
+   * @param dimension dimension of QR Code, represented by bits, being unmasked\r
+   */\r
   abstract void unmaskBitMatrix(int[] bits, int dimension);\r
 \r
+  /**\r
+   * @param reference a value between 0 and 7 indicating one of the eight possible\r
+   *  data mask patterns a QR Code may use\r
+   * @return {@link DataMask} encapsulating the data mask pattern\r
+   */\r
   static DataMask forReference(int reference) {\r
     if (reference < 0 || reference > 7) {\r
       throw new IllegalArgumentException();\r
index 8fa91f4..08cc5bd 100644 (file)
@@ -21,7 +21,10 @@ import com.google.zxing.ReaderException;
 import java.io.UnsupportedEncodingException;
 
 /**
- * See ISO 18004:2006, 6.4.3 - 6.4.7
+ * <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
+ * in one QR Code. This class decodes the bits back into text.</p>
+ *
+ * <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
  *
  * @author srowen@google.com (Sean Owen)
  */
@@ -54,8 +57,9 @@ final class DecodedBitStreamParser {
     Mode mode;
     do {
       // While still another segment to read...
-      mode = Mode.forBits(bits.readBits(4));
+      mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
       if (!mode.equals(Mode.TERMINATOR)) {
+        // How many characters will follow, encoded in this mode?
         int count = bits.readBits(mode.getCharacterCountBits(version));
         if (mode.equals(Mode.NUMERIC)) {
           decodeNumericSegment(bits, result, count);
@@ -66,11 +70,12 @@ final class DecodedBitStreamParser {
         } else if (mode.equals(Mode.KANJI)) {
           decodeKanjiSegment(bits, result, count);
         } else {
-          throw new ReaderException("Unsupported mode indicator: " + mode);
+          throw new ReaderException("Unsupported mode indicator");
         }
       }
     } while (!mode.equals(Mode.TERMINATOR));
 
+    // I thought it wasn't allowed to leave extra bytes after the terminator but it happens
     /*
     int bitsLeft = bits.available();
     if (bitsLeft > 0) {
@@ -85,9 +90,12 @@ final class DecodedBitStreamParser {
   private static void decodeKanjiSegment(BitSource bits,
                                          StringBuffer result,
                                          int count) throws ReaderException {
+    // Each character will require 2 bytes. Read the characters as 2-byte pairs
+    // and decode as Shift_JIS afterwards
     byte[] buffer = new byte[2 * count];
     int offset = 0;
     while (count > 0) {
+      // Each 13 bits encodes a 2-byte character
       int twoBytes = bits.readBits(13);
       int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
       if (assembledTwoBytes < 0x01F00) {
@@ -144,7 +152,7 @@ final class DecodedBitStreamParser {
       count -= 2;
     }
     if (count == 1) {
-      // special case on char left
+      // special case: one character left
       result.append(ALPHANUMERIC_CHARS[bits.readBits(6)]);
     }
   }
@@ -152,7 +160,9 @@ final class DecodedBitStreamParser {
   private static void decodeNumericSegment(BitSource bits,
                                            StringBuffer result,
                                            int count) throws ReaderException {
+    // Read three digits at a time
     while (count >= 3) {
+      // Each 10 bits encodes three digits
       int threeDigitsBits = bits.readBits(10);
       if (threeDigitsBits >= 1000) {
         throw new ReaderException("Illegal value for 3-digit unit: " + threeDigitsBits);
@@ -163,6 +173,7 @@ final class DecodedBitStreamParser {
       count -= 3;
     }
     if (count == 2) {
+      // Two digits left over to read, encoded in 7 bits
       int twoDigitsBits = bits.readBits(7);
       if (twoDigitsBits >= 100) {
         throw new ReaderException("Illegal value for 2-digit unit: " + twoDigitsBits);
@@ -170,6 +181,7 @@ final class DecodedBitStreamParser {
       result.append(ALPHANUMERIC_CHARS[twoDigitsBits / 10]);
       result.append(ALPHANUMERIC_CHARS[twoDigitsBits % 10]);
     } else if (count == 1) {
+      // One digit left over to read
       int digitBits = bits.readBits(4);
       if (digitBits >= 10) {
         throw new ReaderException("Illegal value for digit unit: " + digitBits);
index a37128f..8279eea 100644 (file)
@@ -22,6 +22,9 @@ import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
 import com.google.zxing.common.reedsolomon.ReedSolomonException;\r
 \r
 /**\r
+ * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting\r
+ * the QR Code from an image.</p>\r
+ *\r
  * @author srowen@google.com (Sean Owen)\r
  */\r
 public final class Decoder {\r
@@ -29,6 +32,14 @@ public final class Decoder {
   private Decoder() {\r
   }\r
 \r
+  /**\r
+   * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.\r
+   * "true" is taken to mean a black module.</p>\r
+   *\r
+   * @param image booleans representing white/black QR Code modules\r
+   * @return text encoded within the QR Code\r
+   * @throws ReaderException if the QR Code cannot be decoded\r
+   */\r
   public static String decode(boolean[][] image) throws ReaderException {\r
     int dimension = image.length;\r
     BitMatrix bits = new BitMatrix(dimension);\r
@@ -42,18 +53,34 @@ public final class Decoder {
     return decode(bits);\r
   }\r
 \r
+  /**\r
+   * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>\r
+   *\r
+   * @param bits booleans representing white/black QR Code modules\r
+   * @return text encoded within the QR Code\r
+   * @throws ReaderException if the QR Code cannot be decoded\r
+   */\r
   public static String decode(BitMatrix bits) throws ReaderException {\r
+\r
+    // Construct a parser and read version, error-correction level\r
     BitMatrixParser parser = new BitMatrixParser(bits);\r
     Version version = parser.readVersion();\r
     ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();\r
+\r
+    // Read codewords\r
     byte[] codewords = parser.readCodewords();\r
+    // Separate into data blocks\r
     DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);\r
+\r
+    // Count total number of data bytes\r
     int totalBytes = 0;\r
     for (int i = 0; i < dataBlocks.length; i++) {\r
       totalBytes += dataBlocks[i].getNumDataCodewords();\r
     }\r
     byte[] resultBytes = new byte[totalBytes];\r
     int resultOffset = 0;\r
+\r
+    // Error-correct and copy data blocks together into a stream of bytes\r
     for (int j = 0; j < dataBlocks.length; j++) {\r
       DataBlock dataBlock = dataBlocks[j];\r
       byte[] codewordBytes = dataBlock.getCodewords();\r
@@ -64,12 +91,21 @@ public final class Decoder {
       }\r
     }\r
 \r
+    // Decode the contents of that stream of bytes\r
     return DecodedBitStreamParser.decode(resultBytes, version);\r
   }\r
 \r
-  private static void correctErrors(byte[] codewordBytes, int numDataCodewords)\r
-      throws ReaderException {\r
+  /**\r
+   * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\r
+   * correct the errors in-place using Reed-Solomon error correction.</p>\r
+   *\r
+   * @param codewordBytes data and error correction codewords\r
+   * @param numDataCodewords number of codewords that are data bytes\r
+   * @throws ReaderException if error correction fails\r
+   */\r
+  private static void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ReaderException {\r
     int numCodewords = codewordBytes.length;\r
+    // First read into an array of ints\r
     int[] codewordsInts = new int[numCodewords];\r
     for (int i = 0; i < numCodewords; i++) {\r
       codewordsInts[i] = codewordBytes[i] & 0xFF;\r
@@ -80,6 +116,8 @@ public final class Decoder {
     } catch (ReedSolomonException rse) {\r
       throw new ReaderException(rse.toString());\r
     }\r
+    // Copy back into array of bytes -- only need to worry about the bytes that were data\r
+    // We don't care about errors in the error-correction codewords\r
     for (int i = 0; i < numDataCodewords; i++) {\r
       codewordBytes[i] = (byte) codewordsInts[i];\r
     }\r
index 9b9adb5..93b0a68 100644 (file)
@@ -39,7 +39,7 @@ final class ErrorCorrectionLevel {
 
   private final int ordinal;
 
-  private ErrorCorrectionLevel(final int ordinal) {
+  private ErrorCorrectionLevel(int ordinal) {
     this.ordinal = ordinal;
   }
 
@@ -47,6 +47,10 @@ final class ErrorCorrectionLevel {
     return ordinal;
   }
 
+  /**
+   * @param bits int containing the two bits encoding a QR Code's error correction level
+   * @return {@link ErrorCorrectionLevel} representing the encoded error correction level
+   */
   static ErrorCorrectionLevel forBits(int bits) {
     return FOR_BITS[bits];
   }
index e483ebd..c72154a 100644 (file)
 package com.google.zxing.qrcode.decoder;
 
 /**
+ * <p>Encapsulates a QR Code's format information, including the data mask used and
+ * error correction level.</p>
+ *
  * @author srowen@google.com (Sean Owen)
+ * @see DataMask
+ * @see ErrorCorrectionLevel
  */
 final class FormatInformation {
 
@@ -61,6 +66,7 @@ final class FormatInformation {
       {0x2BED, 0x1F},
   };
 
+  /** Offset i holds the number of 1 bits in the binary representation of i */
   private static final int[] BITS_SET_IN_HALF_BYTE =
       new int[]{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
 
@@ -69,16 +75,15 @@ final class FormatInformation {
 
   private FormatInformation(int formatInfo) {
     // Bits 3,4
-    errorCorrectionLevel =
-        ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
+    errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
     // Bottom 3 bits
     dataMask = (byte) (formatInfo & 0x07);
   }
 
   static int numBitsDiffering(int a, int b) {
-    a ^= b;
-    return
-        BITS_SET_IN_HALF_BYTE[a & 0x0F] +
+    a ^= b; // a now has a 1 bit exactly where its bit differs with b's
+    // Count bits set quickly with a series of lookups:
+    return  BITS_SET_IN_HALF_BYTE[a & 0x0F] +
             BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
             BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
             BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
@@ -88,6 +93,11 @@ final class FormatInformation {
             BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
   }
 
+  /**
+   *
+   * @param rawFormatInfo
+   * @return
+   */
   static FormatInformation decodeFormatInformation(int rawFormatInfo) {
     FormatInformation formatInfo = doDecodeFormatInformation(rawFormatInfo);
     if (formatInfo != null) {
@@ -99,16 +109,17 @@ final class FormatInformation {
     return doDecodeFormatInformation(rawFormatInfo ^ FORMAT_INFO_MASK_QR);
   }
 
-  private static FormatInformation doDecodeFormatInformation(
-      int rawFormatInfo) {
+  private static FormatInformation doDecodeFormatInformation(int rawFormatInfo) {
     // Unmask:
     int unmaskedFormatInfo = rawFormatInfo ^ FORMAT_INFO_MASK_QR;
+    // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
     int bestDifference = Integer.MAX_VALUE;
     int bestFormatInfo = 0;
     for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
       int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
       int targetInfo = decodeInfo[0];
       if (targetInfo == unmaskedFormatInfo) {
+        // Found an exact match
         return new FormatInformation(decodeInfo[1]);
       }
       int bitsDifference = numBitsDiffering(unmaskedFormatInfo, targetInfo);
index 125c5cd..455785a 100644 (file)
@@ -40,6 +40,11 @@ final class Mode {
     this.characterCountBitsForVersions = characterCountBitsForVersions;
   }
 
+  /**
+   * @param bits four bits encoding a QR Code data mode
+   * @return {@link Mode} encoded by these bits
+   * @throws ReaderException if bits do not correspond to a known mode
+   */
   static Mode forBits(int bits) throws ReaderException {
     switch (bits) {
       case 0x0:
@@ -57,6 +62,11 @@ final class Mode {
     }
   }
 
+  /**
+   * @param version version in question
+   * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
+   *  count of characters that will follow encoded in this {@link Mode}
+   */
   int getCharacterCountBits(Version version) {
     int number = version.getVersionNumber();
     int offset;
index 80188ce..2bd6648 100755 (executable)
@@ -87,41 +87,49 @@ public final class Version {
     return ecBlocks[ecLevel.ordinal()];\r
   }\r
 \r
-  public static Version getProvisionalVersionForDimension(int dimension)\r
-      throws ReaderException {\r
+  /**\r
+   * <p>Deduces version information purely from QR Code dimensions.</p>\r
+   *\r
+   * @param dimension dimension in modules\r
+   * @return {@link Version} for a QR Code of that dimension\r
+   * @throws ReaderException if dimension is not 1 mod 4\r
+   */\r
+  public static Version getProvisionalVersionForDimension(int dimension) throws ReaderException {\r
     if (dimension % 4 != 1) {\r
       throw new ReaderException("Dimension must be 1 mod 4");\r
     }\r
     return getVersionForNumber((dimension - 17) >> 2);\r
   }\r
 \r
-  public static Version getVersionForNumber(int versionNumber)\r
-      throws ReaderException {\r
+  public static Version getVersionForNumber(int versionNumber) throws ReaderException {\r
     if (versionNumber < 1 || versionNumber > 40) {\r
-      throw new ReaderException(\r
-          "versionNumber must be between 1 and 40");\r
+      throw new ReaderException("versionNumber must be between 1 and 40");\r
     }\r
     return VERSIONS[versionNumber - 1];\r
   }\r
 \r
-  static Version decodeVersionInformation(int versionBits)\r
-      throws ReaderException {\r
+  static Version decodeVersionInformation(int versionBits) throws ReaderException {\r
     int bestDifference = Integer.MAX_VALUE;\r
     int bestVersion = 0;\r
     for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {\r
       int targetVersion = VERSION_DECODE_INFO[i];\r
+      // Do the version info bits match exactly? done.\r
       if (targetVersion == versionBits) {\r
         return getVersionForNumber(i + 7);\r
       }\r
-      int bitsDifference =\r
-          FormatInformation.numBitsDiffering(versionBits, targetVersion);\r
+      // Otherwise see if this is the closest to a real version info bit string\r
+      // we have seen so far\r
+      int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);\r
       if (bitsDifference < bestDifference) {\r
         bestVersion = i + 7;\r
       }\r
     }\r
+    // We can tolerate up to 3 bits of error since no two version info codewords will\r
+    // differ in less than 4 bits.\r
     if (bestDifference <= 3) {\r
       return getVersionForNumber(bestVersion);\r
     }\r
+    // If we didn't find a close enough match, fail\r
     return null;\r
   }\r
 \r