0,0 → 1,689 |
/* |
* 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. <code>Exif</code> |
* @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 <code>null</code> |
*/ |
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; i<strings.length; i++) { |
strings[i] = Integer.toString(ints[i]); |
} |
return strings; |
} else if (o instanceof byte[]) { |
byte[] bytes = (byte[])o; |
String[] strings = new String[bytes.length]; |
for (int i = 0; i<strings.length; i++) { |
strings[i] = Byte.toString(bytes[i]); |
} |
return strings; |
} else if (o instanceof Rational[]) { |
Rational[] rationals = (Rational[])o; |
String[] strings = new String[rationals.length]; |
for (int i = 0; i<strings.length; i++) { |
strings[i] = rationals[i].toSimpleString(false); |
} |
return strings; |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to an String array. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Gets the specified tag's value as an int array, if possible. Only supported |
* where the tag is set as String, int[], byte[] or Rational[]. |
* @param tagType the tag identifier |
* @return the tag's value as an int array |
* @throws MetadataException if the tag has not been set, or cannot be converted to |
* an int array |
*/ |
public int[] getIntArray(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 Rational[]) { |
Rational[] rationals = (Rational[])o; |
int[] ints = new int[rationals.length]; |
for (int i = 0; i<ints.length; i++) { |
ints[i] = rationals[i].intValue(); |
} |
return ints; |
} else if (o instanceof int[]) { |
return (int[])o; |
} else if (o instanceof byte[]) { |
byte[] bytes = (byte[])o; |
int[] ints = new int[bytes.length]; |
for (int i = 0; i<bytes.length; i++) { |
byte b = bytes[i]; |
ints[i] = b; |
} |
return ints; |
} else if (o instanceof String) { |
String str = (String)o; |
int[] ints = new int[str.length()]; |
for (int i = 0; i<str.length(); i++) { |
ints[i] = str.charAt(i); |
} |
return ints; |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to an int array. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Gets the specified tag's value as an byte array, if possible. Only supported |
* where the tag is set as String, int[], byte[] or Rational[]. |
* @param tagType the tag identifier |
* @return the tag's value as a byte array |
* @throws MetadataException if the tag has not been set, or cannot be converted to |
* a byte array |
*/ |
public byte[] getByteArray(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 Rational[]) { |
Rational[] rationals = (Rational[])o; |
byte[] bytes = new byte[rationals.length]; |
for (int i = 0; i<bytes.length; i++) { |
bytes[i] = rationals[i].byteValue(); |
} |
return bytes; |
} else if (o instanceof byte[]) { |
return (byte[])o; |
} else if (o instanceof int[]) { |
int[] ints = (int[])o; |
byte[] bytes = new byte[ints.length]; |
for (int i = 0; i<ints.length; i++) { |
bytes[i] = (byte)ints[i]; |
} |
return bytes; |
} else if (o instanceof String) { |
String str = (String)o; |
byte[] bytes = new byte[str.length()]; |
for (int i = 0; i<str.length(); i++) { |
bytes[i] = (byte)str.charAt(i); |
} |
return bytes; |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a byte array. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a double, if possible. |
*/ |
public double getDouble(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 Double.parseDouble((String)o); |
} catch (NumberFormatException nfe) { |
throw new MetadataException("unable to parse string " + o + " as a double", nfe); |
} |
} else if (o instanceof Number) { |
return ((Number)o).doubleValue(); |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a double. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a float, if possible. |
*/ |
public float getFloat(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 Float.parseFloat((String)o); |
} catch (NumberFormatException nfe) { |
throw new MetadataException("unable to parse string " + o + " as a float", nfe); |
} |
} else if (o instanceof Number) { |
return ((Number)o).floatValue(); |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a float. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a long, if possible. |
*/ |
public long getLong(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 Long.parseLong((String)o); |
} catch (NumberFormatException nfe) { |
throw new MetadataException("unable to parse string " + o + " as a long", nfe); |
} |
} else if (o instanceof Number) { |
return ((Number)o).longValue(); |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a long. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a boolean, if possible. |
*/ |
public boolean getBoolean(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 Boolean) { |
return ((Boolean)o).booleanValue(); |
} else if (o instanceof String) { |
try { |
return Boolean.getBoolean((String)o); |
} catch (NumberFormatException nfe) { |
throw new MetadataException("unable to parse string " + o + " as a boolean", nfe); |
} |
} else if (o instanceof Number) { |
return (((Number)o).doubleValue()!=0); |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a java.util.Date, if possible. |
*/ |
public java.util.Date getDate(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 java.util.Date) { |
return (java.util.Date)o; |
} else if (o instanceof String) { |
// add new dateformat strings to make this method even smarter |
// so far, this seems to cover all known date strings |
// (for example, AM and PM strings are not supported...) |
String datePatterns[] = { |
"yyyy:MM:dd HH:mm:ss", |
"yyyy:MM:dd HH:mm", |
"yyyy-MM-dd HH:mm:ss", |
"yyyy-MM-dd HH:mm"}; |
String dateString = (String)o; |
for (int i = 0; i<datePatterns.length; i++) { |
try { |
DateFormat parser = new java.text.SimpleDateFormat(datePatterns[i]); |
return parser.parse(dateString); |
} catch (java.text.ParseException ex) { |
// simply try the next pattern |
} |
} |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a java.util.Date. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a Rational, if possible. |
*/ |
public Rational getRational(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 Rational) { |
return (Rational)o; |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational. It is of type '" + o.getClass() + "'."); |
} |
|
public Rational[] getRationalArray(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 Rational[]) { |
return (Rational[])o; |
} |
throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational array. It is of type '" + o.getClass() + "'."); |
} |
|
/** |
* Returns the specified tag's value as a String. This value is the 'raw' value. A more presentable decoding |
* of this value may be obtained from the corresponding Descriptor. |
* @return the String reprensentation of the tag's value, or |
* <code>null</code> 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; i<arrayLength; i++) |
{ |
if (i!=0) |
sbuffer.append(' '); |
if (isObjectArray) |
sbuffer.append(Array.get(o, i).toString()); |
else |
sbuffer.append(Array.getInt(o, i)); |
} |
return sbuffer.toString(); |
} |
|
return o.toString(); |
} |
|
/** |
* Returns the object hashed for the particular tag type specified, if available. |
* @param tagType the tag type identifier |
* @return the tag's value as an Object if available, else null |
*/ |
public Object getObject(int tagType) |
{ |
return _tagMap.get(new Integer(tagType)); |
} |
|
// OTHER METHODS |
|
/** |
* Returns the name of a specified tag as a String. |
* @param tagType the tag type identifier |
* @return the tag's name as a String |
*/ |
public String getTagName(int tagType) |
{ |
Integer key = new Integer(tagType); |
HashMap nameMap = getTagNameMap(); |
if (!nameMap.containsKey(key)) { |
String hex = Integer.toHexString(tagType); |
while (hex.length()<4) { |
hex = "0" + hex; |
} |
return "Unknown tag (0x" + hex + ")"; |
} |
return (String)nameMap.get(key); |
} |
|
/** |
* Provides a description of a tag's value using the descriptor set by |
* <code>setDescriptor(Descriptor)</code>. |
* @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); |
} |
} |