OSDN Git Service

ExifModifier
authorEarl Ou <shunhsingou@google.com>
Wed, 28 Nov 2012 05:58:30 +0000 (13:58 +0800)
committerEarl Ou <shunhsingou@google.com>
Fri, 30 Nov 2012 07:33:35 +0000 (15:33 +0800)
Change-Id: I001b24d8ba004a4080cd898f0d26d706b1839425

gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java [new file with mode: 0644]
gallerycommon/src/com/android/gallery3d/exif/ExifData.java
gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java [new file with mode: 0644]
gallerycommon/src/com/android/gallery3d/exif/ExifParser.java
gallerycommon/src/com/android/gallery3d/exif/ExifTag.java
gallerycommon/src/com/android/gallery3d/exif/IfdData.java
tests/src/com/android/gallery3d/exif/ExifModifierTest.java [new file with mode: 0644]
tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java
tests/src/com/android/gallery3d/exif/ExifTestRunner.java

diff --git a/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java b/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java
new file mode 100644 (file)
index 0000000..7fb9f22
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.exif;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+class ByteBufferInputStream extends InputStream {
+
+    private ByteBuffer mBuf;
+
+    public ByteBufferInputStream(ByteBuffer buf) {
+        mBuf = buf;
+    }
+
+    @Override
+    public int read() {
+        if (!mBuf.hasRemaining()) {
+            return -1;
+        }
+        return mBuf.get() & 0xFF;
+    }
+
+    @Override
+    public int read(byte[] bytes, int off, int len) {
+        if (!mBuf.hasRemaining()) {
+            return -1;
+        }
+
+        len = Math.min(len, mBuf.remaining());
+        mBuf.get(bytes, off, len);
+        return len;
+    }
+}
index 6e5c227..b226dd4 100644 (file)
@@ -287,6 +287,14 @@ public class ExifData {
     }
 
     /**
+     * Adds the given ExifTag to its corresponding IFD.
+     */
+    public void addTag(ExifTag tag) {
+        IfdData ifdData = getOrCreateIfdData(tag.getIfd());
+        ifdData.setTag(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.
diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java b/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java
new file mode 100644 (file)
index 0000000..da31a29
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.exif;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExifModifier {
+    private final ByteBuffer mByteBuffer;
+    private final ExifData mTagToModified;
+    private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
+    private int mOffsetBase;
+
+    private static class TagOffset {
+        final int mOffset;
+        final ExifTag mTag;
+
+        TagOffset(ExifTag tag, int offset) {
+            mTag = tag;
+            mOffset = offset;
+        }
+
+        public ExifTag getTag() {
+            return mTag;
+        }
+    }
+
+    public ExifModifier(ByteBuffer byteBuffer) throws IOException, ExifInvalidFormatException {
+        mByteBuffer = byteBuffer;
+        mOffsetBase = byteBuffer.position();
+        InputStream is = null;
+        try {
+            is = new ByteBufferInputStream(byteBuffer);
+            // Do not require any IFD;
+            ExifParser parser = ExifParser.parse(is, 0);
+            mTagToModified = new ExifData(parser.getByteOrder());
+            mOffsetBase += parser.getTiffStartPosition();
+            mByteBuffer.position(0);
+        } finally {
+            closeSilently(is);
+        }
+    }
+
+    public ByteOrder getByteOrder() {
+        return mTagToModified.getByteOrder();
+    }
+
+    public boolean commit() throws IOException, ExifInvalidFormatException {
+        InputStream is = null;
+        try {
+            is = new ByteBufferInputStream(mByteBuffer);
+            int flag = 0;
+            IfdData[] ifdDatas = new IfdData[] {
+                    mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
+                    mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
+                    mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
+                    mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
+                    mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
+            };
+
+            if (ifdDatas[IfdId.TYPE_IFD_0] != null) flag |= ExifParser.OPTION_IFD_0;
+            if (ifdDatas[IfdId.TYPE_IFD_1] != null) flag |= ExifParser.OPTION_IFD_1;
+            if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) flag |= ExifParser.OPTION_IFD_EXIF;
+            if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) flag |= ExifParser.OPTION_IFD_GPS;
+            if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
+                flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
+            }
+
+            ExifParser parser = ExifParser.parse(is, flag);
+            int event = parser.next();
+            IfdData currIfd = null;
+            while (event != ExifParser.EVENT_END) {
+                switch (event) {
+                    case ExifParser.EVENT_START_OF_IFD:
+                        currIfd = ifdDatas[parser.getCurrentIfd()];
+                        if (currIfd == null) parser.skipRemainingTagsInCurrentIfd();
+                        break;
+                    case ExifParser.EVENT_NEW_TAG:
+                        ExifTag oldTag = parser.getTag();
+                        ExifTag newTag = currIfd.getTag(oldTag.getTagId());
+                        if (newTag != null) {
+                            if (newTag.getComponentCount() != oldTag.getComponentCount()
+                                    || newTag.getDataType() != oldTag.getDataType()) {
+                                return false;
+                            } else {
+                                mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
+                                currIfd.removeTag(oldTag.getTagId());
+                                if (currIfd.getTagCount() == 0) {
+                                    parser.skipRemainingTagsInCurrentIfd();
+                                }
+                            }
+                        }
+                        break;
+                }
+                event = parser.next();
+            }
+            for (IfdData ifd: ifdDatas) {
+                if (ifd != null && ifd.getTagCount() > 0) return false;
+            }
+            modify();
+        } finally {
+            closeSilently(is);
+        }
+        return true;
+    }
+
+    private void modify() {
+        mByteBuffer.order(getByteOrder());
+        for (TagOffset tagOffset: mTagOffsets) {
+            writeTagValue(tagOffset.mTag, tagOffset.mOffset);
+        }
+    }
+
+    private void writeTagValue(ExifTag tag, int offset) {
+        mByteBuffer.position(offset + mOffsetBase);
+        switch (tag.getDataType()) {
+            case ExifTag.TYPE_ASCII:
+                byte buf[] = tag.getStringByte();
+                if (buf.length == tag.getComponentCount()) {
+                    buf[buf.length - 1] = 0;
+                    mByteBuffer.put(buf);
+                } else {
+                    mByteBuffer.put(buf);
+                    mByteBuffer.put((byte) 0);
+                }
+                break;
+            case ExifTag.TYPE_LONG:
+            case ExifTag.TYPE_UNSIGNED_LONG:
+                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+                    mByteBuffer.putInt((int) tag.getValueAt(i));
+                }
+                break;
+            case ExifTag.TYPE_RATIONAL:
+            case ExifTag.TYPE_UNSIGNED_RATIONAL:
+                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+                    Rational v = tag.getRational(i);
+                    mByteBuffer.putInt((int) v.getNominator());
+                    mByteBuffer.putInt((int) v.getDenominator());
+                }
+                break;
+            case ExifTag.TYPE_UNDEFINED:
+            case ExifTag.TYPE_UNSIGNED_BYTE:
+                buf = new byte[tag.getComponentCount()];
+                tag.getBytes(buf);
+                mByteBuffer.put(buf);
+                break;
+            case ExifTag.TYPE_UNSIGNED_SHORT:
+                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+                    mByteBuffer.putShort((short) tag.getValueAt(i));
+                }
+                break;
+        }
+    }
+
+    public void modifyTag(ExifTag tag) {
+        mTagToModified.addTag(tag);
+    }
+
+    private static void closeSilently(Closeable c) {
+        if (c == null) return;
+        try {
+            c.close();
+        } catch (Throwable t) {
+            // do nothing
+        }
+    }
+}
index 2cff12a..a1ce13e 100644 (file)
@@ -18,7 +18,6 @@ package com.android.gallery3d.exif;
 
 import android.util.Log;
 
