OSDN Git Service

am 79704855: Merge "Do not show "Filmstrip view" text when photo is fullscreen" into...
authorBobby Georgescu <georgescu@google.com>
Fri, 19 Oct 2012 00:36:33 +0000 (17:36 -0700)
committerAndroid Git Automerger <android-git-automerger@android.com>
Fri, 19 Oct 2012 00:36:33 +0000 (17:36 -0700)
* commit '79704855fb9b83acf9dc505f9ab1989ec1c770bc':
  Do not show "Filmstrip view" text when photo is fullscreen

src/com/android/gallery3d/exif/ExifData.java
src/com/android/gallery3d/exif/ExifTag.java
tests/res/xml/galaxy_nexus.xml
tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java
tests/src/com/android/gallery3d/exif/ExifParserTest.java
tests/src/com/android/gallery3d/exif/ExifReaderTest.java
tests/src/com/android/gallery3d/exif/ExifTestRunner.java
tests/src/com/android/gallery3d/exif/ExifXmlDataTestCase.java
tests/src/com/android/gallery3d/exif/ExifXmlReader.java

index 39eb574..7f79713 100644 (file)
@@ -216,7 +216,8 @@ public class ExifData {
     }
 
     /**
-     * Adds a tag with the given tag ID. The original tag will be replaced by the new tag. For tags
+     * Adds a tag with the given tag ID. If the tag of the given ID already exists,
+     * the original tag will be returned. Otherwise, a new ExifTag will be created. For tags
      * related to interoperability or thumbnail, call {@link #addInteroperabilityTag(short)} or
      * {@link #addThumbnailTag(short)} respectively.
      * @exception IllegalArgumentException if the tag ID is invalid.
@@ -224,32 +225,43 @@ public class ExifData {
     public ExifTag addTag(short tagId) {
         int ifdId = ExifTag.getIfdIdFromTagId(tagId);
         IfdData ifdData = getOrCreateIfdData(ifdId);
-        ExifTag tag = ExifTag.buildTag(tagId);
-        ifdData.setTag(tag);
+        ExifTag tag = ifdData.getTag(tagId);
+        if (tag == null) {
+            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.
+     * Adds a thumbnail-related tag with the given tag ID. If the tag of the given ID
+     * already exists, the original tag will be returned. Otherwise, a new ExifTag will
+     * be created.
      * @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);
+        ExifTag tag = ifdData.getTag(tagId);
+        if (tag == null) {
+            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.
+     * Adds an interoperability-related tag with the given tag ID. If the tag of the given ID
+     * already exists, the original tag will be returned. Otherwise, a new ExifTag will
+     * be created.
      * @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);
+        ExifTag tag = ifdData.getTag(tagId);
+        if (tag == null) {
+            tag = ExifTag.buildInteroperabilityTag(tagId);
+            ifdData.setTag(tag);
+        }
         return tag;
     }
 
@@ -258,4 +270,4 @@ public class ExifData {
         mStripBytes.clear();
         mIfdDatas[IfdId.TYPE_IFD_1] = null;
     }
-}
\ No newline at end of file
+}
index 49cb6ed..def80a9 100644 (file)
@@ -1354,23 +1354,51 @@ public class ExifTag {
                 (length > mComponentCount) ? mComponentCount : length);
     }
 
+    private String undefinedTypeValueToString() {
+        StringBuilder sbuilder = new StringBuilder();
+        switch (mTagId) {
+            case TAG_COMPONENTS_CONFIGURATION:
+            case TAG_FILE_SOURCE:
+            case TAG_SCENE_TYPE:
+                byte buf[] = (byte[]) mValue;
+                for(int i = 0, n = getComponentCount(); i < n; i++) {
+                    if(i != 0) sbuilder.append(" ");
+                    sbuilder.append(buf[i]);
+                }
+                break;
+            default:
+                sbuilder.append(new String((byte[]) mValue));
+        }
+        return sbuilder.toString();
+    }
+
     /**
      * Returns a string representation of the value of this tag.
      */
