/* * This is public domain software - that is, you can do whatever you want * with it, and include it software that is licensed under the GNU or the * BSD license, or whatever other licence you choose, including proprietary * closed source licenses. I do ask that you leave this header in tact. * * If you make modifications to this code that you think would benefit the * wider community, please send me a copy and I'll post it on my site. * * If you make use of this code, I'd appreciate hearing about it. * drew@drewnoakes.com * Latest version of this software kept at * http://drewnoakes.com/ * * Created by dnoakes on 25-Nov-2002 20:30:39 using IntelliJ IDEA. */ package com.drew.metadata; import com.drew.lang.Rational; import java.io.Serializable; import java.lang.reflect.Array; import java.text.DateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * Base class for all Metadata directory types with supporting methods for setting and * getting tag values. */ public abstract class Directory implements Serializable { /** * Map of values hashed by type identifiers. */ protected final HashMap _tagMap; /** * The descriptor used to interperet tag values. */ protected TagDescriptor _descriptor; /** * A convenient list holding tag values in the order in which they were stored. * This is used for creation of an iterator, and for counting the number of * defined tags. */ protected final List _definedTagList; private List _errorList; // ABSTRACT METHODS /** * Provides the name of the directory, for display purposes. E.g. Exif * @return the name of the directory */ public abstract String getName(); /** * Provides the map of tag names, hashed by tag type identifier. * @return the map of tag names */ protected abstract HashMap getTagNameMap(); // CONSTRUCTORS /** * Creates a new Directory. */ public Directory() { _tagMap = new HashMap(); _definedTagList = new ArrayList(); } // VARIOUS METHODS /** * Indicates whether the specified tag type has been set. * @param tagType the tag type to check for * @return true if a value exists for the specified tag type, false if not */ public boolean containsTag(int tagType) { return _tagMap.containsKey(new Integer(tagType)); } /** * Returns an Iterator of Tag instances that have been set in this Directory. * @return an Iterator of Tag instances */ public Iterator getTagIterator() { return _definedTagList.iterator(); } /** * Returns the number of tags set in this Directory. * @return the number of tags set in this Directory */ public int getTagCount() { return _definedTagList.size(); } /** * Sets the descriptor used to interperet tag values. * @param descriptor the descriptor used to interperet tag values */ public void setDescriptor(TagDescriptor descriptor) { if (descriptor==null) { throw new NullPointerException("cannot set a null descriptor"); } _descriptor = descriptor; } public void addError(String message) { if (_errorList==null) { _errorList = new ArrayList(); } _errorList.add(message); } public boolean hasErrors() { return (_errorList!=null && _errorList.size()>0); } public Iterator getErrors() { return _errorList.iterator(); } public int getErrorCount() { return _errorList.size(); } // TAG SETTERS /** * Sets an int value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as an int */ public void setInt(int tagType, int value) { setObject(tagType, new Integer(value)); } /** * Sets a double value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a double */ public void setDouble(int tagType, double value) { setObject(tagType, new Double(value)); } /** * Sets a float value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a float */ public void setFloat(int tagType, float value) { setObject(tagType, new Float(value)); } /** * Sets an int value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a String */ public void setString(int tagType, String value) { setObject(tagType, value); } /** * Sets an int value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a boolean */ public void setBoolean(int tagType, boolean value) { setObject(tagType, new Boolean(value)); } /** * Sets a long value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a long */ public void setLong(int tagType, long value) { setObject(tagType, new Long(value)); } /** * Sets a java.util.Date value for the specified tag. * @param tagType the tag's value as an int * @param value the value for the specified tag as a java.util.Date */ public void setDate(int tagType, java.util.Date value) { setObject(tagType, value); } /** * Sets a Rational value for the specified tag. * @param tagType the tag's value as an int * @param rational rational number */ public void setRational(int tagType, Rational rational) { setObject(tagType, rational); } /** * Sets a Rational array for the specified tag. * @param tagType the tag identifier * @param rationals the Rational array to store */ public void setRationalArray(int tagType, Rational[] rationals) { setObjectArray(tagType, rationals); } /** * Sets an int array for the specified tag. * @param tagType the tag identifier * @param ints the int array to store */ public void setIntArray(int tagType, int[] ints) { setObjectArray(tagType, ints); } /** * Sets a byte array for the specified tag. * @param tagType the tag identifier * @param bytes the byte array to store */ public void setByteArray(int tagType, byte[] bytes) { setObjectArray(tagType, bytes); } /** * Sets a String array for the specified tag. * @param tagType the tag identifier * @param strings the String array to store */ public void setStringArray(int tagType, String[] strings) { setObjectArray(tagType, strings); } /** * Private helper method, containing common functionality for all 'add' * methods. * @param tagType the tag's value as an int * @param value the value for the specified tag * @throws NullPointerException if value is null */ public void setObject(int tagType, Object value) { if (value==null) { throw new NullPointerException("cannot set a null object"); } Integer key = new Integer(tagType); if (!_tagMap.containsKey(key)) { _definedTagList.add(new Tag(tagType, this)); } _tagMap.put(key, value); } /** * Private helper method, containing common functionality for all 'add...Array' * methods. * @param tagType the tag's value as an int * @param array the array of values for the specified tag */ public void setObjectArray(int tagType, Object array) { // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles setObject(tagType, array); } // TAG GETTERS /** * Returns the specified tag's value as an int, if possible. */ public int getInt(int tagType) throws MetadataException { Object o = getObject(tagType); if (o==null) { throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o instanceof String) { try { return Integer.parseInt((String)o); } catch (NumberFormatException nfe) { // convert the char array to an int String s = (String)o; byte[] bytes = s.getBytes(); long val = 0; for (int i = 0; i < bytes.length; i++) { val = val << 8; val += bytes[i]; } return (int)val; } } else if (o instanceof Number) { return ((Number)o).intValue(); } else if (o instanceof Rational[]) { Rational[] rationals = (Rational[])o; if (rationals.length==1) return rationals[0].intValue(); } else if (o instanceof byte[]) { byte[] bytes = (byte[])o; if (bytes.length==1) return bytes[0]; } else if (o instanceof int[]) { int[] ints = (int[])o; if (ints.length==1) return ints[0]; } throw new MetadataException("Tag '" + tagType + "' cannot be cast to int. It is of type '" + o.getClass() + "'."); } // TODO get Array methods need to return cloned data, to maintain this directory's integrity /** * Gets the specified tag's value as a String array, if possible. Only supported * where the tag is set as String[], String, int[], byte[] or Rational[]. * @param tagType the tag identifier * @return the tag's value as an array of Strings * @throws MetadataException if the tag has not been set or cannot be represented * as a String[] */ public String[] getStringArray(int tagType) throws MetadataException { Object o = getObject(tagType); if (o==null) { throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o instanceof String[]) { return (String[])o; } else if (o instanceof String) { String[] strings = {(String)o}; return strings; } else if (o instanceof int[]) { int[] ints = (int[])o; String[] strings = new String[ints.length]; for (int i = 0; inull if the tag hasn't been defined. */ public String getString(int tagType) { Object o = getObject(tagType); if (o==null) return null; if (o instanceof Rational) return ((Rational)o).toSimpleString(true); if (o.getClass().isArray()) { // handle arrays of objects and primitives int arrayLength = Array.getLength(o); // determine if this is an array of objects i.e. [Lcom.drew.blah boolean isObjectArray = o.getClass().toString().startsWith("class [L"); StringBuffer sbuffer = new StringBuffer(); for (int i = 0; isetDescriptor(Descriptor). * @param tagType the tag type identifier * @return the tag value's description as a String * @throws MetadataException if a descriptor hasn't been set, or if an error * occurs during calculation of the description within the Descriptor */ public String getDescription(int tagType) throws MetadataException { if (_descriptor==null) { throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided"); } return _descriptor.getDescription(tagType); } }