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);
}
}
}