using System; using System.IO; using System.Text; using System.Collections; using com.drew.lang; using System.Globalization; using com.drew.metadata.iptc; /// /// This class was first written by Drew Noakes in Java. /// /// 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, Drew Noakes will appreciate hearing /// about it: drew@drewnoakes.com /// /// Latest Java version of this software kept at /// http://drewnoakes.com/ /// /// The C# class was made by Ferret Renaud: /// renaud91@free.fr /// If you find a bug in the C# code, feel free to mail me. /// namespace com.drew.metadata { /// /// Base class for all Metadata directory types with supporting /// methods for setting and getting tag values. /// [Serializable] public abstract class Directory { /// /// Map of values hashed by type identifiers. /// protected IDictionary _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 IList _definedTagList; private IList _errorList; /// /// Provides the name of the directory, for display purposes. E.g. Exif /// /// the name of the directory public abstract string GetName(); /// /// Provides the map of tag names, hashed by tag type identifier. /// /// the map of tag names protected abstract IDictionary GetTagNameMap(); /// /// Creates a new Directory. /// public Directory() : base() { // Create a Hashtable using a case-insensitive code provider and a case-insensitive comparer, // based on the Spanish culture (es-AR), where "I" is not the uppercase version of "i". CultureInfo myCul = new CultureInfo( "es-AR" ); _tagMap = new Hashtable( new CaseInsensitiveHashCodeProvider( myCul ), new CaseInsensitiveComparer( myCul )); //_tagMap = new Hashtable(); _definedTagList = new ArrayList(); _errorList = new ArrayList(0); } /// /// Indicates whether the specified tag type has been set. /// /// the tag type to check for /// true if a value exists for the specified tag type, false if not public bool ContainsTag(int tagType) { return _tagMap.Contains(tagType); } /// /// Returns an Iterator of Tag instances that have been set in this Directory. /// /// an Iterator of Tag instances public IEnumerator GetTagIterator() { return _definedTagList.GetEnumerator(); } /// /// Returns the number of tags set in this Directory. /// /// the number of tags set in this Directory public int GetTagCount() { return _definedTagList.Count; } /// /// Sets the descriptor used to interperet tag values. /// /// the descriptor used to interperet tag values /// if aDescriptor is null public void SetDescriptor(TagDescriptor aDescriptor) { if (aDescriptor == null) { throw new NullReferenceException("cannot set a null descriptor"); } _descriptor = aDescriptor; } /// /// Adds an error /// /// the error message public void AddError(string message) { _errorList.Add(message); } /// /// Checks if there is error /// /// true if there is error, false otherwise public bool HasErrors() { return (_errorList.Count > 0); } /// /// Gets an enumerator upon errors /// /// en enumerator for errors public IEnumerator GetErrors() { return _errorList.GetEnumerator(); } /// /// Gives the number of errors /// /// the number of errors public int GetErrorCount() { return _errorList.Count; } /// /// Sets an int array for the specified tag. /// /// the tag identifier /// the int array to store public virtual void SetIntArray(int tagType, int[] ints) { SetObject(tagType, ints); } /// /// Helper method, containing common functionality for all 'add' methods. /// /// the tag's value as an int /// the value for the specified tag /// if aValue is null public void SetObject(int tagType, object aValue) { if (aValue == null) { throw new NullReferenceException("cannot set a null object"); } if (!_tagMap.Contains(tagType)) { _tagMap.Add(tagType, aValue); _definedTagList.Add(new Tag(tagType, this)); } else { // We remove it and re-add it with the new value _tagMap.Remove(tagType); _tagMap.Add(tagType, aValue); } } /// /// Returns the specified tag's value as an int, if possible. /// /// the specified tag type /// the specified tag's value as an int, if possible. /// if tag not found public int GetInt(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is string) { try { return Convert.ToInt32((string) o); } catch (FormatException ) { // convert the char array to an int string s = (string) o; int val = 0; for (int i = s.Length - 1; i >= 0; i--) { val += s[i] << (i * 8); } return val; } } else if (o is Rational) { return ((Rational)o).IntValue(); } return (int)o; } /// /// 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[]. /// /// the tag identifier /// the tag's value as an array of Strings /// if tag not found or if it cannot be represented as a string[] public string[] GetStringArray(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is string[]) { return (string[]) o; } else if (o is string) { string[] strings = {(string) o }; return strings; } else if (o is int[]) { int[] ints = (int[]) o; string[] strings = new string[ints.Length]; for (int i = 0; i < strings.Length; i++) { strings[i] = ints[i].ToString(); } return strings; } else if (o is byte[]) { byte[] bytes = (byte[]) o; string[] strings = new string[bytes.Length]; for (int i = 0; i < strings.Length; i++) { strings[i] = bytes[i].ToString(); } return strings; } else if (o is 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( "Requested tag cannot be cast to string array (" + o.GetType().ToString() + ")"); } /// /// 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[]. /// /// the tag identifier /// the tag's value as an int array /// if tag not found or if it cannot be represented as a int[] public int[] GetIntArray(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is 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 is int[]) { return (int[]) o; } else if (o is 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 is string) { string str = (string) o; int[] ints = new int[str.Length]; for (int i = 0; i < str.Length; i++) { ints[i] = str[i]; } return ints; } throw new MetadataException( "Requested tag cannot be cast to int array (" + o.GetType().ToString() + ")"); } /// /// 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[]. /// /// the tag identifier /// the tag's value as a byte array /// if tag not found or if it cannot be represented as a byte[] public byte[] GetByteArray(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is 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 is byte[]) { return (byte[]) o; } else if (o is 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 is string) { string str = (string) o; byte[] bytes = new byte[str.Length]; for (int i = 0; i < str.Length; i++) { bytes[i] = (byte) str[i]; } return bytes; } throw new MetadataException( "Requested tag cannot be cast to byte array (" + o.GetType().ToString() + ")"); } /// /// Returns the specified tag's value as a double, if possible. /// /// the specified tag type /// the specified tag's value as a double, if possible. public double GetDouble(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is string) { try { return Convert.ToDouble((string) o); } catch (FormatException nfe) { throw new MetadataException( "unable to parse string " + o + " as a double", nfe); } } else if (o is Rational) { return ((Rational)o).DoubleValue(); } return (double)o; } /// /// Returns the specified tag's value as a float, if possible. /// /// the specified tag type /// the specified tag's value as a float, if possible. public float GetFloat(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is string) { try { return (float)Convert.ToDouble((string) o); } catch (FormatException nfe) { throw new MetadataException( "unable to parse string " + o + " as a float", nfe); } } else if (o is Rational) { return ((Rational)o).FloatValue(); } return (float)o; } /// /// Returns the specified tag's value as a long, if possible. /// /// the specified tag type /// the specified tag's value as a long, if possible. public long GetLong(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is string) { try { return Convert.ToInt64((string) o); } catch (FormatException nfe) { throw new MetadataException( "unable to parse string " + o + " as a long", nfe); } } else if (o is Rational) { return ((Rational)o).LongValue(); } return (long)o; } /// /// Returns the specified tag's value as a boolean, if possible. /// /// the specified tag type /// the specified tag's value as a boolean, if possible. public bool GetBoolean(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is Boolean) { return ((Boolean) o); } else if (o is string) { try { return Convert.ToBoolean((string) o); } catch (FormatException nfe) { throw new MetadataException( "unable to parse string " + o + " as a bool", nfe); } } return (bool) o; } /// /// Returns the specified tag's value as a date, if possible. /// /// the specified tag type /// the specified tag's value as a date, if possible. public DateTime GetDate(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is DateTime) { return (DateTime) o; } else if (o is string) { string dateString = (string) o; try { return DateTime.Parse(dateString); } catch (FormatException ex) { Console.Error.WriteLine(ex.StackTrace); } } throw new MetadataException("Requested tag cannot be cast to java.util.Date"); } /// /// Returns the specified tag's value as a rational, if possible. /// /// the specified tag type /// the specified tag's value as a rational, if possible. public Rational GetRational(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is Rational) { return (Rational) o; } throw new MetadataException("Requested tag cannot be cast to Rational"); } /// /// Gets the specified tag's value as a rational array, if possible. Only supported where the tag is set as Rational[]. /// /// the tag identifier /// the tag's value as a rational array /// if tag not found or if it cannot be represented as a rational[] public Rational[] GetRationalArray(int tagType) { object o = GetObject(tagType); if (o == null) { throw new MetadataException( "Tag " + GetTagName(tagType) + " has not been set -- check using containsTag() first"); } else if (o is Rational[]) { return (Rational[]) o; } throw new MetadataException( "Requested tag cannot be cast to Rational array (" + o.GetType().ToString() + ")"); } /// /// 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. /// /// the specified tag type /// the string reprensentation of the tag's value, or null if the tag hasn't been defined. public string GetString(int tagType) { object o = GetObject(tagType); if (o == null) { return null; } else if (o is Rational) { return ((Rational) o).ToSimpleString(true); } else if (o.GetType().IsArray) { string s = o.GetType().ToString(); int arrayLength = 0; if (s.IndexOf("Int")!=-1) { // handle arrays of objects and primitives arrayLength = ((int[])o).Length; } else if (s.IndexOf("Rational")!=-1) { arrayLength = ((Rational[])o).Length; } else if (s.IndexOf("string")!=-1 || s.IndexOf("String")!=-1) { arrayLength = ((string[])o).Length; } StringBuilder sbuffer = new StringBuilder(); for (int i = 0; i < arrayLength; i++) { if (i != 0) { sbuffer.Append(' '); } if (s.IndexOf("Int")!=-1) { sbuffer.Append(((int[])o)[i].ToString()); } else if (s.IndexOf("Rational")!=-1) { sbuffer.Append(((Rational[])o)[i].ToString()); } else if (s.IndexOf("string")!=-1 || s.IndexOf("String")!=-1) { sbuffer.Append(((string[])o)[i].ToString()); } } return sbuffer.ToString(); } else { return o.ToString(); } } /// /// Returns the object hashed for the particular tag type specified, if available. /// /// the tag type identifier /// the tag's value as an object if available, else null public object GetObject(int tagType) { return _tagMap[tagType]; } /// /// Returns the name of a specified tag as a string. /// /// the tag type identifier /// the tag's name as a string public string GetTagName(int tagType) { IDictionary nameMap = GetTagNameMap(); if (!nameMap.Contains(tagType)) { string hex = tagType.ToString("X"); while (hex.Length < 4) { hex = "0" + hex; } return "Unknown tag (0x" + hex + ")"; } return (string) nameMap[tagType]; } /// /// Provides a description of a tag's value using the descriptor set by setDescriptor(Descriptor). /// /// the tag type identifier /// the tag value's description as a string /// 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) { if (_descriptor == null) { throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided"); } return _descriptor.GetDescription(tagType); } } }