-import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteOrder;
@@ -154,6 +153,7 @@ public class ExifParser {
     private int mApp1End;
     private byte[] mDataAboveIfd0;
     private int mIfd0Position;
+    private int mTiffStartPosition;
 
     private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
 
@@ -190,10 +190,13 @@ public class ExifParser {
             throw new ExifInvalidFormatException("Invalid offset " + offset);
         }
         mIfd0Position = (int) offset;
-        registerIfd(IfdId.TYPE_IFD_0, offset);
-        if (offset != DEFAULT_IFD0_OFFSET) {
-            mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
-            read(mDataAboveIfd0);
+        mIfdType = IfdId.TYPE_IFD_0;
+        if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
+            registerIfd(IfdId.TYPE_IFD_0, offset);
+            if (offset != DEFAULT_IFD0_OFFSET) {
+                mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
+                read(mDataAboveIfd0);
+            }
         }
     }
 
@@ -203,8 +206,8 @@ public class ExifParser {
      * @exception ExifInvalidFormatException
      */
     public static ExifParser parse(InputStream inputStream, int options)
-             throws IOException, ExifInvalidFormatException {
-         return new ExifParser(inputStream, options);
+            throws IOException, ExifInvalidFormatException {
+        return new ExifParser(inputStream, options);
     }
 
     /**
@@ -353,7 +356,8 @@ public class ExifParser {
         switch (mIfdType) {
             case IfdId.TYPE_IFD_0:
                 return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
-                        || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
+                        || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
+                        || isIfdRequested(IfdId.TYPE_IFD_1);
             case IfdId.TYPE_IFD_1:
                 return isThumbnailRequested();
             case IfdId.TYPE_IFD_EXIF:
@@ -515,6 +519,8 @@ public class ExifParser {
         } else {
             readFullTagValue(tag);
             mTiffStream.skip(4 - dataSize);
+            // Set the offset to the position of value.
+            tag.setOffset(mTiffStream.getReadByteCount() - 4);
         }
         return tag;
     }
@@ -617,42 +623,42 @@ public class ExifParser {
                     tag.setValue(value);
                 }
                 break;
-          case ExifTag.TYPE_UNSIGNED_RATIONAL:
-              {
-                  Rational value[] = new Rational[tag.getComponentCount()];
-                  for (int i = 0, n = value.length; i < n; i++) {
-                      value[i] = readUnsignedRational();
-                  }
-                  tag.setValue(value);
-              }
-              break;
-          case ExifTag.TYPE_UNSIGNED_SHORT:
-              {
-                  int value[] = new int[tag.getComponentCount()];
-                  for (int i = 0, n = value.length; i < n; i++) {
-                      value[i] = readUnsignedShort();
-                  }
-                  tag.setValue(value);
-              }
-              break;
-          case ExifTag.TYPE_LONG:
-              {
-                  int value[] = new int[tag.getComponentCount()];
-                  for (int i = 0, n = value.length; i < n; i++) {
-                      value[i] = readLong();
-                  }
-                  tag.setValue(value);
-              }
-              break;
-          case ExifTag.TYPE_RATIONAL:
-              {
-                  Rational value[] = new Rational[tag.getComponentCount()];
-                  for (int i = 0, n = value.length; i < n; i++) {
-                      value[i] = readRational();
-                  }
-                  tag.setValue(value);
-              }
-              break;
+            case ExifTag.TYPE_UNSIGNED_RATIONAL:
+                {
+                    Rational value[] = new Rational[tag.getComponentCount()];
+                    for (int i = 0, n = value.length; i < n; i++) {
+                        value[i] = readUnsignedRational();
+                    }
+                    tag.setValue(value);
+                }
+                break;
+            case ExifTag.TYPE_UNSIGNED_SHORT:
+                {
+                    int value[] = new int[tag.getComponentCount()];
+                    for (int i = 0, n = value.length; i < n; i++) {
+                        value[i] = readUnsignedShort();
+                    }
+                    tag.setValue(value);
+                }
+                break;
+            case ExifTag.TYPE_LONG:
+                {
+                    int value[] = new int[tag.getComponentCount()];
+                    for (int i = 0, n = value.length; i < n; i++) {
+                        value[i] = readLong();
+                    }
+                    tag.setValue(value);
+                }
+                break;
+            case ExifTag.TYPE_RATIONAL:
+                {
+                    Rational value[] = new Rational[tag.getComponentCount()];
+                    for (int i = 0, n = value.length; i < n; i++) {
+                        value[i] = readRational();
+                    }
+                    tag.setValue(value);
+                }
+                break;
         }
     }
 
@@ -675,7 +681,7 @@ public class ExifParser {
 
     private boolean seekTiffData(InputStream inputStream) throws IOException,
             ExifInvalidFormatException {
-        DataInputStream dataStream = new DataInputStream(inputStream);
+        CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
 
         if (dataStream.readShort() != JpegHeader.SOI) {
             throw new ExifInvalidFormatException("Invalid JPEG format");
@@ -695,6 +701,7 @@ public class ExifParser {
                     headerTail = dataStream.readShort();
                     length -= 6;
                     if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
+                        mTiffStartPosition = dataStream.getReadByteCount();
                         mApp1End = length;
                         return true;
                     }
@@ -709,6 +716,10 @@ public class ExifParser {
         return false;
     }
 
+    int getTiffStartPosition() {
+        return mTiffStartPosition;
+    }
+
     /**
      * Reads bytes from the InputStream.
      */
index cda67c2..753b18c 100644 (file)
@@ -915,9 +915,9 @@ public class ExifTag {
 
     static boolean isValidType(short type) {
         return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII ||
-               type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
-               type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
-               type == TYPE_LONG || type == TYPE_RATIONAL;
+                type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
+                type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
+                type == TYPE_LONG || type == TYPE_RATIONAL;
     }
 
     ExifTag(short tagId, short type, int componentCount, int ifd) {
index 78f9173..6336049 100644 (file)
@@ -79,6 +79,13 @@ class IfdData {
     }
 
     /**
+     * Removes the tag of the given ID
+     */
+    public void removeTag(short tagId) {
+        mExifTags.remove(tagId);
+    }
+
+    /**
      * Gets the tags count in the IFD.
      */
     public int getTagCount() {
diff --git a/tests/src/com/android/gallery3d/exif/ExifModifierTest.java b/tests/src/com/android/gallery3d/exif/ExifModifierTest.java
new file mode 100644 (file)
index 0000000..14e956f
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.gallery3d.exif;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExifModifierTest extends ExifXmlDataTestCase {
+
+    private File mTmpFile;
+    private List<Map<Short, List<String>>> mGroundTruth;
+
+    // TYPE_UNDEFINED with 4 components
+    private static final ExifTag sVersionTag = ExifTag.buildTag(ExifTag.TAG_EXIF_VERSION);
+    // TYPE_UNSIGNED_BYTE with 4 components
+    private static final ExifTag sGpsVersionTag = ExifTag.buildTag(ExifTag.TAG_GPS_VERSION_ID);
+    // TYPE ASCII with arbitary length
+    private static final ExifTag sModelTag = ExifTag.buildTag(ExifTag.TAG_MODEL);
+    // TYPE_ASCII with 20 components
+    private static final ExifTag sDateTimeTag = ExifTag.buildTag(ExifTag.TAG_DATE_TIME);
+    // TYPE_UNSIGNED_SHORT with 1 components
+    private static final ExifTag sCompressionTag = ExifTag.buildTag(ExifTag.TAG_COMPRESSION);
+    // TYPE_UNSIGNED_LONG with 1 components
+    private static final ExifTag sThumbnailFormatTag =
+            ExifTag.buildTag(ExifTag.TAG_JPEG_INTERCHANGE_FORMAT);
+    // TYPE_UNSIGNED_RATIONAL with 3 components
+    private static final ExifTag sLongitudeTag = ExifTag.buildTag(ExifTag.TAG_GPS_LONGITUDE);
+    // TYPE_RATIONAL with 1 components
+    private static final ExifTag sShutterTag = ExifTag.buildTag(ExifTag.TAG_SHUTTER_SPEED_VALUE);
+
+    private static final Map<Short, ExifTag> sTestTags = new HashMap<Short, ExifTag>();
+
+    static {
+        sVersionTag.setValue(new byte[] {1, 2, 3, 4});
+        sGpsVersionTag.setValue(new byte[] {4, 3, 2, 1});
+        sModelTag.setValue("end-of-the-world");
+        sDateTimeTag.setValue("2012:12:31 23:59:59");
+        sCompressionTag.setValue(100);
+        sThumbnailFormatTag.setValue(100);
+        sLongitudeTag.setValue(new Rational[] {new Rational(1, 1), new Rational(10, 10),
+                new Rational(100, 100)});
+        sShutterTag.setValue(new Rational(1, 1));
+
+        sTestTags.put(sVersionTag.getTagId(), sVersionTag);
+        sTestTags.put(sGpsVersionTag.getTagId(), sGpsVersionTag);
+        sTestTags.put(sModelTag.getTagId(), sModelTag);
+        sTestTags.put(sDateTimeTag.getTagId(), sDateTimeTag);
+        sTestTags.put(sCompressionTag.getTagId(), sCompressionTag);
+        sTestTags.put(sThumbnailFormatTag.getTagId(), sThumbnailFormatTag);
+        sTestTags.put(sLongitudeTag.getTagId(), sLongitudeTag);
+        sTestTags.put(sShutterTag.getTagId(), sShutterTag);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mGroundTruth = ExifXmlReader.readXml(getXmlParser());
+        mTmpFile = File.createTempFile("exif_test", ".jpg");
+        FileOutputStream os = null;
+        InputStream is = getImageInputStream();
+        try {
+            os = new FileOutputStream(mTmpFile);
+            byte[] buf = new byte[1024];
+            int n;
+            while ((n = is.read(buf)) > 0) {
+                os.write(buf, 0, n);
+            }
+        } finally {
+            Util.closeSilently(os);
+        }
+    }
+
+    public ExifModifierTest(int imageRes, int xmlRes) {
+        super(imageRes, xmlRes);
+    }
+
+    public ExifModifierTest(String imagePath, String xmlPath) {
+        super(imagePath, xmlPath);
+    }
+
+    public void testModify() throws Exception {
+        Map<Short, Boolean> results = new HashMap<Short, Boolean>();
+
+        RandomAccessFile file = null;
+        try {
+            file = new RandomAccessFile(mTmpFile, "rw");
+            MappedByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, file.length());
+            for (ExifTag tag: sTestTags.values()) {
+                ExifModifier modifier = new ExifModifier(buf);
+                modifier.modifyTag(tag);
+                boolean result = modifier.commit();
+                results.put(tag.getTagId(), result);
+                buf.force();
+                buf.position(0);
+
+                if (!result) {
+                    List<String> value = mGroundTruth.get(tag.getIfd()).get(tag.getTagId());
+                    assertTrue (String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+                            value == null || tag.getTagId() == ExifTag.TAG_MODEL);
+                }
+            }
+        } finally {
+            Util.closeSilently(file);
+        }
+
+        // Parse the new file and check the result
+        InputStream is = null;
+        try {
+            is = new FileInputStream(mTmpFile);
+            ExifData data = new ExifReader().read(is);
+            for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+                checkIfd(data.getIfdData(i), mGroundTruth.get(i), results);
+            }
+        } finally {
+            Util.closeSilently(is);
+        }
+
+    }
+
+
+    private void checkIfd(IfdData ifd, Map<Short, List<String>> ifdValue,
+            Map<Short, Boolean> results) {
+        if (ifd == null) {
+            assertEquals(getImageTitle(), 0 ,ifdValue.size());
+            return;
+        }
+        ExifTag[] tags = ifd.getAllTags();
+        for (ExifTag tag : tags) {
+            List<String> truth = ifdValue.get(tag.getTagId());
+            assertNotNull(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), truth);
+            if (truth.contains(null)) continue;
+
+            ExifTag newTag = sTestTags.get(tag.getTagId());
+            if (newTag != null
+                    && results.get(tag.getTagId())) {
+                assertEquals(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+                        Util.tagValueToString(newTag), Util.tagValueToString(tag));
+            } else {
+                assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+                        truth.contains(Util.tagValueToString(tag).trim()));
+            }
+        }
+        assertEquals(getImageTitle(), ifdValue.size(), tags.length);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        mTmpFile.delete();
+    }
+}
index 8c98fa0..789fd35 100644 (file)
@@ -103,7 +103,8 @@ public class ExifOutputStreamTest extends ExifXmlDataTestCase {
     }
 
     @Override
-    public void tearDown() {
+    public void tearDown() throws Exception {
+        super.tearDown();
         mTmpFile.delete();
     }
 }
index 519e871..b094263 100644 (file)
@@ -56,6 +56,7 @@ public class ExifTestRunner extends InstrumentationTestRunner {
         addAllTestsFromExifTestCase(ExifParserTest.class, suite);
         addAllTestsFromExifTestCase(ExifReaderTest.class, suite);
         addAllTestsFromExifTestCase(ExifOutputStreamTest.class, suite);
+        addAllTestsFromExifTestCase(ExifModifierTest.class, suite);
         return suite;
     }