From 0ff6b0c6501dd6b9436cb36a302ae9ce88c2a824 Mon Sep 17 00:00:00 2001 From: k3b <1374583+k3b@users.noreply.github.com> Date: Sun, 19 Apr 2020 17:22:03 +0200 Subject: [PATCH] #169: ExifInterface via IFile/FileFacade --- .../src/main/java/de/k3b/io/FileFacade.java | 14 +- .../main/java/de/k3b/media/ExifInterface.java | 482 +++++++++--------- .../java/de/k3b/media/ExifInterfaceEx.java | 15 - .../media/PhotoPropertiesUpdateHandler.java | 39 +- ...opertiesUpdateHandlerIntegrationTests.java | 25 +- 5 files changed, 274 insertions(+), 301 deletions(-) diff --git a/fotolib2/src/main/java/de/k3b/io/FileFacade.java b/fotolib2/src/main/java/de/k3b/io/FileFacade.java index 340abee8..b4bf0502 100644 --- a/fotolib2/src/main/java/de/k3b/io/FileFacade.java +++ b/fotolib2/src/main/java/de/k3b/io/FileFacade.java @@ -34,7 +34,7 @@ add support for Android DocumentFile */ public class FileFacade implements IFile { - private final java.io.File file; + private java.io.File file; public FileFacade(java.io.File file) { this.file = file; @@ -64,16 +64,24 @@ public static FileFacade[] get(java.io.File[] files) { @Deprecated @Override public boolean renameTo(IFile newName) { - return file.renameTo(((FileFacade) newName).file); + return renameImpl(((FileFacade) newName).file); } @Override public boolean renameTo(String newName) { File newFile = new File(this.file.getParentFile(), newName); - final boolean result = this.file.renameTo(newFile); + final boolean result = renameImpl(newFile); return result; } + private boolean renameImpl(File newFile) { + final boolean success = this.file.renameTo(newFile); + if (success) { + this.file = newFile; + } + return success; + } + @Override public boolean delete() { return file.delete(); diff --git a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java index 5786d613..44283391 100644 --- a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java +++ b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java @@ -1,7 +1,7 @@ // ExifInterface source code from android-6 - special version without jni /* * Copyright (C) 2007 The Android Open Source Project under the Apache License, Version 2.0 - * Copyright (C) 2016-2019 by k3b under the GPL-v3+. + * Copyright (C) 2016-2020 by k3b under the GPL-v3+. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -29,9 +29,7 @@ import java.io.DataInputStream; import java.io.EOFException; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; @@ -68,8 +66,6 @@ public class ExifInterface { // public to allow error filtering public static final String LOG_TAG = "ExifInterface"; - private static final boolean OLD_API = false; - private static final Logger logger = LoggerFactory.getLogger(LOG_TAG); private static final boolean DEBUG_INTERNAL = false; @@ -1139,12 +1135,8 @@ public ExifInterface(String filename, InputStream in) throws IOException { mExifFile = (filename != null) ? fileFacade.convert(new File(filename)) : null; if (in == null) { InputStream inputStream = null; - try { - inputStream = createInputStream(mExifFile); - loadAttributes(inputStream); - } finally { - closeQuietly(inputStream); - } + inputStream = createInputStream(mExifFile); + loadAttributes(inputStream); } else { loadAttributes(in); @@ -1445,6 +1437,7 @@ private void loadAttributes(InputStream in) { logWarn( "Invalid image.", e); validJpgExifFormat = false; } finally { + closeQuietly(in); if (DEBUG_INTERNAL) { logDebug(this.toString()); } @@ -1516,7 +1509,6 @@ public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFi // Keep the thumbnail in memory mThumbnailBytes = getThumbnail(inFile); - IFile renamedInFile = inFile; boolean overwriteOriginal = inFile.equals(currentOutFile); @@ -1527,33 +1519,25 @@ public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFi currentOutFile = outFile.getParentFile().create(tempName, outFile.getMime()); } - InputStream in = null; - OutputStream out = null; - - try { - in = createInputStream(renamedInFile); - out = createOutputStream(outFile); - - saveJpegAttributes(in, out, mThumbnailBytes); - } finally { - closeQuietly(in); - closeQuietly(out); - } + saveJpegAttributes( + createInputStream(inFile), createOutputStream(currentOutFile), mThumbnailBytes); // all exif writing is done successfully - if (overwriteOriginal) { + if (overwriteOriginal && currentOutFile.exists()) { // cleanup String savedOriginalName = originalName + ".old.jpg"; IFile previousFailedOverwriteOriginal = inFile.getParentFile().findExisting(savedOriginalName); if (previousFailedOverwriteOriginal != null) { - logDebug(String.format("delete old %s", previousFailedOverwriteOriginal)); + if (DEBUG_INTERNAL) { + logDebug(String.format("delete old %s", previousFailedOverwriteOriginal)); + } previousFailedOverwriteOriginal.delete(); } renameOrThrow(inFile, savedOriginalName); renameOrThrow(currentOutFile, originalName); } - if (deleteInFileOnFinish || overwriteOriginal) { + if ((deleteInFileOnFinish || overwriteOriginal) && inFile.exists()) { deleteFile(inFile); } @@ -1654,28 +1638,30 @@ public byte[] getThumbnail(IFile inFile) { return getThumbnail(in); } catch (IOException e) { // Couldn't get a thumbnail image. - } finally { - closeQuietly(in); } return null; } public byte[] getThumbnail(InputStream in) throws IOException { - if (!mHasThumbnail) { - return null; - } - if (mThumbnailBytes != null) { - return mThumbnailBytes; - } - if (in.skip(mThumbnailOffset) != mThumbnailOffset) { - throw new IOException("Corrupted image"); - } - byte[] buffer = new byte[mThumbnailLength]; - if (in.read(buffer) != mThumbnailLength) { - throw new IOException("Corrupted image"); + try { + if (!mHasThumbnail) { + return null; + } + if (mThumbnailBytes != null) { + return mThumbnailBytes; + } + if (in.skip(mThumbnailOffset) != mThumbnailOffset) { + throw new IOException("Corrupted image"); + } + byte[] buffer = new byte[mThumbnailLength]; + if (in.read(buffer) != mThumbnailLength) { + throw new IOException("Corrupted image"); + } + mThumbnailBytes = buffer; + return buffer; + } finally { + closeQuietly(in); } - mThumbnailBytes = buffer; - return buffer; } private void closeQuietly(Closeable in) { @@ -1825,126 +1811,139 @@ private void getJpegAttributes(InputStream inputStream) throws IOException { if (DEBUG_INTERNAL) { logDebug( "getJpegAttributes starting with: " + inputStream); } - DataInputStream dataInputStream = new DataInputStream(inputStream); - byte marker; - int bytesRead = 0; - if ((marker = dataInputStream.readByte()) != MARKER) { - throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); - } - ++bytesRead; - if (dataInputStream.readByte() != MARKER_SOI) { - throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); - } - ++bytesRead; - while (true) { - marker = dataInputStream.readByte(); - if (marker != MARKER) { - throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); + DataInputStream dataInputStream = null; + try { + dataInputStream = new DataInputStream(inputStream); + byte marker; + int bytesRead = 0; + if ((marker = dataInputStream.readByte()) != MARKER) { + throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } ++bytesRead; - marker = dataInputStream.readByte(); - if (DEBUG_INTERNAL) { - logDebug( "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); + if (dataInputStream.readByte() != MARKER_SOI) { + throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } ++bytesRead; - // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and - // the image data will terminate right after. - if (marker == MARKER_EOI || marker == MARKER_SOS) { - break; - } - int length = dataInputStream.readUnsignedShort() - 2; - bytesRead += 2; - if (DEBUG_INTERNAL) { - logDebug( "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " - + (length + 2) + ")"); - } - if (length < 0) { - throw new IOException("Invalid length"); - } - switch (marker) { - case MARKER_APP1: { - if (DEBUG_INTERNAL) { - logDebug( "MARKER_APP1"); - } - if (length < 6) { - // Skip if it's not an EXIF APP1 segment. + while (true) { + marker = dataInputStream.readByte(); + if (marker != MARKER) { + throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); + } + ++bytesRead; + marker = dataInputStream.readByte(); + if (DEBUG_INTERNAL) { + logDebug("Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); + } + ++bytesRead; + // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and + // the image data will terminate right after. + if (marker == MARKER_EOI || marker == MARKER_SOS) { + break; + } + int length = dataInputStream.readUnsignedShort() - 2; + bytesRead += 2; + if (DEBUG_INTERNAL) { + logDebug("JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " + + (length + 2) + ")"); + } + if (length < 0) { + throw new IOException("Invalid length"); + } + switch (marker) { + case MARKER_APP1: { + if (DEBUG_INTERNAL) { + logDebug("MARKER_APP1"); + } + if (length < 6) { + // Skip if it's not an EXIF APP1 segment. + break; + } + byte[] identifier = new byte[6]; + if (inputStream.read(identifier) != 6) { + throw new IOException("Invalid exif"); + } + bytesRead += 6; + length -= 6; + if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { + // Skip if it's not an EXIF APP1 segment. + break; + } + if (length <= 0) { + throw new IOException("Invalid exif"); + } + if (DEBUG_INTERNAL) { + logDebug("readExifSegment with a byte array (length: " + length + ")"); + } + byte[] bytes = new byte[length]; + if (dataInputStream.read(bytes) != length) { + throw new IOException("Invalid exif"); + } + readExifSegment(bytes, bytesRead); + bytesRead += length; + length = 0; break; } - byte[] identifier = new byte[6]; - if (inputStream.read(identifier) != 6) { - throw new IOException("Invalid exif"); - } - bytesRead += 6; - length -= 6; - if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { - // Skip if it's not an EXIF APP1 segment. + case MARKER_COM: { + byte[] bytes = new byte[length]; + if (dataInputStream.read(bytes) != length) { + throw new IOException("Invalid exif"); + } + length = 0; + if (getAttribute(TAG_USER_COMMENT) == null) { + setAttribute(IFD_EXIF_HINT, TAG_USER_COMMENT, ExifAttribute.createString( + EXIF_TAG_USER_COMMENT, + decodePrefixString(bytes.length, bytes, ASCII))); + } break; } - if (length <= 0) { - throw new IOException("Invalid exif"); - } - if (DEBUG_INTERNAL) { - logDebug( "readExifSegment with a byte array (length: " + length + ")"); - } - byte[] bytes = new byte[length]; - if (dataInputStream.read(bytes) != length) { - throw new IOException("Invalid exif"); - } - readExifSegment(bytes, bytesRead); - bytesRead += length; - length = 0; - break; - } - case MARKER_COM: { - byte[] bytes = new byte[length]; - if (dataInputStream.read(bytes) != length) { - throw new IOException("Invalid exif"); + case MARKER_SOF0: + case MARKER_SOF1: + case MARKER_SOF2: + case MARKER_SOF3: + case MARKER_SOF5: + case MARKER_SOF6: + case MARKER_SOF7: + case MARKER_SOF9: + case MARKER_SOF10: + case MARKER_SOF11: + case MARKER_SOF13: + case MARKER_SOF14: + case MARKER_SOF15: { + if (dataInputStream.skipBytes(1) != 1) { + throw new IOException("Invalid SOFx"); + } + setAttribute(IFD_TIFF_HINT, TAG_IMAGE_LENGTH, ExifAttribute.createULong(EXIF_TAG_IMAGE_LENGTH, + dataInputStream.readUnsignedShort(), mExifByteOrder)); + setAttribute(IFD_TIFF_HINT, TAG_IMAGE_WIDTH, ExifAttribute.createULong(EXIF_TAG_IMAGE_WIDTH, + dataInputStream.readUnsignedShort(), mExifByteOrder)); + length -= 5; + break; } - length = 0; - if (getAttribute(TAG_USER_COMMENT) == null) { - setAttribute(IFD_EXIF_HINT, TAG_USER_COMMENT,ExifAttribute.createString( - EXIF_TAG_USER_COMMENT, - decodePrefixString(bytes.length, bytes,ASCII))); + default: { + break; } - break; } - case MARKER_SOF0: - case MARKER_SOF1: - case MARKER_SOF2: - case MARKER_SOF3: - case MARKER_SOF5: - case MARKER_SOF6: - case MARKER_SOF7: - case MARKER_SOF9: - case MARKER_SOF10: - case MARKER_SOF11: - case MARKER_SOF13: - case MARKER_SOF14: - case MARKER_SOF15: { - if (dataInputStream.skipBytes(1) != 1) { - throw new IOException("Invalid SOFx"); - } - setAttribute(IFD_TIFF_HINT, TAG_IMAGE_LENGTH, ExifAttribute.createULong(EXIF_TAG_IMAGE_LENGTH, - dataInputStream.readUnsignedShort(), mExifByteOrder)); - setAttribute(IFD_TIFF_HINT, TAG_IMAGE_WIDTH, ExifAttribute.createULong(EXIF_TAG_IMAGE_WIDTH, - dataInputStream.readUnsignedShort(), mExifByteOrder)); - length -= 5; - break; + if (length < 0) { + throw new IOException("Invalid length"); } - default: { - break; + if (dataInputStream.skipBytes(length) != length) { + throw new IOException("Invalid JPEG segment"); } + bytesRead += length; } - if (length < 0) { - throw new IOException("Invalid length"); - } - if (dataInputStream.skipBytes(length) != length) { - throw new IOException("Invalid JPEG segment"); - } - bytesRead += length; + } finally { + closeQuietly(dataInputStream); } } - // Stores a new JPEG image with EXIF attributes into a given output stream. + + /** + * Stores a new JPEG image with EXIF attributes into a given output stream. + * + * @param inputStream where jpg data comes from. will be closed on exit + * @param outputStream where jpg data goes to. will be closed on exit + * @param thumbnail if not null will be stored as output-jpg-exif-thumbnail + * @throws IOException + */ public void saveJpegAttributes(InputStream inputStream, OutputStream outputStream, byte[] thumbnail) throws IOException { // See JPEG File Interchange Format Specification page 5. @@ -1952,91 +1951,97 @@ public void saveJpegAttributes(InputStream inputStream, OutputStream outputStrea logDebug( "saveJpegAttributes starting with (inputStream: " + inputStream + ", outputStream: " + outputStream + ")"); } - DataInputStream dataInputStream = new DataInputStream(inputStream); - ByteOrderAwarenessDataOutputStream dataOutputStream = - new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); - if (dataInputStream.readByte() != MARKER) { - throw new IOException("Invalid marker"); - } - dataOutputStream.writeByte(MARKER); - if (dataInputStream.readByte() != MARKER_SOI) { - throw new IOException("Invalid marker"); - } - dataOutputStream.writeByte(MARKER_SOI); - // Write EXIF APP1 segment - dataOutputStream.writeByte(MARKER); - dataOutputStream.writeByte(MARKER_APP1); - - writeExifSegment(dataOutputStream, 6, thumbnail); - byte[] bytes = new byte[4096]; - while (true) { - byte marker = dataInputStream.readByte(); - if (marker != MARKER) { + DataInputStream dataInputStream = null; + ByteOrderAwarenessDataOutputStream dataOutputStream = null; + try { + dataInputStream = new DataInputStream(inputStream); + dataOutputStream = new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); + if (dataInputStream.readByte() != MARKER) { throw new IOException("Invalid marker"); } - marker = dataInputStream.readByte(); - switch (marker) { - case MARKER_APP1: { - int length = dataInputStream.readUnsignedShort() - 2; - if (length < 0) { - throw new IOException("Invalid length"); - } - byte[] identifier = new byte[6]; - if (length >= 6) { - if (dataInputStream.read(identifier) != 6) { - throw new IOException("Invalid exif"); + dataOutputStream.writeByte(MARKER); + if (dataInputStream.readByte() != MARKER_SOI) { + throw new IOException("Invalid marker"); + } + dataOutputStream.writeByte(MARKER_SOI); + // Write EXIF APP1 segment + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(MARKER_APP1); + + writeExifSegment(dataOutputStream, 6, thumbnail); + byte[] bytes = new byte[4096]; + while (true) { + byte marker = dataInputStream.readByte(); + if (marker != MARKER) { + throw new IOException("Invalid marker"); + } + marker = dataInputStream.readByte(); + switch (marker) { + case MARKER_APP1: { + int length = dataInputStream.readUnsignedShort() - 2; + if (length < 0) { + throw new IOException("Invalid length"); } - if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { - // Skip the original EXIF APP1 segment. - if (dataInputStream.skip(length - 6) != length - 6) { - throw new IOException("Invalid length"); + byte[] identifier = new byte[6]; + if (length >= 6) { + if (dataInputStream.read(identifier) != 6) { + throw new IOException("Invalid exif"); + } + if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { + // Skip the original EXIF APP1 segment. + if (dataInputStream.skip(length - 6) != length - 6) { + throw new IOException("Invalid length"); + } + break; } - break; } + // Copy non-EXIF APP1 segment. + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(marker); + dataOutputStream.writeUnsignedShort(length + 2); + if (length >= 6) { + length -= 6; + dataOutputStream.write(identifier); + } + int read; + while (length > 0 && (read = dataInputStream.read( + bytes, 0, Math.min(length, bytes.length))) >= 0) { + dataOutputStream.write(bytes, 0, read); + length -= read; + } + break; } - // Copy non-EXIF APP1 segment. - dataOutputStream.writeByte(MARKER); - dataOutputStream.writeByte(marker); - dataOutputStream.writeUnsignedShort(length + 2); - if (length >= 6) { - length -= 6; - dataOutputStream.write(identifier); - } - int read; - while (length > 0 && (read = dataInputStream.read( - bytes, 0, Math.min(length, bytes.length))) >= 0) { - dataOutputStream.write(bytes, 0, read); - length -= read; - } - break; - } - case MARKER_EOI: - case MARKER_SOS: { - dataOutputStream.writeByte(MARKER); - dataOutputStream.writeByte(marker); - // Copy all the remaining data - streamCopy(dataInputStream, dataOutputStream); - return; - } - default: { - // Copy JPEG segment - dataOutputStream.writeByte(MARKER); - dataOutputStream.writeByte(marker); - int length = dataInputStream.readUnsignedShort(); - dataOutputStream.writeUnsignedShort(length); - length -= 2; - if (length < 0) { - throw new IOException("Invalid length"); + case MARKER_EOI: + case MARKER_SOS: { + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(marker); + // Copy all the remaining data + streamCopy(dataInputStream, dataOutputStream); + return; } - int read; - while (length > 0 && (read = dataInputStream.read( - bytes, 0, Math.min(length, bytes.length))) >= 0) { - dataOutputStream.write(bytes, 0, read); - length -= read; + default: { + // Copy JPEG segment + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(marker); + int length = dataInputStream.readUnsignedShort(); + dataOutputStream.writeUnsignedShort(length); + length -= 2; + if (length < 0) { + throw new IOException("Invalid length"); + } + int read; + while (length > 0 && (read = dataInputStream.read( + bytes, 0, Math.min(length, bytes.length))) >= 0) { + dataOutputStream.write(bytes, 0, read); + length -= read; + } + break; } - break; } } + } finally { + closeQuietly(dataOutputStream); + closeQuietly(dataInputStream); } } @@ -2434,6 +2439,7 @@ private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); return totalSize; } + /** * Determines the data format of EXIF entry value. * @@ -2789,19 +2795,14 @@ private static boolean startsWith(byte[] content, byte[] prefix) { //------------- File api to be overwritten for android specific DocumentFile implementation protected InputStream createInputStream(IFile exifFile) throws FileNotFoundException { - if (OLD_API) return new FileInputStream(((FileFacade) exifFile).getFile()); return exifFile.openInputStream(); } protected OutputStream createOutputStream(IFile outFile) throws FileNotFoundException { - if (OLD_API) return new FileOutputStream(((FileFacade) outFile).getFile()); - return mExifFile.openOutputStream(); + return outFile.openOutputStream(); } protected boolean renameTo(IFile originalInFile, IFile renamedInFile) { - if (OLD_API) { - return ((FileFacade) originalInFile).getFile().renameTo(((FileFacade) renamedInFile).getFile()); - } return originalInFile.renameTo(renamedInFile.getName()); } @@ -2812,29 +2813,4 @@ protected String getAbsolutePath(IFile inFile) { protected boolean deleteFile(IFile renamedInFile) { return renamedInFile.delete(); } - - @Deprecated - protected InputStream createInputStream(File exifFile) throws FileNotFoundException { - return new FileInputStream(exifFile); - } - - @Deprecated - protected OutputStream createOutputStream(File outFile) throws FileNotFoundException { - return new FileOutputStream(outFile); - } - - @Deprecated - protected boolean renameTo(File originalInFile, File renamedInFile) { - return originalInFile.renameTo(renamedInFile); - } - - @Deprecated - protected String getAbsolutePath(File inFile) { - return inFile.getAbsolutePath(); - } - - @Deprecated - protected boolean deleteFile(File file) { - return file.delete(); - } } diff --git a/fotolib2/src/main/java/de/k3b/media/ExifInterfaceEx.java b/fotolib2/src/main/java/de/k3b/media/ExifInterfaceEx.java index 042d80d2..324e75b7 100644 --- a/fotolib2/src/main/java/de/k3b/media/ExifInterfaceEx.java +++ b/fotolib2/src/main/java/de/k3b/media/ExifInterfaceEx.java @@ -35,7 +35,6 @@ import java.util.TimeZone; import de.k3b.LibGlobal; -import de.k3b.io.FileFacade; import de.k3b.io.IFile; import de.k3b.io.ListUtils; import de.k3b.io.VISIBILITY; @@ -109,15 +108,6 @@ public static int getOrientationId(String fullPath) { protected ExifInterfaceEx() {super();xmpExtern=null; mDbg_context = "";} - /** - * @deprecated use {@link #saveAttributes(IFile, IFile, boolean)} instead - */ - @Deprecated - @Override - public void saveAttributes(File inFile, File outFile, boolean deleteInFileOnFinish) throws IOException { - saveAttributes(fileFacade.convert(inFile), fileFacade.convert(outFile), deleteInFileOnFinish); - } - @Override public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFinish) throws IOException { fixDateTakenIfNeccessary(inFile); @@ -135,11 +125,6 @@ public void saveJpegAttributes(InputStream inputStream, OutputStream outputStrea super.saveJpegAttributes(inputStream, outputStream, thumbnail); } - @Override - protected boolean deleteFile(File file) { - return deleteFile(new FileFacade(file)); - } - @Override protected boolean deleteFile(IFile file) { boolean result = super.deleteFile(file); diff --git a/fotolib2/src/main/java/de/k3b/media/PhotoPropertiesUpdateHandler.java b/fotolib2/src/main/java/de/k3b/media/PhotoPropertiesUpdateHandler.java index 982976d5..d17bd4d1 100644 --- a/fotolib2/src/main/java/de/k3b/media/PhotoPropertiesUpdateHandler.java +++ b/fotolib2/src/main/java/de/k3b/media/PhotoPropertiesUpdateHandler.java @@ -54,6 +54,14 @@ public class PhotoPropertiesUpdateHandler extends PhotoPropertiesWrapper impleme private boolean deleteOriginalAfterFinish; // true: after save original jpg/mxp are deleted (move instead of copy) private long dbgLoadEndTimestamp; + private PhotoPropertiesUpdateHandler( + IPhotoProperties readChild, IPhotoProperties writeChild, + ExifInterfaceEx exif, PhotoPropertiesXmpSegment xmp) { + super(readChild, writeChild); + this.exif = exif; + this.xmp = xmp; + } + /** * public api: Factory to create PhotoPropertiesUpdateHandler. Settings/ Internal state determine * configuration for PhotoPropertiesUpdateHandler. @@ -66,9 +74,9 @@ public class PhotoPropertiesUpdateHandler extends PhotoPropertiesWrapper impleme * @return new loaded instance * @throws IOException */ - public static PhotoPropertiesUpdateHandler create(String absoluteJpgInPath, String absoluteJpgOutPath, - boolean deleteOriginalAfterFinish, String dbg_context) - throws IOException { + public static PhotoPropertiesUpdateHandler create( + String absoluteJpgInPath, String absoluteJpgOutPath, + boolean deleteOriginalAfterFinish, String dbg_context) throws IOException { return create(absoluteJpgInPath, absoluteJpgOutPath, deleteOriginalAfterFinish, dbg_context, LibGlobal.mediaUpdateStrategy.contains("J"), // write jpg file LibGlobal.mediaUpdateStrategy.contains("X"), // write xmp file @@ -90,9 +98,10 @@ public static PhotoPropertiesUpdateHandler create(String absoluteJpgInPath, Stri * @return new loaded instance * @throws IOException */ - public static PhotoPropertiesUpdateHandler create(String absoluteJpgInPath, String absoluteJpgOutPath, - boolean deleteOriginalAfterFinish, String dbg_context, - boolean writeJpg, boolean writeXmp, boolean createXmpIfNotExist) + protected static PhotoPropertiesUpdateHandler create( + String absoluteJpgInPath, String absoluteJpgOutPath, + boolean deleteOriginalAfterFinish, String dbg_context, + boolean writeJpg, boolean writeXmp, boolean createXmpIfNotExist) throws IOException { long startTimestamp = 0; if (LibGlobal.debugEnabledJpgMetaIo) { @@ -162,13 +171,6 @@ public static PhotoPropertiesUpdateHandler create(String absoluteJpgInPath, Stri return result; } - private PhotoPropertiesUpdateHandler(IPhotoProperties readChild, IPhotoProperties writeChild, ExifInterfaceEx exif, PhotoPropertiesXmpSegment xmp) - { - super(readChild, writeChild); - this.exif = exif; - this.xmp = xmp; - } - public void setAbsoluteJpgOutPath(String absoluteJpgOutPath) { this.absoluteJpgOutPath = absoluteJpgOutPath; } @@ -233,7 +235,9 @@ private int transferXmp(String dbg_context) throws IOException { return changedFiles; } - private int copyReplaceIfExist(String absoluteJpgInPath, String outJpgFullPath, boolean longFormat, String dbg_context) throws IOException { + private int copyReplaceIfExist( + String absoluteJpgInPath, String outJpgFullPath, + boolean longFormat, String dbg_context) throws IOException { int changedFiles = 0; File xmpInFile = FileCommands.getExistingSidecarOrNull(absoluteJpgInPath, longFormat); if (xmpInFile != null) { @@ -245,9 +249,10 @@ private int copyReplaceIfExist(String absoluteJpgInPath, String outJpgFullPath, return changedFiles; } - private int saveXmp(PhotoPropertiesXmpSegment xmp, - String outFullJpgPath, - boolean isLongFileName, String dbg_context) throws IOException { + private int saveXmp( + PhotoPropertiesXmpSegment xmp, + String outFullJpgPath, + boolean isLongFileName, String dbg_context) throws IOException { File xmpOutFile = FileCommands.getSidecar(outFullJpgPath, isLongFileName); xmp.save(xmpOutFile, LibGlobal.debugEnabledJpgMetaIo, dbg_context); return 1; diff --git a/fotolib2/src/test/java/de/k3b/media/PhotoPropertiesUpdateHandlerIntegrationTests.java b/fotolib2/src/test/java/de/k3b/media/PhotoPropertiesUpdateHandlerIntegrationTests.java index e690cea0..8a2c5608 100644 --- a/fotolib2/src/test/java/de/k3b/media/PhotoPropertiesUpdateHandlerIntegrationTests.java +++ b/fotolib2/src/test/java/de/k3b/media/PhotoPropertiesUpdateHandlerIntegrationTests.java @@ -53,8 +53,7 @@ public static void initDirectories() { } @Test - public void emptyWriteEmptyExifXmpCreate() throws IOException - { + public void emptyWriteEmptyExifXmpCreate() throws IOException { ExifInterfaceEx.fixDateOnSave = false; File out = new File(OUTDIR,"emptyWriteEmptyExifXmpCreate.jpg"); @@ -80,8 +79,7 @@ public void emptyWriteEmptyExifXmpCreate() throws IOException } @Test - public void existingWriteEmptyExifXmp() throws IOException - { + public void existingWriteEmptyExifXmp() throws IOException { // workaround UserComment=null is not implemented ExifInterfaceEx.useUserComment = false; ExifInterfaceEx.fixDateOnSave = false; @@ -111,13 +109,14 @@ public void existingWriteEmptyExifXmp() throws IOException } @Test - public void existingWriteValueExifXmpCreate() throws IOException - { + public void existingWriteValueExifXmpCreate() throws IOException { File out = new File(OUTDIR,"existingWriteValueExifXmpCreate.jpg"); TestUtil.saveTestResourceAs(TestUtil.TEST_FILE_JPG_WITH_EXIF, out); - PhotoPropertiesUpdateHandler sut = PhotoPropertiesUpdateHandler.create(out.getAbsolutePath(), null, false, "JUnit" - , true, true, true); //exif, xmp, create + PhotoPropertiesUpdateHandler sut = PhotoPropertiesUpdateHandler.create( + out.getAbsolutePath(), null, + false, "JUnit", + true, true, true); //exif, xmp, create PhotoPropertiesDTO value = createTestValue(); PhotoPropertiesUtil.copy(sut, value, true, true); @@ -141,8 +140,7 @@ private static PhotoPropertiesDTO createTestValue() { @Test - public void emptyWriteValuesXmpCreate() throws IOException - { + public void emptyWriteValuesXmpCreate() throws IOException { File out = new File(OUTDIR,"emptyWriteValuesXmpCreate.jpg"); TestUtil.saveTestResourceAs(TestUtil.TEST_FILE_JPG_WITH_NO_EXIF, out); @@ -162,8 +160,7 @@ public void emptyWriteValuesXmpCreate() throws IOException } @Test - public void emptyWriteValuesExifOnly() throws IOException - { + public void emptyWriteValuesExifOnly() throws IOException { File out = new File(OUTDIR,"emptyWriteValuesExifOnly.jpg"); TestUtil.saveTestResourceAs(TestUtil.TEST_FILE_JPG_WITH_NO_EXIF, out); @@ -182,7 +179,9 @@ public void emptyWriteValuesExifOnly() throws IOException // logger.info(sut.toString()); } - private void assertEqual(File file, PhotoPropertiesDTO expectedExif, PhotoPropertiesDTO expectedXmp, PhotoPropertiesUpdateHandler oldSut) throws IOException { + private void assertEqual( + File file, PhotoPropertiesDTO expectedExif, PhotoPropertiesDTO expectedXmp, + PhotoPropertiesUpdateHandler oldSut) throws IOException { PhotoPropertiesUpdateHandler sut = PhotoPropertiesUpdateHandler.create(file.getAbsolutePath(), null, false, "JUnit-check" , true, true, false); //exif, xmp, create