-    public String valueToString() {
+    String valueToString() {
         StringBuilder sbuilder = new StringBuilder();
         switch (getDataType()) {
             case ExifTag.TYPE_UNDEFINED:
+                sbuilder.append(undefinedTypeValueToString());
+                break;
             case ExifTag.TYPE_UNSIGNED_BYTE:
-                byte buf[] = new byte[getComponentCount()];
-                getBytes(buf);
+                byte buf[] = (byte[]) mValue;
                 for(int i = 0, n = getComponentCount(); i < n; i++) {
                     if(i != 0) sbuilder.append(" ");
                     sbuilder.append(String.format("%02x", buf[i]));
                 }
                 break;
             case ExifTag.TYPE_ASCII:
-                sbuilder.append(getString());
+                String s = getString();
+                for (int i = 0, n = s.length(); i < n; i++) {
+                    int code = s.codePointAt(i);
+                    if (code == 0) continue;
+                    if (code > 31 && code < 127) {
+                        sbuilder.append((char) code);
+                    } else {
+                        sbuilder.append('.');
+                    }
+                }
                 break;
             case ExifTag.TYPE_UNSIGNED_LONG:
                 for(int i = 0, n = getComponentCount(); i < n; i++) {
@@ -1415,6 +1443,15 @@ public class ExifTag {
                 || tagId == TAG_INTEROPERABILITY_IFD;
     }
 
+    /**
+     * Returns true if the ID is one of the following: {@link #TAG_EXIF_IFD},
+     * {@link #TAG_GPS_IFD}, {@link #TAG_INTEROPERABILITY_IFD}
+     */
+    static boolean isSubIfdOffsetTag(short tagId) {
+        return tagId == TAG_EXIF_IFD
+                || tagId == TAG_GPS_IFD
+                || tagId == TAG_INTEROPERABILITY_IFD;
+    }
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof ExifTag) {
index cefd078..0c80cb8 100644 (file)
@@ -1,48 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
 <exif>
-    <ifd name="ifd0">
-        <tag id="0x100" name="ImageWidth">2560</tag>
-        <tag id="0x101" name="ImageHeight">1920</tag>
-        <tag id="0x10f" name="Make">google</tag>
-        <tag id="0x110" name="Model">Nexus S</tag>
-        <tag id="0x112" name="Orientation">1</tag>
-        <tag id="0x131" name="Software">MASTER</tag>
-        <tag id="0x132" name="DateTime">2012:07:30 16:28:42</tag>
-        <tag id="0x213" name="YCbCrPositioning">1</tag>
-        <tag id="0x8769" name="ExifOffset">164</tag>
-    </ifd>
-    <ifd name="exif-ifd">
-        <tag id="0x829A" name="ExposureTime">1/40</tag>
-        <tag id="0x829D" name="FNumber">26/10</tag>
-        <tag id="0x8822" name="ExposureProgram">3</tag>
-        <tag id="0x8827" name="IsoSpeedRatings">100</tag>
-        <tag id="0x9000" name="ExifVersion">30 32 32 30</tag>
-        <tag id="0x9003" name="DateTimeTimeOriginal">2012:07:30 16:28:42</tag>
-        <tag id="0x9004" name="DateTimeTimeDigitized">2012:07:30 16:28:42</tag>
-        <tag id="0x9201" name="ShutterSpeed">50/10</tag>
-        <tag id="0x9202" name="ApertureValue">30/10</tag>
-        <tag id="0x9203" name="BrightnessValue">30/10</tag>
-        <tag id="0x9204" name="ExposureBiasValue">0/0</tag>
-        <tag id="0x9205" name="MaxApertureValue">30/10</tag>
-        <tag id="0x9207" name="MeteringMode">2</tag>
-        <tag id="0x9209" name="Flash">0</tag>
-        <tag id="0x920A" name="FocalLength">343/100</tag>
-        <tag id="0x9286" name="UserComment">00 00 00 49 49 43 53 41 55 73 65 72 20 63 6f 6d 6d 65 6e 74 73 00</tag>
-        <tag id="0xA001" name="ColoSpace">1</tag>
-        <tag id="0xA002" name="PixelXDimension">2560</tag>
-        <tag id="0xA003" name="PixelYDimension">1920</tag>
-        <tag id="0xA402" name="ExposureMode">0</tag>
-        <tag id="0xA403" name="WhithBalance">0</tag>
-        <tag id="0xA406" name="SceneCaptureType">0</tag>
-    </ifd>
-    <ifd name="ifd1">
-        <tag id="0x103" name="Compression">6</tag>
-        <tag id="0x11A" name="XResolution">72/1</tag>
-        <tag id="0x11B" name="YResolution">72/1</tag>
-        <tag id="0x128" name="ResolutionUnit">2</tag>
-        <tag id="0x100" name="ImageWidth">320</tag>
-        <tag id="0x101" name="ImageHeight">240</tag>
-        <tag id="0x112" name="Orientation">1</tag>
-        <tag id="0x201" name="JpegInterchangeFormat">690</tag>
-        <tag id="0x202" name="JpegInterchangeFormatLength">10447</tag>
-    </ifd>
+    <tag ifd="IFD0" id="0x0100" name="ImageWidth">2560</tag>
+    <tag ifd="IFD0" id="0x0101" name="ImageHeight">1920</tag>
+    <tag ifd="IFD0" id="0x010f" name="Make">google</tag>
+    <tag ifd="IFD0" id="0x0110" name="Model">Nexus S</tag>
+    <tag ifd="IFD0" id="0x0112" name="Orientation">1</tag>
+    <tag ifd="IFD0" id="0x0131" name="Software">MASTER</tag>
+    <tag ifd="IFD0" id="0x0132" name="ModifyDate">2012:07:30 16:28:42</tag>
+    <tag ifd="IFD0" id="0x0213" name="YCbCrPositioning">1</tag>
+    <tag ifd="ExifIFD" id="0x829a" name="ExposureTime">1/40</tag>
+    <tag ifd="ExifIFD" id="0x829d" name="FNumber">26/10</tag>
+    <tag ifd="ExifIFD" id="0x8822" name="ExposureProgram">3</tag>
+    <tag ifd="ExifIFD" id="0x8827" name="ISO">100</tag>
+    <tag ifd="ExifIFD" id="0x9000" name="ExifVersion">0220</tag>
+    <tag ifd="ExifIFD" id="0x9003" name="DateTimeOriginal">2012:07:30 16:28:42</tag>
+    <tag ifd="ExifIFD" id="0x9004" name="CreateDate">2012:07:30 16:28:42</tag>
+    <tag ifd="ExifIFD" id="0x9201" name="ShutterSpeedValue">50/10</tag>
+    <tag ifd="ExifIFD" id="0x9202" name="ApertureValue">30/10</tag>
+    <tag ifd="ExifIFD" id="0x9203" name="BrightnessValue">30/10</tag>
+    <tag ifd="ExifIFD" id="0x9204" name="ExposureCompensation">0/0</tag>
+    <tag ifd="ExifIFD" id="0x9205" name="MaxApertureValue">30/10</tag>
+    <tag ifd="ExifIFD" id="0x9207" name="MeteringMode">2</tag>
+    <tag ifd="ExifIFD" id="0x9209" name="Flash">0</tag>
+    <tag ifd="ExifIFD" id="0x920a" name="FocalLength">343/100</tag>
+    <tag ifd="ExifIFD" id="0x9286" name="UserComment">IICSAUser comments</tag>
+    <tag ifd="ExifIFD" id="0xa001" name="ColorSpace">1</tag>
+    <tag ifd="ExifIFD" id="0xa002" name="ExifImageWidth">2560</tag>
+    <tag ifd="ExifIFD" id="0xa003" name="ExifImageHeight">1920</tag>
+    <tag ifd="ExifIFD" id="0xa402" name="ExposureMode">0</tag>
+    <tag ifd="ExifIFD" id="0xa403" name="WhiteBalance">0</tag>
+    <tag ifd="ExifIFD" id="0xa406" name="SceneCaptureType">0</tag>
+    <tag ifd="IFD1" id="0x0100" name="ImageWidth">320</tag>
+    <tag ifd="IFD1" id="0x0101" name="ImageHeight">240</tag>
+    <tag ifd="IFD1" id="0x0103" name="Compression">6</tag>
+    <tag ifd="IFD1" id="0x0112" name="Orientation">1</tag>
+    <tag ifd="IFD1" id="0x011a" name="XResolution">72/1</tag>
+    <tag ifd="IFD1" id="0x011b" name="YResolution">72/1</tag>
+    <tag ifd="IFD1" id="0x0128" name="ResolutionUnit">2</tag>
+    <tag ifd="IFD1" id="0x0201" name="ThumbnailOffset">690</tag>
+    <tag ifd="IFD1" id="0x0202" name="ThumbnailLength">10447</tag>
 </exif>
index ad603df..51375e1 100644 (file)
@@ -19,6 +19,8 @@ package com.android.gallery3d.exif;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -26,48 +28,66 @@ import java.io.IOException;
 import java.io.InputStream;
 
 public class ExifOutputStreamTest extends ExifXmlDataTestCase {
-    public ExifOutputStreamTest(int imageResourceId, int xmlResourceId) {
-        super(imageResourceId, xmlResourceId);
+    public ExifOutputStreamTest(int imgRes, int xmlRes) {
+        super(imgRes, xmlRes);
     }
 
-    public void testExifOutputStream() throws IOException, ExifInvalidFormatException {
+    public ExifOutputStreamTest(String imgPath, String xmlPath) {
+        super(imgPath, xmlPath);
+    }
+
+    public void testExifOutputStream() throws Exception {
         File file = File.createTempFile("exif_test", ".jpg");
         InputStream imageInputStream = null;
         InputStream exifInputStream = null;
         FileInputStream reDecodeInputStream = null;
         FileInputStream reParseInputStream = null;
         try {
-            // Read the image
-            imageInputStream = getInstrumentation()
-                    .getContext().getResources().openRawResource(mImageResourceId);
-            Bitmap bmp = BitmapFactory.decodeStream(imageInputStream);
+            try {
+                byte[] imgData = readToByteArray(getImageInputStream());
+                imageInputStream = new ByteArrayInputStream(imgData);
+                exifInputStream = new ByteArrayInputStream(imgData);
+
+                // Read the image data
+                Bitmap bmp = BitmapFactory.decodeStream(imageInputStream);
+                // Read exif data
+                ExifData exifData = new ExifReader().read(exifInputStream);
 
-            // Read exif data
-            exifInputStream = getInstrumentation()
-                    .getContext().getResources().openRawResource(mImageResourceId);
-            ExifData exifData = new ExifReader().read(exifInputStream);
+                // Encode the image with the exif data
+                FileOutputStream outputStream = new FileOutputStream(file);
+                ExifOutputStream exifOutputStream = new ExifOutputStream(outputStream);
+                exifOutputStream.setExifData(exifData);
+                bmp.compress(Bitmap.CompressFormat.JPEG, 100, exifOutputStream);
+                exifOutputStream.close();
 
-            // Encode the image with the exif data
-            FileOutputStream outputStream = new FileOutputStream(file);
-            ExifOutputStream exifOutputStream = new ExifOutputStream(outputStream);
-            exifOutputStream.setExifData(exifData);
-            bmp.compress(Bitmap.CompressFormat.JPEG, 100, exifOutputStream);
-            exifOutputStream.close();
+                // Re-decode the temp file and check the data.
+                reDecodeInputStream = new FileInputStream(file);
+                Bitmap decodedBmp = BitmapFactory.decodeStream(reDecodeInputStream);
+                assertNotNull(decodedBmp);
 
-            // Re-decode the temp file and check the data.
-            reDecodeInputStream = new FileInputStream(file);
-            Bitmap decodedBmp = BitmapFactory.decodeStream(reDecodeInputStream);
-            assertNotNull(decodedBmp);
+                // Re-parse the temp file the check EXIF tag
+                reParseInputStream = new FileInputStream(file);
+                ExifData reExifData = new ExifReader().read(reParseInputStream);
+                assertEquals(exifData, reExifData);
+            } finally {
+                Util.closeSilently(imageInputStream);
+                Util.closeSilently(exifInputStream);
+                Util.closeSilently(reDecodeInputStream);
+                Util.closeSilently(reParseInputStream);
+            }
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
+        }
+    }
 
-            // Re-parse the temp file the check EXIF tag
-            reParseInputStream = new FileInputStream(file);
-            ExifData reExifData = new ExifReader().read(reParseInputStream);
-            assertEquals(exifData, reExifData);
-        } finally {
-            Util.closeSilently(imageInputStream);
-            Util.closeSilently(exifInputStream);
-            Util.closeSilently(reDecodeInputStream);
-            Util.closeSilently(reParseInputStream);
+    private byte[] readToByteArray(InputStream is) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        int len;
+        byte[] buf = new byte[1024];
+        while ((len = is.read(buf)) > -1) {
+            bos.write(buf, 0, len);
         }
+        bos.flush();
+        return bos.toByteArray();
     }
-}
\ No newline at end of file
+}
index c1ad834..c93e09a 100644 (file)
 
 package com.android.gallery3d.exif;
 
-import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class ExifParserTest extends ExifXmlDataTestCase {
     private static final String TAG = "ExifParserTest";
 
-    private HashMap<Short, String> mIfd0Value = new HashMap<Short, String>();
-    private HashMap<Short, String> mIfd1Value = new HashMap<Short, String>();
-    private HashMap<Short, String> mExifIfdValue = new HashMap<Short, String>();
-    private HashMap<Short, String> mInteroperabilityIfdValue = new HashMap<Short, String>();
-
-    private InputStream mImageInputStream;
-
-    public ExifParserTest(int imageResourceId, int xmlResourceId) {
-        super(imageResourceId, xmlResourceId);
+    public ExifParserTest(int imgRes, int xmlRes) {
+        super(imgRes, xmlRes);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        mImageInputStream = getInstrumentation()
-                .getContext().getResources().openRawResource(mImageResourceId);
+    public ExifParserTest(String imgPath, String xmlPath) {
+        super(imgPath, xmlPath);
+    }
 
-        XmlResourceParser parser =
-                getInstrumentation().getContext().getResources().getXml(mXmlResourceId);
+    private List<Map<Short, Set<String>>> mGroundTruth;
 
-        ExifXmlReader.readXml(parser, mIfd0Value, mIfd1Value, mExifIfdValue
-                , mInteroperabilityIfdValue);
-        parser.close();
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mGroundTruth = ExifXmlReader.readXml(getXmlParser());
     }
 
-    public void testParse() throws IOException, ExifInvalidFormatException {
-        ExifParser parser = ExifParser.parse(mImageInputStream);
-        int event = parser.next();
-        while (event != ExifParser.EVENT_END) {
-            switch (event) {
-                case ExifParser.EVENT_START_OF_IFD:
-                    break;
-                case ExifParser.EVENT_NEW_TAG:
-                    ExifTag tag = parser.getTag();
-                    if (!tag.hasValue()) {
-                        parser.registerForTagValue(tag);
-                    } else {
+    public void testParse() throws Exception {
+        try {
+            ExifParser parser = ExifParser.parse(getImageInputStream());
+            int event = parser.next();
+            while (event != ExifParser.EVENT_END) {
+                switch (event) {
+                    case ExifParser.EVENT_START_OF_IFD:
+                        break;
+                    case ExifParser.EVENT_NEW_TAG:
+                        ExifTag tag = parser.getTag();
+                        if (!tag.hasValue()) {
+                            parser.registerForTagValue(tag);
+                        } else {
+                            checkTag(tag);
+                        }
+                        break;
+                    case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+                        tag = parser.getTag();
+                        if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
+                            byte[] buf = new byte[tag.getComponentCount()];
+                            parser.read(buf);
+                            tag.setValue(buf);
+                        }
                         checkTag(tag);
-                    }
-                    break;
-                case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
-                    tag = parser.getTag();
-                    if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
-                        byte[] buf = new byte[tag.getComponentCount()];
-                        parser.read(buf);
-                        tag.setValue(buf);
-                    }
-                    checkTag(tag);
-                    break;
+                        break;
+                }
+                event = parser.next();
             }
-            event = parser.next();
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
         }
     }
 
     private void checkTag(ExifTag tag) {
-        HashMap<Short, String> truth = null;
-        switch (tag.getIfd()) {
-            case IfdId.TYPE_IFD_0:
-                truth = mIfd0Value;
-                break;
-            case IfdId.TYPE_IFD_1:
-                truth = mIfd1Value;
-                break;
-            case IfdId.TYPE_IFD_EXIF:
-                truth = mExifIfdValue;
-                break;
-            case IfdId.TYPE_IFD_INTEROPERABILITY:
-                truth = mInteroperabilityIfdValue;
-                break;
+        // Ignore offset tags since the ground-truth from exiftool doesn't have it.
+        // We can verify it by examining the sub-IFD or thumbnail itself.
+        if (ExifTag.isSubIfdOffsetTag(tag.getTagId())) return;
+
+        // TODO: Test MakerNote and UserComment
+        if (tag.getTagId() == ExifTag.TAG_MAKER_NOTE
+                || tag.getTagId() == ExifTag.TAG_USER_COMMENT) return;
+
+        Set<String> truth = mGroundTruth.get(tag.getIfd()).get(tag.getTagId());
+
+        if (truth == null) {
+            fail(String.format("Unknown Tag %02x", tag.getTagId()) + ", " + getImageTitle());
         }
 
-        String truthString = truth.get(tag.getTagId());
         String dataString = tag.valueToString().trim();
-        if (truthString == null) {
-            fail(String.format("Unknown Tag %02x", tag.getTagId()));
-        }
-        assertEquals(String.format("Tag %02x", tag.getTagId()), truthString, dataString);
+        assertTrue(String.format("Tag %02x", tag.getTagId()) + ", " + getImageTitle()
+                + ": " + dataString,
+                truth.contains(dataString));
     }
 
-    private void parseOneIfd(int ifd, int options, HashMap<Short, String> expectedResult)
-            throws IOException, ExifInvalidFormatException {
-        int numOfTag = 0;
-        ExifParser parser = ExifParser.parse(mImageInputStream, options);
-        int event = parser.next();
-        while(event != ExifParser.EVENT_END) {
-            switch (event) {
-                case ExifParser.EVENT_START_OF_IFD:
-                    assertEquals(ifd, parser.getCurrentIfd());
-                    break;
-                case ExifParser.EVENT_NEW_TAG:
-                    numOfTag++;
-                    ExifTag tag = parser.getTag();
-                    if (tag.hasValue()) {
+    private void parseOneIfd(int ifd, int options) throws Exception {
+        try {
+            Map<Short, Set<String>> expectedResult = mGroundTruth.get(ifd);
+            int numOfTag = 0;
+            ExifParser parser = ExifParser.parse(getImageInputStream(), options);
+            int event = parser.next();
+            while(event != ExifParser.EVENT_END) {
+                switch (event) {
+                    case ExifParser.EVENT_START_OF_IFD:
+                        assertEquals(getImageTitle(), ifd, parser.getCurrentIfd());
+                        break;
+                    case ExifParser.EVENT_NEW_TAG:
+                        ExifTag tag = parser.getTag();
+                        // The exiftool doesn't provide MakerNote tag
+                        if (!ExifTag.isSubIfdOffsetTag(tag.getTagId())
+                                && tag.getTagId() != ExifTag.TAG_MAKER_NOTE) numOfTag++;
+                        if (tag.hasValue()) {
+                            checkTag(tag);
+                        } else {
+                            parser.registerForTagValue(tag);
+                        }
+                        break;
+                    case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+                        tag = parser.getTag();
+                        if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
+                            byte[] buf = new byte[tag.getComponentCount()];
+                            parser.read(buf);
+                            tag.setValue(buf);
+                        }
                         checkTag(tag);
-                    } else {
-                        parser.registerForTagValue(tag);
-                    }
-                    break;
-                case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
-                    tag = parser.getTag();
-                    if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
-                        byte[] buf = new byte[tag.getComponentCount()];
-                        parser.read(buf);
-                        tag.setValue(buf);
-                    }
-                    checkTag(tag);
-                    break;
-                case ExifParser.EVENT_COMPRESSED_IMAGE:
-                case ExifParser.EVENT_UNCOMPRESSED_STRIP:
-                    fail("Invalid Event type: " + event);
-                    break;
+                        break;
+                    case ExifParser.EVENT_COMPRESSED_IMAGE:
+                    case ExifParser.EVENT_UNCOMPRESSED_STRIP:
+                        fail("Invalid Event type: " + event + ", " + getImageTitle());
+                        break;
+                }
+                event = parser.next();
             }
-            event = parser.next();
+            assertEquals(expectedResult.size(), numOfTag);
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
         }
-        assertEquals(expectedResult.size(), numOfTag);
     }
 
-    public void testOnlyExifIfd() throws IOException, ExifInvalidFormatException {
-        parseOneIfd(IfdId.TYPE_IFD_EXIF, ExifParser.OPTION_IFD_EXIF, mExifIfdValue);
+    public void testOnlyExifIfd() throws Exception {
+        parseOneIfd(IfdId.TYPE_IFD_EXIF, ExifParser.OPTION_IFD_EXIF);
     }
 
-    public void testOnlyIfd0() throws IOException, ExifInvalidFormatException {
-        parseOneIfd(IfdId.TYPE_IFD_0, ExifParser.OPTION_IFD_0, mIfd0Value);
+    public void testOnlyIfd0() throws Exception {
+        parseOneIfd(IfdId.TYPE_IFD_0, ExifParser.OPTION_IFD_0);
     }
 
-    public void testOnlyIfd1() throws IOException, ExifInvalidFormatException {
-        parseOneIfd(IfdId.TYPE_IFD_1, ExifParser.OPTION_IFD_1, mIfd1Value);
+    public void testOnlyIfd1() throws Exception {
+        parseOneIfd(IfdId.TYPE_IFD_1, ExifParser.OPTION_IFD_1);
     }
 
-    public void testOnlyInteroperabilityIfd() throws IOException, ExifInvalidFormatException {
-        parseOneIfd(IfdId.TYPE_IFD_INTEROPERABILITY, ExifParser.OPTION_IFD_INTEROPERABILITY
-                , mInteroperabilityIfdValue);
+    public void testOnlyInteroperabilityIfd() throws Exception {
+        parseOneIfd(IfdId.TYPE_IFD_INTEROPERABILITY, ExifParser.OPTION_IFD_INTEROPERABILITY);
     }
 
-    public void testOnlyReadSomeTag() throws IOException, ExifInvalidFormatException {
-        ExifParser parser = ExifParser.parse(mImageInputStream, ExifParser.OPTION_IFD_0);
-        int event = parser.next();
-        boolean isTagFound = false;
-        while (event != ExifParser.EVENT_END) {
-            switch (event) {
-                case ExifParser.EVENT_START_OF_IFD:
-                    assertEquals(IfdId.TYPE_IFD_0, parser.getCurrentIfd());
-                    break;
-                case ExifParser.EVENT_NEW_TAG:
-                    ExifTag tag = parser.getTag();
-                    if (tag.getTagId() == ExifTag.TAG_MODEL) {
-                        if (tag.hasValue()) {
-                            isTagFound = true;
-                            checkTag(tag);
-                        } else {
-                            parser.registerForTagValue(tag);
+    public void testOnlyReadSomeTag() throws Exception {
+        try {
+            ExifParser parser = ExifParser.parse(getImageInputStream(), ExifParser.OPTION_IFD_0);
+            int event = parser.next();
+            boolean isTagFound = false;
+            while (event != ExifParser.EVENT_END) {
+                switch (event) {
+                    case ExifParser.EVENT_START_OF_IFD:
+                        assertEquals(getImageTitle(), IfdId.TYPE_IFD_0, parser.getCurrentIfd());
+                        break;
+                    case ExifParser.EVENT_NEW_TAG:
+                        ExifTag tag = parser.getTag();
+                        if (tag.getTagId() == ExifTag.TAG_MODEL) {
+                            if (tag.hasValue()) {
+                                isTagFound = true;
+                                checkTag(tag);
+                            } else {
+                                parser.registerForTagValue(tag);
+                            }
+                            parser.skipRemainingTagsInCurrentIfd();
                         }
-                        parser.skipRemainingTagsInCurrentIfd();
-                    }
-                    break;
-                case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
-                    tag = parser.getTag();
-                    assertEquals(ExifTag.TAG_MODEL, tag.getTagId());
-                    checkTag(tag);
-                    isTagFound = true;
-                    break;
+                        break;
+                    case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+                        tag = parser.getTag();
+                        assertEquals(getImageTitle(), ExifTag.TAG_MODEL, tag.getTagId());
+                        checkTag(tag);
+                        isTagFound = true;
+                        break;
+                }
+                event = parser.next();
             }
-            event = parser.next();
+            assertTrue(getImageTitle(), isTagFound);
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
         }
-        assertTrue(isTagFound);
     }
 
-    public void testReadThumbnail() throws ExifInvalidFormatException, IOException {
-        ExifParser parser = ExifParser.parse(mImageInputStream,
-                ExifParser.OPTION_IFD_1 | ExifParser.OPTION_THUMBNAIL);
-
-        int event = parser.next();
-        Bitmap bmp = null;
-        boolean mIsContainCompressedImage = false;
-        while (event != ExifParser.EVENT_END) {
-            switch (event) {
-                case ExifParser.EVENT_NEW_TAG:
-                    ExifTag tag = parser.getTag();
-                    if (tag.getTagId() == ExifTag.TAG_COMPRESSION) {
-                        if (tag.getUnsignedShort(0) == ExifTag.Compression.JPEG) {
-                            mIsContainCompressedImage = true;
+    public void testReadThumbnail() throws Exception {
+        try {
+            ExifParser parser = ExifParser.parse(getImageInputStream(),
+                    ExifParser.OPTION_IFD_1 | ExifParser.OPTION_THUMBNAIL);
+
+            int event = parser.next();
+            Bitmap bmp = null;
+            boolean mIsContainCompressedImage = false;
+            while (event != ExifParser.EVENT_END) {
+                switch (event) {
+                    case ExifParser.EVENT_NEW_TAG:
+                        ExifTag tag = parser.getTag();
+                        if (tag.getTagId() == ExifTag.TAG_COMPRESSION) {
+                            if (tag.getUnsignedShort(0) == ExifTag.Compression.JPEG) {
+                                mIsContainCompressedImage = true;
+                            }
                         }
-                    }
-                    break;
-                case ExifParser.EVENT_COMPRESSED_IMAGE:
-                    int imageSize = parser.getCompressedImageSize();
-                    byte buf[] = new byte[imageSize];
-                    parser.read(buf);
-                    bmp = BitmapFactory.decodeByteArray(buf, 0, imageSize);
-                    break;
+                        break;
+                    case ExifParser.EVENT_COMPRESSED_IMAGE:
+                        int imageSize = parser.getCompressedImageSize();
+                        byte buf[] = new byte[imageSize];
+                        parser.read(buf);
+                        bmp = BitmapFactory.decodeByteArray(buf, 0, imageSize);
+                        break;
+                }
+                event = parser.next();
             }
-            event = parser.next();
-        }
-        if (mIsContainCompressedImage) {
-            assertNotNull(bmp);
+            if (mIsContainCompressedImage) {
+                assertNotNull(getImageTitle(), bmp);
+            }
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
         }
     }
-
-    @Override
-    protected void tearDown() throws IOException {
-        mImageInputStream.close();
-        mIfd0Value.clear();
-        mIfd1Value.clear();
-        mExifIfdValue.clear();
-    }
-}
\ No newline at end of file
+}
index 2691208..40d269e 100644 (file)
 
 package com.android.gallery3d.exif;
 
-import android.content.res.XmlResourceParser;
 import android.graphics.BitmapFactory;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class ExifReaderTest extends ExifXmlDataTestCase {
     private static final String TAG = "ExifReaderTest";
 
-    private final HashMap<Short, String> mIfd0Value = new HashMap<Short, String>();
-    private final HashMap<Short, String> mIfd1Value = new HashMap<Short, String>();
-    private final HashMap<Short, String> mExifIfdValue = new HashMap<Short, String>();
-    private final HashMap<Short, String> mInteroperabilityIfdValue = new HashMap<Short, String>();
-
-    private InputStream mImageInputStream;
-
-    public ExifReaderTest(int imageResourceId, int xmlResourceId) {
-        super(imageResourceId, xmlResourceId);
+    public ExifReaderTest(int imgRes, int xmlRes) {
+        super(imgRes, xmlRes);
     }
 
-    @Override
-    public void setUp() throws Exception {
-        mImageInputStream = getInstrumentation()
-                .getContext().getResources().openRawResource(mImageResourceId);
-
-        XmlResourceParser parser =
-                getInstrumentation().getContext().getResources().getXml(mXmlResourceId);
-
-        ExifXmlReader.readXml(parser, mIfd0Value, mIfd1Value, mExifIfdValue
-                , mInteroperabilityIfdValue);
-        parser.close();
+    public ExifReaderTest(String imgPath, String xmlPath) {
+        super(imgPath, xmlPath);
     }
 
-    public void testRead() throws ExifInvalidFormatException, IOException {
-        ExifReader reader = new ExifReader();
-        ExifData exifData = reader.read(mImageInputStream);
-        checkIfd(exifData.getIfdData(IfdId.TYPE_IFD_0), mIfd0Value);
-        checkIfd(exifData.getIfdData(IfdId.TYPE_IFD_1), mIfd1Value);
-        checkIfd(exifData.getIfdData(IfdId.TYPE_IFD_EXIF), mExifIfdValue);
-        checkIfd(exifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
-                mInteroperabilityIfdValue);
-        checkThumbnail(exifData);
+    public void testRead() throws Exception {
+        try {
+            ExifReader reader = new ExifReader();
+            ExifData exifData = reader.read(getImageInputStream());
+            List<Map<Short, Set<String>>> groundTruth = ExifXmlReader.readXml(getXmlParser());
+            for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+                checkIfd(exifData.getIfdData(i), groundTruth.get(i));
+            }
+            checkThumbnail(exifData);
+        } catch (Exception e) {
+            throw new Exception(getImageTitle(), e);
+        }
     }
 
     private void checkThumbnail(ExifData exifData) {
@@ -66,9 +52,10 @@ public class ExifReaderTest extends ExifXmlDataTestCase {
         if (ifd1 != null) {
             if (ifd1.getTag(ExifTag.TAG_COMPRESSION).getUnsignedShort(0) ==
                     ExifTag.Compression.JPEG) {
-                assertTrue(exifData.hasCompressedThumbnail());
+                assertTrue(getImageTitle(), exifData.hasCompressedThumbnail());
                 byte[] thumbnail = exifData.getCompressedThumbnail();
-                assertTrue(BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length) != null);
+                assertTrue(getImageTitle(),
+                        BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length) != null);
             } else {
                 // Try to check the strip count with the formula provided by EXIF spec.
                 int planarType = ExifTag.PlanarConfiguration.CHUNKY;
@@ -86,20 +73,23 @@ public class ExifReaderTest extends ExifXmlDataTestCase {
                         ExifTag.TAG_STRIP_OFFSETS).getComponentCount();
 
                 if (planarType == ExifTag.PlanarConfiguration.CHUNKY) {
-                    assertTrue(stripCount == (imageLength + rowsPerStrip - 1) / rowsPerStrip);
+                    assertTrue(getImageTitle(),
+                            stripCount == (imageLength + rowsPerStrip - 1) / rowsPerStrip);
                 } else {
                     ExifTag samplePerPixelTag = ifd1.getTag(ExifTag.TAG_SAMPLES_PER_PIXEL);
                     int samplePerPixel = samplePerPixelTag.getUnsignedShort(0);
-                    assertTrue(stripCount ==
+                    assertTrue(getImageTitle(),
+                            stripCount ==
                             (imageLength + rowsPerStrip - 1) / rowsPerStrip * samplePerPixel);
                 }
 
                 for (int i = 0; i < stripCount; i++) {
                     ExifTag byteCountTag = ifd1.getTag(ExifTag.TAG_STRIP_BYTE_COUNTS);
                     if (byteCountTag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
-                        assertEquals(byteCountTag.getUnsignedShort(i), exifData.getStrip(i).length);
+                        assertEquals(getImageTitle(),
+                                byteCountTag.getUnsignedShort(i), exifData.getStrip(i).length);
                     } else {
-                        assertEquals(
+                        assertEquals(getImageTitle(),
                                 byteCountTag.getUnsignedLong(i), exifData.getStrip(i).length);
                     }
                 }
@@ -115,20 +105,24 @@ public class ExifReaderTest extends ExifXmlDataTestCase {
         }
     }
 
-    private void checkIfd(IfdData ifd, HashMap<Short, String> ifdValue) {
+    private void checkIfd(IfdData ifd, Map<Short, Set<String>> ifdValue) {
         if (ifd == null) {
-            assertEquals(0 ,ifdValue.size());
+            assertEquals(getImageTitle(), 0 ,ifdValue.size());
             return;
         }
         ExifTag[] tags = ifd.getAllTags();
+        int size = 0;
         for (ExifTag tag : tags) {
-            assertEquals(ifdValue.get(tag.getTagId()), tag.valueToString().trim());
+            if (ExifTag.isSubIfdOffsetTag(tag.getTagId())
+                    || tag.getTagId() == ExifTag.TAG_MAKER_NOTE) continue;
+            if (tag.getTagId() != ExifTag.TAG_USER_COMMENT) {
+                Set<String> truth = ifdValue.get(tag.getTagId());
+                assertNotNull(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), truth);
+                assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+                        truth.contains(tag.valueToString().trim()));
+            }
+            size++;
         }
-        assertEquals(ifdValue.size(), tags.length);
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        mImageInputStream.close();
+        assertEquals(getImageTitle(), ifdValue.size(), size);
     }
 }
index 57a7111..519e871 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.gallery3d.exif;
 
+import android.content.Context;
+import android.os.Environment;
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
 import android.util.Log;
@@ -25,8 +27,11 @@ import com.android.gallery3d.tests.R;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 
+import java.io.File;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 public class ExifTestRunner extends InstrumentationTestRunner {
     private static final String TAG = "ExifTestRunner";
@@ -34,12 +39,17 @@ public class ExifTestRunner extends InstrumentationTestRunner {
     private static final int[] IMG_RESOURCE = {
         R.raw.galaxy_nexus
     };
+
     private static final int[] EXIF_DATA_RESOURCE = {
         R.xml.galaxy_nexus
     };
 
+    private static List<String> mTestImgPath = new ArrayList<String>();
+    private static List<String> mTestXmlPath = new ArrayList<String>();
+
     @Override
     public TestSuite getAllTests() {
+        getTestImagePath();
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(ExifDataTest.class);
         suite.addTestSuite(ExifTagTest.class);
@@ -49,6 +59,25 @@ public class ExifTestRunner extends InstrumentationTestRunner {
         return suite;
     }
 
+    private void getTestImagePath() {
+        Context context = getContext();
+        File imgDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+        File xmlDir = new File(context.getExternalFilesDir(null).getPath(), "Xml");
+
+        if (imgDir != null && xmlDir != null) {
+            String[] imgs = imgDir.list();
+            if (imgs == null) return;
+            for (String imgName: imgs) {
+                String xmlName = imgName.substring(0, imgName.lastIndexOf('.')) + ".xml";
+                File xmlFile = new File(xmlDir, xmlName);
+                if (xmlFile.exists()) {
+                    mTestImgPath.add(new File(imgDir, imgName).getAbsolutePath());
+                    mTestXmlPath.add(xmlFile.getAbsolutePath());
+                }
+            }
+        }
+    }
+
     private void addAllTestsFromExifTestCase(Class<? extends ExifXmlDataTestCase> testClass,
             TestSuite suite) {
         for (Method method : testClass.getDeclaredMethods()) {
@@ -72,6 +101,25 @@ public class ExifTestRunner extends InstrumentationTestRunner {
                         Log.e(TAG, "Failed to create test case", e);
                     }
                 }
+                for (int i = 0, n = mTestImgPath.size(); i < n; i++) {
+                    TestCase test;
+                    try {
+                        test = testClass.getDeclaredConstructor(String.class, String.class).
+                                newInstance(mTestImgPath.get(i), mTestXmlPath.get(i));
+                        test.setName(method.getName());
+                        suite.addTest(test);
+                    } catch (IllegalArgumentException e) {
+                        Log.e(TAG, "Failed to create test case", e);
+                    } catch (InstantiationException e) {
+                        Log.e(TAG, "Failed to create test case", e);
+                    } catch (IllegalAccessException e) {
+                        Log.e(TAG, "Failed to create test case", e);
+                    } catch (InvocationTargetException e) {
+                        Log.e(TAG, "Failed to create test case", e);
+                    } catch (NoSuchMethodException e) {
+                        Log.e(TAG, "Failed to create test case", e);
+                    }
+                }
             }
         }
     }
index 41b3151..5b61778 100644 (file)
 
 package com.android.gallery3d.exif;
 
+import android.content.res.Resources;
 import android.test.InstrumentationTestCase;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 
 public class ExifXmlDataTestCase extends InstrumentationTestCase {
-    protected final int mImageResourceId;
-    protected final int mXmlResourceId;
+
+    private static final String RES_ID_TITLE = "Resource ID: %x";
+
+    private InputStream mImageInputStream;
+    private InputStream mXmlInputStream;
+    private XmlPullParser mXmlParser;
+    private final String mImagePath;
+    private final String mXmlPath;
+    private final int mImageResourceId;
+    private final int mXmlResourceId;
 
     public ExifXmlDataTestCase(int imageRes, int xmlRes) {
+        mImagePath = null;
+        mXmlPath = null;
         mImageResourceId = imageRes;
         mXmlResourceId = xmlRes;
     }
+
+    public ExifXmlDataTestCase(String imagePath, String xmlPath) {
+        mImagePath = imagePath;
+        mXmlPath = xmlPath;
+        mImageResourceId = 0;
+        mXmlResourceId = 0;
+    }
+
+    protected InputStream getImageInputStream() {
+        return mImageInputStream;
+    }
+
+    protected XmlPullParser getXmlParser() {
+        return mXmlParser;
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        if (mImagePath != null) {
+            mImageInputStream = new FileInputStream(mImagePath);
+            mXmlInputStream = new FileInputStream(mXmlPath);
+            mXmlParser = Xml.newPullParser();
+            mXmlParser.setInput(new InputStreamReader(mXmlInputStream));
+        } else {
+            Resources res = getInstrumentation().getContext().getResources();
+            mImageInputStream = res.openRawResource(mImageResourceId);
+            mXmlParser = res.getXml(mXmlResourceId);
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        Util.closeSilently(mImageInputStream);
+        Util.closeSilently(mXmlInputStream);
+    }
+
+    protected String getImageTitle() {
+        if (mImagePath != null) {
+            return mImagePath;
+        } else {
+            return String.format(RES_ID_TITLE, mImageResourceId);
+        }
+    }
 }
index 72dd313..54def27 100644 (file)
@@ -20,78 +20,94 @@ import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class ExifXmlReader {
+    private static final String TAG_EXIF = "exif";
+    private static final String TAG_TAG = "tag";
 
-    private static final String XML_EXIF_TAG = "exif";
-    private static final String XML_IFD_TAG = "ifd";
-    private static final String XML_IFD_NAME = "name";
-    private static final String XML_TAG = "tag";
-    private static final String XML_IFD0 = "ifd0";
-    private static final String XML_IFD1 = "ifd1";
-    private static final String XML_EXIF_IFD = "exif-ifd";
-    private static final String XML_INTEROPERABILITY_IFD = "interoperability-ifd";
-    private static final String XML_TAG_ID = "id";
-
-    public static void readXml(XmlPullParser parser, HashMap<Short, String> ifd0,
-            HashMap<Short, String> ifd1, HashMap<Short, String> exifIfd,
-            HashMap<Short, String> interoperabilityIfd) throws XmlPullParserException,
-            IOException {
+    private static final String IFD0 = "IFD0";
+    private static final String EXIF_IFD = "ExifIFD";
+    private static final String GPS_IFD = "GPS";
+    private static final String IFD1 = "IFD1";
+    private static final String INTEROP_IFD = "InteropIFD";
+
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_IFD = "ifd";
+
+    /**
+     * This function read the ground truth XML.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    static public List<Map<Short, Set<String>>> readXml(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        List<Map<Short, Set<String>>> exifData =
+                new ArrayList<Map<Short, Set<String>>>(IfdId.TYPE_IFD_COUNT);
+        for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+            exifData.add(new HashMap<Short, Set<String>>());
+        }
 
         while (parser.next() != XmlPullParser.END_DOCUMENT) {
             if (parser.getEventType() == XmlPullParser.START_TAG) {
                 break;
             }
         }
+        parser.require(XmlPullParser.START_TAG, null, TAG_EXIF);
 
-        assert(parser.getName().equals(XML_EXIF_TAG));
-
-        parser.require(XmlPullParser.START_TAG, null, XML_EXIF_TAG);
         while (parser.next() != XmlPullParser.END_TAG) {
-            if (parser.getEventType() == XmlPullParser.START_TAG) {
-                readXmlIfd(parser, ifd0, ifd1, exifIfd, interoperabilityIfd);
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
             }
-        }
-        parser.require(XmlPullParser.END_TAG, null, XML_EXIF_TAG);
-    }
 
-    private static void readXmlIfd(XmlPullParser parser, HashMap<Short, String> ifd0,
-            HashMap<Short, String> ifd1, HashMap<Short, String> exifIfd,
-            HashMap<Short, String> interoperabilityIfd) throws XmlPullParserException,
-            IOException {
-        parser.require(XmlPullParser.START_TAG, null, XML_IFD_TAG);
-        String name = parser.getAttributeValue(null, XML_IFD_NAME);
-        HashMap<Short, String> ifdData = null;
-        if (XML_IFD0.equals(name)) {
-            ifdData = ifd0;
-        } else if (XML_IFD1.equals(name)) {
-            ifdData = ifd1;
-        } else if (XML_EXIF_IFD.equals(name)) {
-            ifdData = exifIfd;
-        } else if (XML_INTEROPERABILITY_IFD.equals(name)) {
-            ifdData = interoperabilityIfd;
-        } else {
-            throw new RuntimeException("Unknown IFD name in xml file: " + name);
-        }
-        while (parser.next() != XmlPullParser.END_TAG) {
-            if (parser.getEventType() == XmlPullParser.START_TAG) {
-                readXmlTag(parser, ifdData);
+            parser.require(XmlPullParser.START_TAG, null, TAG_TAG);
+
+            int ifdId = getIfdIdFromString(parser.getAttributeValue(null, ATTR_IFD));
+            short id = Integer.decode(parser.getAttributeValue(null, ATTR_ID)).shortValue();
+
+            String value = "";
+            if (parser.next() == XmlPullParser.TEXT) {
+                value = parser.getText();
+                parser.next();
             }
+
+            if (ifdId < 0) {
+                // TODO: the MarkerNote segment.
+            } else {
+                Set<String> tagData = exifData.get(ifdId).get(id);
+                if (tagData == null) {
+                    tagData = new HashSet<String>();
+                    exifData.get(ifdId).put(id, tagData);
+                }
+                tagData.add(value.trim());
+            }
+
+            parser.require(XmlPullParser.END_TAG, null, null);
         }
-        parser.require(XmlPullParser.END_TAG, null, XML_IFD_TAG);
+        return exifData;
     }
 
-    private static void readXmlTag(XmlPullParser parser, HashMap<Short, String> data)
-        throws XmlPullParserException, IOException {
-        parser.require(XmlPullParser.START_TAG, null, XML_TAG);
-        short id = Integer.decode(parser.getAttributeValue(null, XML_TAG_ID)).shortValue();
-        String value = "";
-        if (parser.next() == XmlPullParser.TEXT) {
-            value = parser.getText();
-            parser.next();
+    static private int getIfdIdFromString(String prefix) {
+        if (IFD0.equals(prefix)) {
+            return IfdId.TYPE_IFD_0;
+        } else if (EXIF_IFD.equals(prefix)) {
+            return IfdId.TYPE_IFD_EXIF;
+        } else if (GPS_IFD.equals(prefix)) {
+            return IfdId.TYPE_IFD_GPS;
+        } else if (IFD1.equals(prefix)) {
+            return IfdId.TYPE_IFD_1;
+        } else if (INTEROP_IFD.equals(prefix)) {
+            return IfdId.TYPE_IFD_INTEROPERABILITY;
+        } else {
+            assert(false);
+            return -1;
         }
-        data.put(id, value);
-        parser.require(XmlPullParser.END_TAG, null, XML_TAG);
     }
-}
\ No newline at end of file
+}