/* Copyright (c) 2008 Google Inc. * * 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.google.gdata.data.photos; import com.google.gdata.util.common.xml.XmlWriter; import com.google.gdata.data.Extension; import com.google.gdata.data.ExtensionDescription; import com.google.gdata.data.ExtensionPoint; import com.google.gdata.data.ExtensionProfile; import com.google.gdata.data.photos.impl.ExifTag; import com.google.gdata.util.ParseException; import com.google.gdata.util.XmlParser; import com.google.gdata.util.XmlParser.ElementHandler; import org.xml.sax.Attributes; import java.io.IOException; import java.util.Collection; import java.util.Date; /** * A photo's exif tags. Exif tags are represented as a collection element with * nested elements, because that way clients can iterate over the exif tags * without having to know ahead of time exactly what is in it. We also support * retrieval of particular exif tags if the client knows what they want. Some * standard tags are supported with helper methods to retrieve them by name. * * */ public class ExifTags extends ExtensionPoint implements Extensible, Extension { /* * Declare the extensions for our tags, which contains a single repeated tag * element that can have varying names (but a single exif namespace). */ @Override public void declareExtensions(ExtensionProfile extProfile) { extProfile.declare(ExifTags.class, ExifTag.getDefaultDescription()); extProfile.declareArbitraryXmlExtension(ExifTags.class); super.declareExtensions(extProfile); } /* * The default description for exif tags is just an element with the name * "tags" */ public static ExtensionDescription getDefaultDescription() { return new ExtensionDescription(ExifTags.class, Namespaces.EXIF_NAMESPACE, "tags"); } /** * Gets the exif tags as a collection of {@link ExifTag}. */ public Collection<ExifTag> getExifTags() { return getRepeatingExtension(ExifTag.class); } /** * Get a particular exif tag by name. This will retrieve the proper exif * tag based on the name. */ public ExifTag getExifTag(String exifName) { Collection<ExifTag> tags = getExifTags(); for (ExifTag tag : tags) { if (tag.getName().equals(exifName)) { return tag; } } return null; } /** * Gets the value of a particular exif tag, or null if it doesn't exist. */ public String getExifTagValue(String exifName) { ExifTag tag = getExifTag(exifName); return tag == null ? null : tag.getValue(); } /** * Convenience method to set an exif tag based on a float value. */ public void setExifTagValue(String name, Number value) { if (value != null && value.floatValue() != 0.0F) { setExifTagValue(name, value.toString()); } else { setExifTagValue(name, (String) null); } } /** * Sets the value of a particular exif tag by name. */ public void setExifTagValue(String name, String value) { ExifTag tag = getExifTag(name); if (tag != null) { removeRepeatingExtension(tag); } if (value != null) { addRepeatingExtension(new ExifTag(name, value)); } } // Convenience methods to retrieve well-known exif data. /** * @return the make of the camera, i.e. Nikon, Canon, Sony. */ public String getCameraMake() { return getExifTagValue("make"); } /** * Set the make of the camera used. */ public void setCameraMake(String make) { setExifTagValue("make", make); } /** * @return the model of the camera used. */ public String getCameraModel() { return getExifTagValue("model"); } /** * Set the model of the camera used. */ public void setCameraModel(String model) { setExifTagValue("model", model); } /** * @return the iso equivalent value used. * @throws ParseException if the value was not parsable as an integer. */ public Integer getIsoEquivalent() throws ParseException { String iso = getExifTagValue("iso"); if (iso == null) { return null; } else { try { return Integer.valueOf(iso); } catch (NumberFormatException nfe) { throw new ParseException("Invalid Iso field " + iso, nfe); } } } /** * Set the iso equivalent value used. */ public void setIsoEquivalent(Integer iso) { setExifTagValue("iso", iso); } /** * @return the exposure time used. */ public Float getExposureTime() throws ParseException { String exposure = getExifTagValue("exposure"); if (exposure == null) { return null; } else { try { return Float.valueOf(exposure); } catch (NumberFormatException nfe) { throw new ParseException("Invalid exposure field " + exposure, nfe); } } } /** * Set the exposure time used. */ public void setExposureTime(Float exposure) { setExifTagValue("exposure", exposure); } /** * @return the fstop value used. * @throws ParseException if the value is not a valid floating point number. */ public Float getApetureFNumber() throws ParseException { String fstop = getExifTagValue("fstop"); if (fstop == null) { return null; } else { try { return Float.valueOf(fstop); } catch (NumberFormatException nfe) { throw new ParseException("Invalid fstop field " + fstop, nfe); } } } /** * Set the fstop value used. */ public void setApetureFNumber(Float fstop) { setExifTagValue("fstop", fstop); } /** * @return the distance to the subject. * @throws ParseException if the value is not a valid floating point number. */ public Float getDistance() throws ParseException { String distance = getExifTagValue("distance"); if (distance == null) { return null; } else { try { return Float.valueOf(distance); } catch (NumberFormatException nfe) { throw new ParseException("Invalid distance field " + distance, nfe); } } } /** * Set the distance to the subject. */ public void setDistance(Float distance) { setExifTagValue("distance", distance); } /** * @return the time the photo was taken. * @throws ParseException if the value is not a number, represented as a long. */ public Date getTime() throws ParseException { String time = getExifTagValue("time"); if (time == null) { return null; } else { try { return new Date(Long.parseLong(time)); } catch (NumberFormatException nfe) { throw new ParseException("Invalid time field " + time, nfe); } } } /** * Set the date/time the photo was taken. */ public void setTime(Date time) { setExifTagValue("time", time == null ? null : Long.toString(time.getTime())); } /** * @return the focal length used. * @throws ParseException if the value is not a valid floating point number. */ public Float getFocalLength() throws ParseException { String focalLength = getExifTagValue("focallength"); if (focalLength == null) { return null; } else { try { return Float.valueOf(focalLength); } catch (NumberFormatException nfe) { throw new ParseException("Invalid focal length " + focalLength, nfe); } } } /** * Set the focal length used. */ public void setFocalLength(Float focalLength) { setExifTagValue("focallength", focalLength); } /** * @return {@link Boolean#TRUE} if the flash was used. */ public Boolean getFlashUsed() { String flash = getExifTagValue("flash"); return flash == null ? null : Boolean.valueOf(flash); } /** * Set whether the flash was used. */ public void setFlashUsed(Boolean flash) { setExifTagValue("flash", flash == null ? null : flash.toString()); } /** * @return the unique image id for the photo. */ public String getImageUniqueID() { return getExifTagValue("imageUniqueID"); } /** * Set the unique image id for the photo. */ public void setImageUniqueID(String imageUniqueID) { setExifTagValue("imageUniqueID", imageUniqueID); } /* * Generate the xml for this element. This is hacked to support including * arbitrary exif fields as nested elements. */ @Override public void generate(XmlWriter w, ExtensionProfile extProfile) throws IOException { w.startElement(Namespaces.EXIF_NAMESPACE, "tags", null, null); Collection<ExifTag> fields = getExifTags(); for (ExifTag field : fields) { field.generate(w, extProfile); } w.endElement(); } /* * Get a handler for parsing this element. */ @Override public ElementHandler getHandler(final ExtensionProfile extProfile, String namespace, String localName, Attributes attrs) { return new XmlParser.ElementHandler() { @Override public XmlParser.ElementHandler getChildHandler(String namespace, String localName, Attributes attrs) throws ParseException, IOException { if (Namespaces.EXIF.equals(namespace)) { ExifTag field = new ExifTag(localName); addRepeatingExtension(field); return field.getHandler(extProfile, namespace, localName, attrs); } return getExtensionHandler(extProfile, ExifTags.class, namespace, localName, attrs); } }; } }