/**
*
*/
package geodress.model.writer;
import geodress.exceptions.FileTypeNotSupportedException;
import geodress.exceptions.MetaDataErrorException;
import geodress.exceptions.OperationNotSupportedException;
import geodress.main.InfoConstants;
import geodress.main.Logging;
import geodress.model.PictureFilter;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A writer for EXIF data (in JPEG files) using <a
* href="http://www.sno.phy.queensu.ca/~phil/exiftool/">ExifTool</a>.
*
* @author Stefan T.
*/
public class ExifToolWriter implements MetaDataWriter {
/** Logger object */
private static Logger logger = null;
/** verbose level of ExifTool, 0 = just a few / 5 = much information */
private final int verboseLevel = 1;
/** TRUE if a backup should be done before writing data */
private boolean backup = true;
/**
* a map with all field values that should be saved, the key contains the
* name of the field as used by ExifTool
*/
private Map<String, String> saveMap;
/**
* The path to the ExifTool executable (NULL if not available) or simple
* <tt>exiftool</tt> if set as a system variable.
*/
private String exifToolPath = null;
/**
* Initializes class.
*/
public ExifToolWriter() {
/* initializes logger */
logger = Logging.getLogger(this.getClass().getName());
saveMap = new HashMap<String, String>();
/* try to find ExifTool */
try {
setExifToolPath("exiftool");
} catch (IOException ioe) {
logger.log(Level.FINER,
"ExifTool is not set as an environment variable", ioe);
}
}
/**
* @see geodress.model.writer.MetaDataWriter#setData(int, java.lang.String)
*/
@Override
public void setData(int field, String value)
throws OperationNotSupportedException {
if (field < 0 || 5 < field) {
throw new OperationNotSupportedException("writing for field "
+ field + " is not supported yet");
}
if (!isExifToolAvailable()) {
logger.log(Level.CONFIG, "ExifTool is not available, "
+ "but setData(int, String) is used.");
}
String fieldName = "";
switch (field) {
case InfoConstants.GPS_LATITUDE:
throw new OperationNotSupportedException(
"writing latitude is not supported yet");
case InfoConstants.GPS_LONGITUDE:
throw new OperationNotSupportedException(
"writing longitude is not supported yet");
case InfoConstants.DATE_TIME:
throw new OperationNotSupportedException(
"writing date/time is not supported yet");
case InfoConstants.USER_COMMENT:
logger.log(Level.FINER, "new user comment for writing: " + value);
fieldName = "EXIF:UserComment";
break;
case InfoConstants.IMAGE_DESCRIPTION:
logger.log(Level.FINER, "new image description for writing: "
+ value);
fieldName = "EXIF:ImageDescription";
break;
default:
throw new OperationNotSupportedException("writing for field "
+ field + " is not supported yet");
}
saveMap.put(fieldName, value);
}
/**
* @see geodress.model.writer.MetaDataWriter#write(java.io.File)
*/
@Override
public void write(File file) throws MetaDataErrorException, IOException,
FileTypeNotSupportedException {
if (!saveMap.isEmpty()) {
/* check if file exists */
if (!file.exists()) {
throw new IOException("The file " + file + " does not exist.");
}
/* reject picture if its type is not JPEG */
if (!new PictureFilter().accept(file)) {
throw new FileTypeNotSupportedException("the file type of "
+ file.getAbsolutePath()
+ " is not supported for writing EXIF data");
}
/* check if ExifTool is available */
if (!isExifToolAvailable()) {
throw new MetaDataErrorException(
"ExifTool is not available, but needed for EXIF data writing.");
}
List<String> command = new LinkedList<String>();
command.add(exifToolPath);
command.add("-v" + verboseLevel);
if (!backup) {
command.add("-overwrite_original");
}
for (String field : saveMap.keySet()) {
command.add("-" + field + "=" + saveMap.get(field));
}
command.add(file.getAbsolutePath());
logger.log(Level.FINE, "executing" + command);
/* execute */
ProcessBuilder pb = new ProcessBuilder(command);
Process p = pb.start();
String s, output = "";
BufferedReader br = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((s = br.readLine()) != null) {
output = output + s + "\n";
}
/* check output if error occurred */
try {
p.waitFor();
} catch (InterruptedException ie) {
logger.log(Level.WARNING,
"exception while waiting for ExifTool terminating", ie);
}
if (p.exitValue() != 0) {
throw new IOException(
"ExifTool execution returned exit status "
+ p.exitValue()
+ " instead of 0. Other output was: " + output);
}
} else {
logger.log(Level.FINEST,
"empty saveMap, so no writing operations in " + file);
}
}
/**
* Returns the current path to ExifTool.
*
* @return the exifToolPath
*/
public String getExifToolPath() {
return exifToolPath;
}
/**
* Set the path to ExifTool
*
* @param exifToolPath
* the exifToolPath to set
* @throws IOException
* thrown if ExifTool could not be found
*/
public void setExifToolPath(String exifToolPath) throws IOException {
/* check if it is really accessible */
List<String> command = new LinkedList<String>();
command.add(exifToolPath);
new ProcessBuilder(command).start();
/* no exception -> ExifTool accessible */
this.exifToolPath = exifToolPath;
logger.log(Level.FINE, "a new path for ExifTool was set: "
+ exifToolPath);
}
/**
* Checks if the ExifTool is available.
*
* @return TRUE if ExifTool is available
*/
public boolean isExifToolAvailable() {
return (exifToolPath != null);
}
/**
* @see geodress.model.writer.MetaDataWriter#setBackupMode(boolean)
*/
@Override
public void setBackupMode(boolean backup) {
this.backup = backup;
}
}