OSDN Git Service

Simplify the usage of ExifData
authorEarl Ou <shunhsingou@google.com>
Thu, 30 Aug 2012 08:07:08 +0000 (16:07 +0800)
committerEarl Ou <shunhsingou@google.com>
Wed, 19 Sep 2012 11:02:57 +0000 (19:02 +0800)
Bug:7192363

Change-Id: I2c6ab9974f4472f63483a4de814728584944535e

src/com/android/gallery3d/app/CropImage.java
src/com/android/gallery3d/exif/ExifData.java
src/com/android/gallery3d/exif/ExifOutputStream.java
src/com/android/gallery3d/exif/ExifParser.java
src/com/android/gallery3d/exif/ExifTag.java
src/com/android/gallery3d/exif/IfdData.java
tests/src/com/android/gallery3d/exif/ExifParserTest.java
tests/src/com/android/gallery3d/exif/ExifReaderTest.java

index c77f57c..78eeac7 100644 (file)
@@ -59,7 +59,6 @@ import com.android.gallery3d.exif.ExifData;
 import com.android.gallery3d.exif.ExifOutputStream;
 import com.android.gallery3d.exif.ExifReader;
 import com.android.gallery3d.exif.ExifTag;
-import com.android.gallery3d.exif.IfdId;
 import com.android.gallery3d.picasasource.PicasaSource;
 import com.android.gallery3d.ui.BitmapTileProvider;
 import com.android.gallery3d.ui.CropView;
@@ -399,16 +398,8 @@ public class CropImage extends AbstractGalleryActivity {
     }
 
     private void changeExifImageSizeTags(ExifData data, int width, int height) {
-        // FIXME: would the image size be too large for TYPE_UNSIGHED_SHORT?
-        ExifTag tag = new ExifTag(ExifTag.TAG_IMAGE_WIDTH,
-                            ExifTag.TYPE_UNSIGNED_SHORT, 1, IfdId.TYPE_IFD_0);
-        tag.setValue(new int[] {width});
-        data.getIfdData(IfdId.TYPE_IFD_0).setTag(tag);
-
-        tag = new ExifTag(ExifTag.TAG_IMAGE_LENGTH,
-                            ExifTag.TYPE_UNSIGNED_SHORT, 1, IfdId.TYPE_IFD_0);
-        tag.setValue(new int[] {height});
-        data.getIfdData(IfdId.TYPE_IFD_0).setTag(tag);
+        data.addTag(ExifTag.TAG_IMAGE_WIDTH).setValue(width);
+        data.addTag(ExifTag.TAG_IMAGE_LENGTH).setValue(height);
     }
 
     private Uri saveToMediaProvider(JobContext jc, Bitmap cropped) {
index 0c16cc9..59b5f97 100644 (file)
@@ -36,16 +36,7 @@ public class ExifData {
         mByteOrder = order;
     }
 
-    /**
-     * Gets the IFD data of the specified IFD.
-     *
-     * @see IfdId#TYPE_IFD_0
-     * @see IfdId#TYPE_IFD_1
-     * @see IfdId#TYPE_IFD_EXIF
-     * @see IfdId#TYPE_IFD_GPS
-     * @see IfdId#TYPE_IFD_INTEROPERABILITY
-     */
-    public IfdData getIfdData(int ifdId) {
+    IfdData getIfdData(int ifdId) {
         return mIfdDatas[ifdId];
     }
 
@@ -53,7 +44,7 @@ public class ExifData {
      * Adds IFD data. If IFD data of the same type already exists,
      * it will be replaced by the new data.
      */
-    public void addIfdData(IfdData data) {
+    void addIfdData(IfdData data) {
         mIfdDatas[data.getId()] = data;
     }
 
@@ -143,6 +134,11 @@ public class ExifData {
         return false;
     }
 
+    /**
+     * Adds {@link ExifTag#TAG_GPS_LATITUDE}, {@link ExifTag#TAG_GPS_LONGITUDE},
+     * {@link ExifTag#TAG_GPS_LATITUDE_REF} and {@link ExifTag#TAG_GPS_LONGITUDE_REF} with the
+     * given latitude and longitude.
+     */
     public void addGpsTags(double latitude, double longitude) {
         IfdData gpsIfd = getIfdData(IfdId.TYPE_IFD_GPS);
         if (gpsIfd == null) {
@@ -182,4 +178,78 @@ public class ExifData {
         return new Rational[] {
                 new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100)};
     }
+
+    private IfdData getOrCreateIfdData(int ifdId) {
+        IfdData ifdData = mIfdDatas[ifdId];
+        if (ifdData == null) {
+            ifdData = new IfdData(ifdId);
+            mIfdDatas[ifdId] = ifdData;
+        }
+        return ifdData;
+    }
+
+    /**
+     * Gets the tag with the given tag ID. Returns null if the tag does not exist. For tags
+     * related to interoperability or thumbnail, call {@link #getInteroperabilityTag(short)} and
+     * {@link #getThumbnailTag(short)} respectively.
+     */
+    public ExifTag getTag(short tagId) {
+        int ifdId = ExifTag.getIfdIdFromTagId(tagId);
+        IfdData ifdData = mIfdDatas[ifdId];
+        return (ifdData == null) ? null : ifdData.getTag(tagId);
+    }
+
+    /**
+     * Gets the thumbnail-related tag with the given tag ID.
+     */
+    public ExifTag getThumbnailTag(short tagId) {
+        IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_1];
+        return (ifdData == null) ? null : ifdData.getTag(tagId);
+    }
+
+    /**
+     * Gets the interoperability-related tag with the given tag ID.
+     */
+    public ExifTag getInteroperabilityTag(short tagId) {
+        IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_INTEROPERABILITY];
+        return (ifdData == null) ? null : ifdData.getTag(tagId);
+    }
+
+    /**
+     * Adds a tag with the given tag ID. The original tag will be replaced by the new tag. For tags
+     * related to interoperability or thumbnail, call {@link #addInteroperabilityTag(short)} or
+     * {@link #addThumbnailTag(short)} respectively.
+     * @exception IllegalArgumentException if the tag ID is invalid.
+     */
+    public ExifTag addTag(short tagId) {
+        int ifdId = ExifTag.getIfdIdFromTagId(tagId);
+        IfdData ifdData = getOrCreateIfdData(ifdId);
+        ExifTag tag = ExifTag.buildTag(tagId);
+        ifdData.setTag(tag);
+        return tag;
+    }
+
+    /**
+     * Adds a thumbnail-related tag with the given tag ID. The original tag will be replaced
+     * by the new tag.
+     * @exception IllegalArgumentException if the tag ID is invalid.
+     */
+    public ExifTag addThumbnailTag(short tagId) {
+        IfdData ifdData = getOrCreateIfdData(IfdId.TYPE_IFD_1);
+        ExifTag tag = ExifTag.buildThumbnailTag(tagId);
+        ifdData.setTag(tag);
+        return tag;
+    }
+
+    /**
+     * Adds an interoperability-related tag with the given tag ID. The original tag will be
+     * replaced by the new tag.
+     * @exception IllegalArgumentException if the tag ID is invalid.
+     */
+    public ExifTag addInteroperabilityTag(short tagId) {
+        IfdData ifdData = getOrCreateIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
+        ExifTag tag = ExifTag.buildInteroperabilityTag(tagId);
+        ifdData.setTag(tag);
+        return tag;
+    }
 }
\ No newline at end of file
index 9466faa..b8db8e3 100644 (file)
@@ -221,9 +221,9 @@ public class ExifOutputStream extends FilterOutputStream {
                     dataOutputStream.write(0);
                 }
                 break;
-            case ExifTag.TYPE_INT:
+            case ExifTag.TYPE_LONG:
                 for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    dataOutputStream.writeInt(tag.getInt(i));
+                    dataOutputStream.writeInt(tag.getLong(i));
                 }
                 break;
             case ExifTag.TYPE_RATIONAL:
@@ -238,9 +238,9 @@ public class ExifOutputStream extends FilterOutputStream {
                 tag.getBytes(buf);
                 dataOutputStream.write(buf);
                 break;
-            case ExifTag.TYPE_UNSIGNED_INT:
+            case ExifTag.TYPE_UNSIGNED_LONG:
                 for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    dataOutputStream.writeInt((int) tag.getUnsignedInt(i));
+                    dataOutputStream.writeInt((int) tag.getUnsignedLong(i));
                 }
                 break;
             case ExifTag.TYPE_UNSIGNED_SHORT:
@@ -271,7 +271,7 @@ public class ExifOutputStream extends FilterOutputStream {
             mExifData.addIfdData(ifd0);
         }
         ExifTag exifOffsetTag = new ExifTag(ExifTag.TAG_EXIF_IFD,
-                ExifTag.TYPE_UNSIGNED_INT, 1, IfdId.TYPE_IFD_0);
+                ExifTag.TYPE_UNSIGNED_LONG, 1, IfdId.TYPE_IFD_0);
         ifd0.setTag(exifOffsetTag);
 
         // Exif IFD is required for all file.
@@ -285,7 +285,7 @@ public class ExifOutputStream extends FilterOutputStream {
         IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
         if (gpsIfd != null) {
             ExifTag gpsOffsetTag = new ExifTag(ExifTag.TAG_GPS_IFD,
-                    ExifTag.TYPE_UNSIGNED_INT, 1, IfdId.TYPE_IFD_0);
+                    ExifTag.TYPE_UNSIGNED_LONG, 1, IfdId.TYPE_IFD_0);
             ifd0.setTag(gpsOffsetTag);
         }
 
@@ -293,7 +293,7 @@ public class ExifOutputStream extends FilterOutputStream {
         IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
         if (interIfd != null) {
             ExifTag interOffsetTag = new ExifTag(ExifTag.TAG_INTEROPERABILITY_IFD,
-                    ExifTag.TYPE_UNSIGNED_INT, 1, IfdId.TYPE_IFD_EXIF);
+                    ExifTag.TYPE_UNSIGNED_LONG, 1, IfdId.TYPE_IFD_EXIF);
             exifIfd.setTag(interOffsetTag);
         }
 
@@ -306,10 +306,10 @@ public class ExifOutputStream extends FilterOutputStream {
                 mExifData.addIfdData(ifd1);
             }
             ExifTag offsetTag = new ExifTag(ExifTag.TAG_JPEG_INTERCHANGE_FORMAT,
-                    ExifTag.TYPE_UNSIGNED_INT, 1, IfdId.TYPE_IFD_1);
+                    ExifTag.TYPE_UNSIGNED_LONG, 1, IfdId.TYPE_IFD_1);
             ifd1.setTag(offsetTag);
             ExifTag lengthTag = new ExifTag(ExifTag.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
-                    ExifTag.TYPE_UNSIGNED_INT, 1, IfdId.TYPE_IFD_1);
+                    ExifTag.TYPE_UNSIGNED_LONG, 1, IfdId.TYPE_IFD_1);
             lengthTag.setValue(mExifData.getCompressedThumbnail().length);
             ifd1.setTag(lengthTag);
         } else if (mExifData.hasUncompressedStrip()){
@@ -319,9 +319,9 @@ public class ExifOutputStream extends FilterOutputStream {
             }
             int stripCount = mExifData.getStripCount();
             ExifTag offsetTag = new ExifTag(ExifTag.TAG_STRIP_OFFSETS,
-                    ExifTag.TYPE_UNSIGNED_INT, stripCount, IfdId.TYPE_IFD_1);
+                    ExifTag.TYPE_UNSIGNED_LONG, stripCount, IfdId.TYPE_IFD_1);
             ExifTag lengthTag = new ExifTag(ExifTag.TAG_STRIP_BYTE_COUNTS,
-                    ExifTag.TYPE_UNSIGNED_INT, stripCount, IfdId.TYPE_IFD_1);
+                    ExifTag.TYPE_UNSIGNED_LONG, stripCount, IfdId.TYPE_IFD_1);
             long[] lengths = new long[stripCount];
             for (int i = 0; i < mExifData.getStripCount(); i++) {
                 lengths[i] = mExifData.getStrip(i).length;
index dfa8184..f1e52c5 100644 (file)
@@ -230,7 +230,7 @@ public class ExifParser {
             }
             return EVENT_NEW_TAG;
         } else if (offset == endOfTags) {
-            long ifdOffset = readUnsignedInt();
+            long ifdOffset = readUnsignedLong();
             // There is a link to ifd1 at the end of ifd0
             if (mIfdType == IfdId.TYPE_IFD_0) {
                 if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
@@ -296,7 +296,7 @@ public class ExifParser {
         } else {
             skipTo(endOfTags);
         }
-        long ifdOffset = readUnsignedInt();
+        long ifdOffset = readUnsignedLong();
         // For ifd0, there is a link to ifd1 in the end of all tags
         if (mIfdType == IfdId.TYPE_IFD_0
                 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
@@ -340,7 +340,7 @@ public class ExifParser {
      * @see #registerForTagValue(ExifTag)
      * @see #read(byte[])
      * @see #read(byte[], int, int)
-     * @see #readInt()
+     * @see #readLong()
      * @see #readRational()
      * @see #readShort()
      * @see #readString(int)
@@ -396,7 +396,9 @@ public class ExifParser {
         if (mStripSizeTag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
             return mStripSizeTag.getUnsignedShort(mImageEvent.stripIndex);
         } else {
-            return (int) mStripSizeTag.getUnsignedInt(mImageEvent.stripIndex);
+            // Cast unsigned int to int since the strip size is always smaller
+            // than the size of APP1 (65536)
+            return (int) mStripSizeTag.getUnsignedLong(mImageEvent.stripIndex);
         }
     }
 
@@ -406,7 +408,9 @@ public class ExifParser {
      */
     public int getCompressedImageSize() {
         if (mJpegSizeTag == null) return 0;
-        return (int) mJpegSizeTag.getUnsignedInt(0);
+        // Cast unsigned int to int since the thumbnail is always smaller
+        // than the size of APP1 (65536)
+        return (int) mJpegSizeTag.getUnsignedLong(0);
     }
 
     private void skipTo(int offset) throws IOException {
@@ -429,6 +433,8 @@ public class ExifParser {
     }
 
     private void registerIfd(int ifdType, long offset) {
+        // Cast unsigned int to int since the offset is always smaller
+        // than the size of APP1 (65536)
         mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
     }
 
@@ -474,22 +480,22 @@ public class ExifParser {
             case ExifTag.TAG_EXIF_IFD:
                 if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
                         || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
-                    registerIfd(IfdId.TYPE_IFD_EXIF, tag.getUnsignedInt(0));
+                    registerIfd(IfdId.TYPE_IFD_EXIF, tag.getUnsignedLong(0));
                 }
                 break;
             case ExifTag.TAG_GPS_IFD:
                 if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
-                    registerIfd(IfdId.TYPE_IFD_GPS, tag.getUnsignedInt(0));
+                    registerIfd(IfdId.TYPE_IFD_GPS, tag.getUnsignedLong(0));
                 }
                 break;
             case ExifTag.TAG_INTEROPERABILITY_IFD:
                 if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
-                    registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getUnsignedInt(0));
+                    registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getUnsignedLong(0));
                 }
                 break;
             case ExifTag.TAG_JPEG_INTERCHANGE_FORMAT:
                 if (isThumbnailRequested()) {
-                    registerCompressedImage(tag.getUnsignedInt(0));
+                    registerCompressedImage(tag.getUnsignedLong(0));
                 }
                 break;
             case ExifTag.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
@@ -504,7 +510,7 @@ public class ExifParser {
                             if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
                                 registerUncompressedStrip(i, tag.getUnsignedShort(i));
                             } else {
-                                registerUncompressedStrip(i, tag.getUnsignedInt(i));
+                                registerUncompressedStrip(i, tag.getUnsignedLong(i));
                             }
                         }
                     } else {
@@ -535,11 +541,11 @@ public class ExifParser {
             case ExifTag.TYPE_ASCII:
                 tag.setValue(readString(tag.getComponentCount()));
                 break;
-            case ExifTag.TYPE_UNSIGNED_INT:
+            case ExifTag.TYPE_UNSIGNED_LONG:
                 {
                     long value[] = new long[tag.getComponentCount()];
                     for (int i = 0, n = value.length; i < n; i++) {
-                        value[i] = readUnsignedInt();
+                        value[i] = readUnsignedLong();
                     }
                     tag.setValue(value);
                 }
@@ -562,11 +568,11 @@ public class ExifParser {
                   tag.setValue(value);
               }
               break;
-          case ExifTag.TYPE_INT:
+          case ExifTag.TYPE_LONG:
               {
                   int value[] = new int[tag.getComponentCount()];
                   for (int i = 0, n = value.length; i < n; i++) {
-                      value[i] = readInt();
+                      value[i] = readLong();
                   }
                   tag.setValue(value);
               }
@@ -629,14 +635,24 @@ public class ExifParser {
                 && dataStream.readShort() == EXIF_HEADER_TAIL);
     }
 
+    /**
+     * Reads bytes from the InputStream.
+     */
     public int read(byte[] buffer, int offset, int length) throws IOException {
         return mTiffStream.read(buffer, offset, length);
     }
 
+    /**
+     * Equivalent to read(buffer, 0, buffer.length).
+     */
     public int read(byte[] buffer) throws IOException {
         return mTiffStream.read(buffer);
     }
 
+    /**
+     * Reads a String from the InputStream with UTF8 charset.
+     * This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
+     */
     public String readString(int n) throws IOException {
         if (n > 0) {
             byte[] buf = new byte[n];
@@ -647,37 +663,52 @@ public class ExifParser {
         }
     }
 
+    /**
+     * Reads a String from the InputStream with the given charset.
+     * This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
+     */
     public String readString(int n, Charset charset) throws IOException {
         byte[] buf = new byte[n];
         mTiffStream.readOrThrow(buf);
         return new String(buf, 0, n - 1, charset);
     }
 
+    /**
+     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the InputStream.
+     */
     public int readUnsignedShort() throws IOException {
-        return readShort() & 0xffff;
+        return mTiffStream.readShort() & 0xffff;
     }
 
-    public long readUnsignedInt() throws IOException {
-        return readInt() & 0xffffffffL;
+    /**
+     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the InputStream.
+     */
+    public long readUnsignedLong() throws IOException {
+        return readLong() & 0xffffffffL;
     }
 
+    /**
+     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the InputStream.
+     */
     public Rational readUnsignedRational() throws IOException {
-        long nomi = readUnsignedInt();
-        long denomi = readUnsignedInt();
+        long nomi = readUnsignedLong();
+        long denomi = readUnsignedLong();
         return new Rational(nomi, denomi);
     }
 
-    public int readInt() throws IOException {
+    /**
+     * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
+     */
+    public int readLong() throws IOException {
         return mTiffStream.readInt();
     }
 
-    public short readShort() throws IOException {
-        return mTiffStream.readShort();
-    }
-
+    /**
+     * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
+     */
     public Rational readRational() throws IOException {
-        int nomi = readInt();
-        int denomi = readInt();
+        int nomi = readLong();
+        int denomi = readLong();
         return new Rational(nomi, denomi);
     }
 
index 72a2f98..1aa84e5 100644 (file)
@@ -16,7 +16,8 @@
 
 package com.android.gallery3d.exif;
 
-import java.lang.reflect.Array;
+import android.util.SparseArray;
+
 import java.util.Arrays;
 
 /**
@@ -182,7 +183,7 @@ public class ExifTag {
     }
 
     /**
-     * Contants for {@link #TAG_COMPRESSION}
+     * Constants for {@link #TAG_COMPRESSION}
      */
     public static interface Compression {
         public static final short UNCOMPRESSION = 1;
@@ -254,19 +255,19 @@ public class ExifTag {
         // LSB
         public static final short DID_NOT_FIRED = 0;
         public static final short FIRED = 1;
-        // 1~2 bits
+        // 1st~2nd bits
         public static final short RETURN_NO_STROBE_RETURN_DETECTION_FUNCTION = 0 << 1;
         public static final short RETURN_STROBE_RETURN_LIGHT_NOT_DETECTED = 2 << 1;
         public static final short RETURN_STROBE_RETURN_LIGHT_DETECTED = 3 << 1;
-        // 3~4 bits
+        // 3rd~4th bits
         public static final short MODE_UNKNOWN = 0 << 3;
         public static final short MODE_COMPULSORY_FLASH_FIRING = 1 << 3;
         public static final short MODE_COMPULSORY_FLASH_SUPPRESSION = 2 << 3;
         public static final short MODE_AUTO_MODE = 3 << 3;
-        // 5 bit
+        // 5th bit
         public static final short FUNCTION_PRESENT = 0 << 5;
         public static final short FUNCTION_NO_FUNCTION = 1 << 5;
-        // 6 bit
+        // 6th bit
         public static final short RED_EYE_REDUCTION_NO_OR_UNKNOWN = 0 << 6;
         public static final short RED_EYE_REDUCTION_SUPPORT = 1 << 6;
     }
@@ -487,13 +488,42 @@ public class ExifTag {
         public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1;
     }
 
+    /**
+     * The BYTE type in the EXIF standard. An 8-bit unsigned integer.
+     */
     public static final short TYPE_UNSIGNED_BYTE = 1;
+    /**
+     * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit ASCII code.
+     * The final byte is terminated with NULL.
+     */
     public static final short TYPE_ASCII = 2;
+    /**
+     * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
+     */
     public static final short TYPE_UNSIGNED_SHORT = 3;
-    public static final short TYPE_UNSIGNED_INT = 4;
+    /**
+     * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
+     */
+    public static final short TYPE_UNSIGNED_LONG = 4;
+    /**
+     * The RATIONAL type of EXIF standard. It consists of two LONGs. The first one is the numerator
+     * and the second one expresses the denominator.
+     */
     public static final short TYPE_UNSIGNED_RATIONAL = 5;
+    /**
+     * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any value
+     * depending on the field definition.
+     */
     public static final short TYPE_UNDEFINED = 7;
-    public static final short TYPE_INT = 9;
+    /**
+     * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
+     * (2's complement notation).
+     */
+    public static final short TYPE_LONG = 9;
+    /**
+     * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first one is the
+     * numerator and the second one is the denominator.
+     */
     public static final short TYPE_RATIONAL = 10;
 
     private static final int TYPE_TO_SIZE_MAP[] = new int[11];
@@ -501,10 +531,10 @@ public class ExifTag {
         TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
         TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
         TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
-        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_INT] = 4;
+        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
         TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
         TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
-        TYPE_TO_SIZE_MAP[TYPE_INT] = 4;
+        TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
         TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
     }
 
@@ -512,11 +542,11 @@ public class ExifTag {
      * Gets the element size of the given data type.
      *
      * @see #TYPE_ASCII
-     * @see #TYPE_INT
+     * @see #TYPE_LONG
      * @see #TYPE_RATIONAL
      * @see #TYPE_UNDEFINED
      * @see #TYPE_UNSIGNED_BYTE
-     * @see #TYPE_UNSIGNED_INT
+     * @see #TYPE_UNSIGNED_LONG
      * @see #TYPE_UNSIGNED_RATIONAL
      * @see #TYPE_UNSIGNED_SHORT
      */
@@ -524,17 +554,366 @@ public class ExifTag {
         return TYPE_TO_SIZE_MAP[type];
     }
 
+    private static volatile SparseArray<Integer> sTagInfo = null;
+    private static volatile SparseArray<Integer> sInteroperTagInfo = null;
+    private static final int SIZE_UNDEFINED = 0;
+
+    private static SparseArray<Integer> getTagInfo() {
+        if (sTagInfo == null) {
+            synchronized(ExifTag.class) {
+                if (sTagInfo == null) {
+                    sTagInfo = new SparseArray<Integer>();
+                    initTagInfo();
+                }
+            }
+        }
+        return sTagInfo;
+    }
+
+    private static SparseArray<Integer> getInteroperTagInfo() {
+        if (sInteroperTagInfo == null) {
+            synchronized(ExifTag.class) {
+                if (sInteroperTagInfo == null) {
+                    sInteroperTagInfo = new SparseArray<Integer>();
+                    sInteroperTagInfo.put(TAG_INTEROPERABILITY_INDEX,
+                            (IfdId.TYPE_IFD_INTEROPERABILITY << 24)
+                            | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+                }
+            }
+        }
+        return sInteroperTagInfo;
+    }
+
+    private static void initTagInfo() {
+        /**
+         * We put tag information in a 4-bytes integer. The first byte is the
+         * IFD of the tag, and the second byte is the default data type. The
+         * last two byte are a short value indicating the component count of this
+         * tag.
+         */
+        sTagInfo.put(TAG_MAKE,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_IMAGE_WIDTH,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_IMAGE_LENGTH,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_BITS_PER_SAMPLE,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 3);
+        sTagInfo.put(TAG_COMPRESSION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_PHOTOMETRIC_INTERPRETATION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_ORIENTATION, (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SAMPLES_PER_PIXEL,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_PLANAR_CONFIGURATION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_Y_CB_CR_SUB_SAMPLING,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 2);
+        sTagInfo.put(TAG_Y_CB_CR_POSITIONING,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_X_RESOLUTION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_Y_RESOLUTION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_RESOLUTION_UNIT,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_STRIP_OFFSETS,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_ROWS_PER_STRIP,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_STRIP_BYTE_COUNTS,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_JPEG_INTERCHANGE_FORMAT,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_TRANSFER_FUNCTION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 3 * 256);
+        sTagInfo.put(TAG_WHITE_POINT,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 2);
+        sTagInfo.put(TAG_PRIMARY_CHROMATICITIES,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 6);
+        sTagInfo.put(TAG_Y_CB_CR_COEFFICIENTS,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 3);
+        sTagInfo.put(TAG_REFERENCE_BLACK_WHITE,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 6);
+        sTagInfo.put(TAG_DATE_TIME,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | 20);
+        sTagInfo.put(TAG_IMAGE_DESCRIPTION,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_MAKE,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_MODEL,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_SOFTWARE,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_ARTIST,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_COPYRIGHT,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_EXIF_IFD,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_GPS_IFD,
+                (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+
+        // EXIF TAG
+        sTagInfo.put(TAG_EXIF_VERSION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4);
+        sTagInfo.put(TAG_FLASHPIX_VERSION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4);
+        sTagInfo.put(TAG_COLOR_SPACE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_COMPONENTS_CONFIGURATION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4);
+        sTagInfo.put(TAG_COMPRESSED_BITS_PER_PIXEL,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_PIXEL_X_DIMENSION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_PIXEL_Y_DIMENSION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_LONG << 16 | 1);
+        sTagInfo.put(TAG_MAKER_NOTE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_USER_COMMENT,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_RELATED_SOUND_FILE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 13);
+        sTagInfo.put(TAG_DATE_TIME_ORIGINAL,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 20);
+        sTagInfo.put(TAG_DATE_TIME_DIGITIZED,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 20);
+        sTagInfo.put(TAG_SUB_SEC_TIME,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_SUB_SEC_TIME_ORIGINAL,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_SUB_SEC_TIME_DIGITIZED,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_IMAGE_UNIQUE_ID,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 33);
+        sTagInfo.put(TAG_EXPOSURE_TIME,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_F_NUMBER,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_EXPOSURE_PROGRAM,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SPECTRAL_SENSITIVITY,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_ISO_SPEED_RATINGS,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_OECF,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_SHUTTER_SPEED_VALUE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_APERTURE_VALUE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_BRIGHTNESS_VALUE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_EXPOSURE_BIAS_VALUE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_MAX_APERTURE_VALUE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_SUBJECT_DISTANCE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_METERING_MODE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_LIGHT_SOURCE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_FLASH,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_FOCAL_LENGTH,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_SUBJECT_AREA,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_FLASH_ENERGY,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_SPATIAL_FREQUENCY_RESPONSE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_FOCAL_PLANE_X_RESOLUTION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_FOCAL_PLANE_Y_RESOLUTION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_FOCAL_PLANE_RESOLUTION_UNIT,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SUBJECT_LOCATION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 2);
+        sTagInfo.put(TAG_EXPOSURE_INDEX,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_SENSING_METHOD,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_FILE_SOURCE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 1);
+        sTagInfo.put(TAG_SCENE_TYPE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 1);
+        sTagInfo.put(TAG_CFA_PATTERN,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_CUSTOM_RENDERED,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_EXPOSURE_MODE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_WHITE_BALANCE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_DIGITAL_ZOOM_RATIO,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_FOCAL_LENGTH_IN_35_MM_FILE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SCENE_CAPTURE_TYPE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_GAIN_CONTROL,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_CONTRAST,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SATURATION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_SHARPNESS,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        sTagInfo.put(TAG_DEVICE_SETTING_DESCRIPTION,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_SUBJECT_DISTANCE_RANGE,
+                (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1);
+        // GPS tag
+        sTagInfo.put(TAG_GPS_VERSION_ID,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_BYTE << 16 | 4);
+        sTagInfo.put(TAG_GPS_LATITUDE_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_LONGITUDE_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_LATITUDE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_RATIONAL << 16 | 3);
+        sTagInfo.put(TAG_GPS_LONGITUDE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_RATIONAL << 16 | 3);
+        sTagInfo.put(TAG_GPS_ALTITUDE_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_BYTE << 16 | 1);
+        sTagInfo.put(TAG_GPS_ALTITUDE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_TIME_STAMP,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 3);
+        sTagInfo.put(TAG_GPS_SATTELLITES,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_GPS_STATUS,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_MEASURE_MODE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_DOP,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_SPEED_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_SPEED,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_TRACK_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_TRACK,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_IMG_DIRECTION_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_IMG_DIRECTION,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_MAP_DATUM,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_GPS_DEST_LATITUDE_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_DEST_LATITUDE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_DEST_BEARING_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_DEST_BEARING,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_DEST_DISTANCE_REF,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2);
+        sTagInfo.put(TAG_GPS_DEST_DISTANCE,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1);
+        sTagInfo.put(TAG_GPS_PROCESSING_METHOD,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_GPS_AREA_INFORMATION,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED);
+        sTagInfo.put(TAG_GPS_DATA_STAMP,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 11);
+        sTagInfo.put(TAG_GPS_DIFFERENTIAL,
+                (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_SHORT << 16 | 11);
+    }
+
     private final short mTagId;
     private final short mDataType;
     private final int mIfd;
+    private final boolean mComponentCountDefined;
     private int mComponentCount;
     private Object mValue;
     private int mOffset;
 
-    public ExifTag(short tagId, short type, int componentCount, int ifd) {
+    static private short getTypeFromInfo(int info) {
+        return (short) ((info >> 16) & 0xff);
+    }
+
+    static private int getComponentCountFromInfo(int info) {
+        return info & 0xffff;
+    }
+
+    static private int getIfdIdFromInfo(int info) {
+        return (info >> 24) & 0xff;
+    }
+
+    static private boolean getComponentCountDefined(short tagId, int ifd) {
+        Integer info = (ifd == IfdId.TYPE_IFD_INTEROPERABILITY) ?
+                getInteroperTagInfo().get(tagId) : getTagInfo().get(tagId);
+        if (info == null) return false;
+        return getComponentCountFromInfo(info) != SIZE_UNDEFINED;
+    }
+
+    static int getIfdIdFromTagId(short tagId) {
+        Integer info = getTagInfo().get(tagId);
+        if (info == null) {
+            throw new IllegalArgumentException("Unknown Tag ID: " + tagId);
+        }
+        return getIfdIdFromInfo(info);
+    }
+
+    /**
+     * Create a tag with given ID. For tags related to interoperability and thumbnail, call
+     * {@link #buildInteroperabilityTag(short)} and {@link #buildThumbnailTag(short)} respectively.
+     * @exception IllegalArgumentException If the ID is invalid.
+     */
+    static public ExifTag buildTag(short tagId) {
+        Integer info = getTagInfo().get(tagId);
+        if (info == null) {
+            throw new IllegalArgumentException("Unknown Tag ID: " + tagId);
+        }
+        return new ExifTag(tagId, getTypeFromInfo(info),
+                getComponentCountFromInfo(info),
+                getIfdIdFromInfo(info));
+    }
+
+    /**
+     * Create a tag related to thumbnail with given ID.
+     * @exception IllegalArgumentException If the ID is invalid.
+     */
+    static public ExifTag buildThumbnailTag(short tagId) {
+        Integer info = getTagInfo().get(tagId);
+        if (info == null || getIfdIdFromInfo(info) != IfdId.TYPE_IFD_0) {
+            throw new IllegalArgumentException("Unknown Thumnail Tag ID: " + tagId);
+        }
+        return new ExifTag(tagId, getTypeFromInfo(info),
+                getComponentCountFromInfo(info),
+                IfdId.TYPE_IFD_1);
+    }
+
+    /**
+     * Create a tag related to interoperability with given ID.
+     * @exception IllegalArgumentException If the ID is invalid.
+     */
+    static public ExifTag buildInteroperabilityTag(short tagId) {
+        Integer info = getInteroperTagInfo().get(tagId);
+        if (info == null || getIfdIdFromInfo(info) != IfdId.TYPE_IFD_INTEROPERABILITY) {
+            throw new RuntimeException("Unknown Interoperability Tag ID: " + tagId);
+        }
+        return new ExifTag(tagId, getTypeFromInfo(info),
+                getComponentCountFromInfo(info),
+                IfdId.TYPE_IFD_INTEROPERABILITY);
+    }
+
+    ExifTag(short tagId, short type, int componentCount, int ifd) {
         mTagId = tagId;
         mDataType = type;
         mComponentCount = componentCount;
+        mComponentCountDefined = getComponentCountDefined(tagId, ifd);
         mIfd = ifd;
     }
 
@@ -562,11 +941,11 @@ public class ExifTag {
      * Gets the data type of this tag
      *
      * @see #TYPE_ASCII
-     * @see #TYPE_INT
+     * @see #TYPE_LONG
      * @see #TYPE_RATIONAL
      * @see #TYPE_UNDEFINED
      * @see #TYPE_UNSIGNED_BYTE
-     * @see #TYPE_UNSIGNED_INT
+     * @see #TYPE_UNSIGNED_LONG
      * @see #TYPE_UNSIGNED_RATIONAL
      * @see #TYPE_UNSIGNED_SHORT
      */
@@ -613,33 +992,126 @@ public class ExifTag {
         mOffset = offset;
     }
 
-    /**
-     * Sets integer values into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to the length.
-     */
-    public void setValue(int[] value) {
-        long[] data = new long[value.length];
-        for (int i = 0; i < value.length; i++) {
-            data[i] = value[i];
+    private void checkComponentCountOrThrow(int count)
+            throws IllegalArgumentException {
+        if (mComponentCountDefined && (mComponentCount != count)) {
+            throw new IllegalArgumentException("Tag " + mTagId + ": Required "
+                    + mComponentCount + " components but was given " + count
+                    + " component(s)");
         }
-        mValue = data;
-        mComponentCount = value.length;
     }
 
-    /**
-     * Sets integer value into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to 1.
-     */
-    public void setValue(int value) {
-        mValue = new long[] {value};
-        mComponentCount = 1;
+    private void throwTypeNotMatchedException(String className)
+            throws IllegalArgumentException {
+        throw new IllegalArgumentException("Tag " + mTagId + ": expect type " +
+                convertTypeToString(mDataType) + " but got " + className);
+    }
+
+    private static String convertTypeToString(short type) {
+        switch (type) {
+            case TYPE_UNSIGNED_BYTE:
+                return "UNSIGNED_BYTE";
+            case TYPE_ASCII:
+                return "ASCII";
+            case TYPE_UNSIGNED_SHORT:
+                return "UNSIGNED_SHORT";
+            case TYPE_UNSIGNED_LONG:
+                return "UNSIGNED_LONG";
+            case TYPE_UNSIGNED_RATIONAL:
+                return "UNSIGNED_RATIONAL";
+            case TYPE_UNDEFINED:
+                return "UNDEFINED";
+            case TYPE_LONG:
+                return "LONG";
+            case TYPE_RATIONAL:
+                return "RATIONAL";
+            default:
+                return "";
+        }
+    }
+
+    private static final int UNSIGNED_SHORT_MAX = 65535;
+    private static final long UNSIGNED_LONG_MAX = 4294967295L;
+    private static final long LONG_MAX = Integer.MAX_VALUE;
+    private static final long LONG_MIN = Integer.MIN_VALUE;
+
+    private void checkOverflowForUnsignedShort(int[] value) {
+        for (int v : value) {
+            if (v > UNSIGNED_SHORT_MAX || v < 0) {
+                throw new IllegalArgumentException(
+                        "Tag " + mTagId+ ": Value" + v +
+                        " is illegal for type UNSIGNED_SHORT");
+            }
+        }
+    }
+
+    private void checkOverflowForUnsignedLong(long[] value) {
+        for (long v: value) {
+            if (v < 0 || v > UNSIGNED_LONG_MAX) {
+                throw new IllegalArgumentException(
+                        "Tag " + mTagId+ ": Value" + v +
+                        " is illegal for type UNSIGNED_LONG");
+            }
+        }
+    }
+
+    private void checkOverflowForUnsignedLong(int[] value) {
+        for (int v: value) {
+            if (v < 0) {
+                throw new IllegalArgumentException(
+                        "Tag " + mTagId+ ": Value" + v +
+                        " is illegal for type UNSIGNED_LONG");
+            }
+        }
+    }
+
+    private void checkOverflowForUnsignedRational(Rational[] value) {
+        for (Rational v: value) {
+            if (v.getNominator() < 0 || v.getDenominator() < 0
+                    || v.getNominator() > UNSIGNED_LONG_MAX
+                    || v.getDenominator() > UNSIGNED_LONG_MAX) {
+                throw new IllegalArgumentException(
+                        "Tag " + mTagId+ ": Value" + v +
+                        " is illegal for type UNSIGNED_RATIONAL");
+            }
+        }
+    }
+
+    private void checkOverflowForRational(Rational[] value) {
+        for (Rational v: value) {
+            if (v.getNominator() < LONG_MIN || v.getDenominator() < LONG_MIN
+                    || v.getNominator() > LONG_MAX
+                    || v.getDenominator() > LONG_MAX) {
+                throw new IllegalArgumentException(
+                        "Tag " + mTagId+ ": Value" + v +
+                        " is illegal for type RATIONAL");
+            }
+        }
     }
 
     /**
-     * Sets short values into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to the length.
+     * Sets integer values into this tag.
+     * @exception IllegalArgumentException for the following situation:
+     * <ul>
+     *     <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
+     *      {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
+     *     <li>The value overflows. </li>
+     *     <li>The value.length does NOT match the definition of component count in
+     *      EXIF standard.</li>
+     * </ul>
      */
-    public void setValue(short[] value) {
+    public void setValue(int[] value) {
+        checkComponentCountOrThrow(value.length);
+        if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG &&
+                mDataType != TYPE_UNSIGNED_LONG) {
+            throwTypeNotMatchedException("int");
+        }
+        if (mDataType == TYPE_UNSIGNED_SHORT) {
+            checkOverflowForUnsignedShort(value);
+        } else if (mDataType == TYPE_UNSIGNED_LONG) {
+            checkOverflowForUnsignedLong(value);
+        }
+
         long[] data = new long[value.length];
         for (int i = 0; i < value.length; i++) {
             data[i] = value[i];
@@ -649,141 +1121,218 @@ public class ExifTag {
     }
 
     /**
-     * Sets short value into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to 1.
+     * Sets integer values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *     <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
+     *      {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
+     *     <li>The value overflows.</li>
+     *     <li>The component count in the definition of EXIF standard is not 1.</li>
+     * </ul>
      */
-    public void setValue(short value) {
-        mValue = new long[] {value};
-        mComponentCount = 1;
+    public void setValue(int value) {
+        checkComponentCountOrThrow(1);
+        setValue(new int[] {value});
     }
 
     /**
-     * Sets long values into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to the length.
+     * Sets long values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *      <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
+     *      <li>The value overflows. </li>
+     *      <li>The value.length does NOT match the definition of component count in
+     *       EXIF standard.</li>
+     * </ul>
      */
     public void setValue(long[] value) {
-        long[] data = new long[value.length];
-        System.arraycopy(value, 0, data, 0, value.length);
-        mValue = data;
+        checkComponentCountOrThrow(value.length);
+        if (mDataType != TYPE_UNSIGNED_LONG) {
+            throwTypeNotMatchedException("long");
+        }
+        checkOverflowForUnsignedLong(value);
+        mValue = value;
         mComponentCount = value.length;
     }
 
     /**
-     * Sets long value into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to 1.
+     * Sets long values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *     <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
+     *     <li>The value overflows. </li>
+     *     <li>The component count in the definition of EXIF standard is not 1.</li>
+     * </ul>
      */
     public void setValue(long value) {
-        mValue = new long[] {value};
-        mComponentCount = 1;
+        setValue(new long[] {value});
     }
 
     /**
-     * Sets String value into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to
-     * value.length() + 1.
+     * Sets string values into this tag.
+     * @exception IllegalArgumentException If the data type is not {@link #TYPE_ASCII}
+     * or value.length() + 1 does NOT fit the definition of the component count in the
+     * EXIF standard.
      */
     public void setValue(String value) {
+        checkComponentCountOrThrow(value.length() + 1);
+        if (mDataType != TYPE_ASCII) {
+            throwTypeNotMatchedException("String");
+        }
         mComponentCount = value.length() + 1;
         mValue = value;
     }
 
     /**
-     * Sets Rational values into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to the length.
+     * Sets Rational values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *      <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL} or
+     *       {@link #TYPE_RATIONAL} .</li>
+     *      <li>The value overflows. </li>
+     *      <li>The value.length does NOT match the definition of component count in
+     *       EXIF standard.</li>
+     * </ul>
      */
     public void setValue(Rational[] value) {
-        mValue = new Rational[value.length];
-        System.arraycopy(value, 0, mValue, 0, value.length);
+        if (mDataType == TYPE_UNSIGNED_RATIONAL) {
+            checkOverflowForUnsignedRational(value);
+        } else if (mDataType == TYPE_RATIONAL) {
+            checkOverflowForRational(value);
+        } else {
+            throwTypeNotMatchedException("Rational");
+        }
+        checkComponentCountOrThrow(value.length);
+        mValue = value;
         mComponentCount = value.length;
     }
 
     /**
-     * Sets Rational value into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to 1.
-     */
+     * Sets Rational values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *      <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL} or
+     *       {@link #TYPE_RATIONAL} .</li>
+     *      <li>The value overflows. </li>
+     *      <li>The component count in the definition of EXIF standard is not 1.</li>
+     * </ul>
+     * */
     public void setValue(Rational value) {
-        mValue = new Rational[] {value};
-        mComponentCount = 1;
+        setValue(new Rational[] {value});
     }
 
     /**
-     * Sets byte values into this tag. This is useful when we want to modify the tags
-     * and write it back to the JPEG file. The component count will be set to the length.
-     */
+     * Sets byte values into this tag.
+     * @exception IllegalArgumentException For the following situation:
+     * <ul>
+     *      <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
+     *       {@link #TYPE_UNDEFINED} .</li>
+     *      <li>The length does NOT match the definition of component count in EXIF standard.</li>
+     * </ul>
+     * */
     public void setValue(byte[] value, int offset, int length) {
-        long[] data = new long[length];
-        for (int i = 0; i < length; i++) {
-            data[i] = value[i + offset];
+        checkComponentCountOrThrow(length);
+        if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) {
+            throwTypeNotMatchedException("byte");
         }
-        mValue = data;
+        mValue = new byte[length];
+        System.arraycopy(value, offset, mValue, 0, length);
         mComponentCount = length;
     }
 
     /**
-     * Sets the byte array as the value of this tag.
-     * This is equivalent to setValue(value, 0, value.length).
+     * Equivalent to setValue(value, 0, value.length).
      */
     public void setValue(byte[] value) {
         setValue(value, 0, value.length);
     }
 
-    public short getShort(int index) {
-        if (mValue instanceof long[]) {
-            return (short) (((long[]) mValue) [index]);
-        } else {
-            throw new RuntimeException("There is no numerical value in this tag");
-        }
-    }
-
+    /**
+     * Gets the {@link #TYPE_UNSIGNED_SHORT} data.
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNSIGNED_SHORT}.
+     */
     public int getUnsignedShort(int index) {
-        if (mValue instanceof long[]) {
-            return (int) (((long[]) mValue) [index]);
-        } else {
-            throw new RuntimeException("There is no numerical value in this tag");
+        if (mDataType != TYPE_UNSIGNED_SHORT) {
+            throw new IllegalArgumentException("Cannot get UNSIGNED_SHORT value from "
+                    + convertTypeToString(mDataType));
         }
+        return (int) (((long[]) mValue) [index]);
     }
 
-    public int getInt(int index) {
-        if (mValue instanceof long[]) {
-            return (int) (((long[]) mValue) [index]);
-        } else {
-            throw new RuntimeException("There is no numerical value in this tag");
+    /**
+     * Gets the {@link #TYPE_LONG} data.
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_LONG}.
+     */
+    public int getLong(int index) {
+        if (mDataType != TYPE_LONG) {
+            throw new IllegalArgumentException("Cannot get LONG value from "
+                    + convertTypeToString(mDataType));
         }
+        return (int) (((long[]) mValue) [index]);
     }
 
-    public long getUnsignedInt(int index) {
-        if (mValue instanceof long[]) {
-            return ((long[]) mValue) [index];
-        } else {
-            throw new RuntimeException("There is no numerical value in this tag");
+    /**
+     * Gets the {@link #TYPE_UNSIGNED_LONG} data.
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNSIGNED_LONG}.
+     */
+    public long getUnsignedLong(int index) {
+        if (mDataType != TYPE_UNSIGNED_LONG) {
+            throw new IllegalArgumentException("Cannot get UNSIGNED LONG value from "
+                    + convertTypeToString(mDataType));
         }
+        return ((long[]) mValue) [index];
     }
 
+    /**
+     * Gets the {@link #TYPE_ASCII} data.
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_ASCII}.
+     */
     public String getString() {
+        if (mDataType != TYPE_ASCII) {
+            throw new IllegalArgumentException("Cannot get ASCII value from "
+                    + convertTypeToString(mDataType));
+        }
         return (String) mValue;
     }
 
+    /**
+     * Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data.
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_RATIONAL} or
+     * {@link #TYPE_UNSIGNED_RATIONAL}.
+     */
     public Rational getRational(int index) {
-        Object value = Array.get(mValue, index);
-        if (value instanceof Rational) {
-            return (Rational) value;
-        } else {
-            throw new RuntimeException("There is no Rational value in this tag");
+        if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) {
+            throw new IllegalArgumentException("Cannot get RATIONAL value from "
+                    + convertTypeToString(mDataType));
         }
+        return ((Rational[]) mValue) [index];
     }
 
+    /**
+     * Equivalent to getBytes(buffer, 0, buffer.length).
+     */
     public void getBytes(byte[] buf) {
         getBytes(buf, 0, buf.length);
     }
 
+    /**
+     * Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data.
+     *
+     * @param buf the byte array in which to store the bytes read.
+     * @param offset the initial position in buffer to store the bytes.
+     * @param length the maximum number of bytes to store in buffer. If length > component count,
+     * only the valid bytes will be stored.
+     *
+     * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNDEFINED} or
+     * {@link #TYPE_UNSIGNED_BYTE}.
+     */
     public void getBytes(byte[] buf, int offset, int length) {
-        if (!(mValue instanceof long[])) {
-            throw new RuntimeException("There is no byte value in this tag");
-        }
-        long[] data = (long[]) mValue;
-        for (int i = 0; i < length; i++) {
-            buf[offset + i] = (byte) data[i];
+        if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) {
+            throw new IllegalArgumentException("Cannot get BYTE value from "
+                    + convertTypeToString(mDataType));
         }
+        System.arraycopy(mValue, 0, buf, offset,
+                (length > mComponentCount) ? mComponentCount : length);
     }
 
     /**
@@ -802,13 +1351,12 @@ public class ExifTag {
                 }
                 break;
             case ExifTag.TYPE_ASCII:
-                // trim the string for comparison between xml
-                sbuilder.append(getString().trim());
+                sbuilder.append(getString());
                 break;
-            case ExifTag.TYPE_UNSIGNED_INT:
+            case ExifTag.TYPE_UNSIGNED_LONG:
                 for(int i = 0, n = getComponentCount(); i < n; i++) {
                     if(i != 0) sbuilder.append(" ");
-                    sbuilder.append(getUnsignedInt(i));
+                    sbuilder.append(getUnsignedLong(i));
                 }
                 break;
             case ExifTag.TYPE_RATIONAL:
@@ -825,10 +1373,10 @@ public class ExifTag {
                     sbuilder.append(getUnsignedShort(i));
                 }
                 break;
-            case ExifTag.TYPE_INT:
+            case ExifTag.TYPE_LONG:
                 for(int i = 0, n = getComponentCount(); i < n; i++) {
                     if(i != 0) sbuilder.append(" ");
-                    sbuilder.append(getInt(i));
+                    sbuilder.append(getLong(i));
                 }
                 break;
         }
@@ -840,7 +1388,7 @@ public class ExifTag {
      * {@link #TAG_GPS_IFD}, {@link #TAG_JPEG_INTERCHANGE_FORMAT},
      * {@link #TAG_STRIP_OFFSETS}, {@link #TAG_INTEROPERABILITY_IFD}
      */
-    public static boolean isOffsetTag(short tagId) {
+    static boolean isOffsetTag(short tagId) {
         return tagId == TAG_EXIF_IFD
                 || tagId == TAG_GPS_IFD
                 || tagId == TAG_JPEG_INTERCHANGE_FORMAT
@@ -852,7 +1400,6 @@ public class ExifTag {
     public boolean equals(Object obj) {
         if (obj instanceof ExifTag) {
             ExifTag tag = (ExifTag) obj;
-            boolean isArray = mValue != null && mValue.getClass().isArray();
             if (mValue != null) {
                 if (mValue instanceof long[]) {
                     if (!(tag.mValue instanceof long[])) return false;
@@ -860,6 +1407,9 @@ public class ExifTag {
                 } else if (mValue instanceof Rational[]) {
                     if (!(tag.mValue instanceof Rational[])) return false;
                     return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue);
+                } else if (mValue instanceof byte[]) {
+                    if (!(tag.mValue instanceof byte[])) return false;
+                    return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue);
                 } else {
                     return mValue.equals(tag.mValue);
                 }
index a2b367f..78f9173 100644 (file)
@@ -25,7 +25,7 @@ import java.util.Map;
  * @see ExifData
  * @see ExifTag
  */
-public class IfdData {
+class IfdData {
 
     private final int mIfdId;
     private final Map<Short, ExifTag> mExifTags = new HashMap<Short, ExifTag>();
index 1af106f..549fb06 100644 (file)
@@ -103,7 +103,7 @@ public class ExifParserTest extends InstrumentationTestCase {
         }
 
         String truthString = truth.get(tag.getTagId());
-        String dataString = tag.valueToString();
+        String dataString = tag.valueToString().trim();
         if (truthString == null) {
             fail(String.format("Unknown Tag %02x", tag.getTagId()));
         }
index 236c0ae..74b8bd3 100644 (file)
@@ -105,7 +105,7 @@ public class ExifReaderTest extends InstrumentationTestCase {
                         assertEquals(byteCountTag.getUnsignedShort(i), exifData.getStrip(i).length);
                     } else {
                         assertEquals(
-                                byteCountTag.getUnsignedInt(i), exifData.getStrip(i).length);
+                                byteCountTag.getUnsignedLong(i), exifData.getStrip(i).length);
                     }
                 }
             }
@@ -116,7 +116,7 @@ public class ExifReaderTest extends InstrumentationTestCase {
         if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
             return tag.getUnsignedShort(0);
         } else {
-            return (int) tag.getUnsignedInt(0);
+            return (int) tag.getUnsignedLong(0);
         }
     }
 
@@ -127,7 +127,7 @@ public class ExifReaderTest extends InstrumentationTestCase {
         }
         ExifTag[] tags = ifd.getAllTags();
         for (ExifTag tag : tags) {
-            assertEquals(ifdValue.get(tag.getTagId()), tag.valueToString());
+            assertEquals(ifdValue.get(tag.getTagId()), tag.valueToString().trim());
         }
         assertEquals(ifdValue.size(), tags.length);
     }