Ignore:
Timestamp:
2013-08-09T18:05:11+02:00 (13 years ago)
Author:
bastiK
Message:

applied #8895 - Upgrade metadata-extractor to v. 2.6.4 (patch by ebourg)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/com/drew/metadata/Directory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 25-Nov-2002 20:30:39 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata;
    1822
    1923import com.drew.lang.Rational;
    20 
    21 import java.io.Serializable;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26import com.drew.lang.annotations.SuppressWarnings;
     27
     28import java.io.UnsupportedEncodingException;
    2229import java.lang.reflect.Array;
    2330import java.text.DateFormat;
    24 import java.util.ArrayList;
    25 import java.util.HashMap;
    26 import java.util.Iterator;
    27 import java.util.List;
     31import java.text.ParseException;
     32import java.text.SimpleDateFormat;
     33import java.util.*;
    2834
    2935/**
    30  * Base class for all Metadata directory types with supporting methods for setting and
    31  * getting tag values.
     36 * Abstract base class for all directory implementations, having methods for getting and setting tag values of various
     37 * data types.
     38 *
     39 * @author Drew Noakes http://drewnoakes.com
    3240 */
    33 public abstract class Directory implements Serializable
     41public abstract class Directory
    3442{
    35     /**
    36      * Map of values hashed by type identifiers.
    37      */
    38     protected final HashMap _tagMap;
    39 
    40     /**
    41      * The descriptor used to interperet tag values.
    42      */
    43     protected TagDescriptor _descriptor;
     43    // TODO get Array methods need to return cloned data, to maintain this directory's integrity
     44
     45    /** Map of values hashed by type identifiers. */
     46    @NotNull
     47    protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
    4448
    4549    /**
     
    4852     * defined tags.
    4953     */
    50     protected final List _definedTagList;
    51 
    52     private List _errorList;
     54    @NotNull
     55    protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
     56
     57    @NotNull
     58    private final Collection<String> _errorList = new ArrayList<String>(4);
     59
     60    /** The descriptor used to interpret tag values. */
     61    protected TagDescriptor _descriptor;
    5362
    5463// ABSTRACT METHODS
     
    5665    /**
    5766     * Provides the name of the directory, for display purposes.  E.g. <code>Exif</code>
     67     *
    5868     * @return the name of the directory
    5969     */
     70    @NotNull
    6071    public abstract String getName();
    6172
    6273    /**
    6374     * Provides the map of tag names, hashed by tag type identifier.
     75     *
    6476     * @return the map of tag names
    6577     */
    66     protected abstract HashMap getTagNameMap();
    67 
    68 // CONSTRUCTORS
    69 
    70     /**
    71      * Creates a new Directory.
    72      */
    73     public Directory()
    74     {
    75         _tagMap = new HashMap();
    76         _definedTagList = new ArrayList();
    77     }
     78    @NotNull
     79    protected abstract HashMap<Integer, String> getTagNameMap();
     80
     81    protected Directory()
     82    {}
    7883
    7984// VARIOUS METHODS
     
    8186    /**
    8287     * Indicates whether the specified tag type has been set.
     88     *
    8389     * @param tagType the tag type to check for
    8490     * @return true if a value exists for the specified tag type, false if not
    8591     */
     92    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
    8693    public boolean containsTag(int tagType)
    8794    {
    88         return _tagMap.containsKey(new Integer(tagType));
     95        return _tagMap.containsKey(Integer.valueOf(tagType));
    8996    }
    9097
    9198    /**
    9299     * Returns an Iterator of Tag instances that have been set in this Directory.
     100     *
    93101     * @return an Iterator of Tag instances
    94102     */
    95     public Iterator getTagIterator()
    96     {
    97         return _definedTagList.iterator();
     103    @NotNull
     104    public Collection<Tag> getTags()
     105    {
     106        return _definedTagList;
    98107    }
    99108
    100109    /**
    101110     * Returns the number of tags set in this Directory.
     111     *
    102112     * @return the number of tags set in this Directory
    103113     */
     
    108118
    109119    /**
    110      * Sets the descriptor used to interperet tag values.
    111      * @param descriptor the descriptor used to interperet tag values
    112      */
    113     public void setDescriptor(TagDescriptor descriptor)
    114     {
    115         if (descriptor==null) {
     120     * Sets the descriptor used to interpret tag values.
     121     *
     122     * @param descriptor the descriptor used to interpret tag values
     123     */
     124    @java.lang.SuppressWarnings({ "ConstantConditions" })
     125    public void setDescriptor(@NotNull TagDescriptor descriptor)
     126    {
     127        if (descriptor == null)
    116128            throw new NullPointerException("cannot set a null descriptor");
    117         }
    118129        _descriptor = descriptor;
    119130    }
    120131
    121     public void addError(String message)
    122     {
    123         if (_errorList==null) {
    124             _errorList = new ArrayList();
    125         }
     132    /**
     133     * Registers an error message with this directory.
     134     *
     135     * @param message an error message.
     136     */
     137    public void addError(@NotNull String message)
     138    {
    126139        _errorList.add(message);
    127140    }
    128141
     142    /**
     143     * Gets a value indicating whether this directory has any error messages.
     144     *
     145     * @return true if the directory contains errors, otherwise false
     146     */
    129147    public boolean hasErrors()
    130148    {
    131         return (_errorList!=null && _errorList.size()>0);
    132     }
    133 
    134     public Iterator getErrors()
    135     {
    136         return _errorList.iterator();
    137     }
    138 
     149        return _errorList.size() > 0;
     150    }
     151
     152    /**
     153     * Used to iterate over any error messages contained in this directory.
     154     *
     155     * @return an iterable collection of error message strings.
     156     */
     157    @NotNull
     158    public Iterable<String> getErrors()
     159    {
     160        return _errorList;
     161    }
     162
     163    /** Returns the count of error messages in this directory. */
    139164    public int getErrorCount()
    140165    {
     
    145170
    146171    /**
    147      * Sets an int value for the specified tag.
     172     * Sets an <code>int</code> value for the specified tag.
     173     *
    148174     * @param tagType the tag's value as an int
    149      * @param value the value for the specified tag as an int
     175     * @param value   the value for the specified tag as an int
    150176     */
    151177    public void setInt(int tagType, int value)
    152178    {
    153         setObject(tagType, new Integer(value));
    154     }
    155 
    156     /**
    157      * Sets a double value for the specified tag.
     179        setObject(tagType, value);
     180    }
     181
     182    /**
     183     * Sets an <code>int[]</code> (array) for the specified tag.
     184     *
     185     * @param tagType the tag identifier
     186     * @param ints    the int array to store
     187     */
     188    public void setIntArray(int tagType, @NotNull int[] ints)
     189    {
     190        setObjectArray(tagType, ints);
     191    }
     192
     193    /**
     194     * Sets a <code>float</code> value for the specified tag.
     195     *
    158196     * @param tagType the tag's value as an int
    159      * @param value the value for the specified tag as a double
     197     * @param value   the value for the specified tag as a float
     198     */
     199    public void setFloat(int tagType, float value)
     200    {
     201        setObject(tagType, value);
     202    }
     203
     204    /**
     205     * Sets a <code>float[]</code> (array) for the specified tag.
     206     *
     207     * @param tagType the tag identifier
     208     * @param floats  the float array to store
     209     */
     210    public void setFloatArray(int tagType, @NotNull float[] floats)
     211    {
     212        setObjectArray(tagType, floats);
     213    }
     214
     215    /**
     216     * Sets a <code>double</code> value for the specified tag.
     217     *
     218     * @param tagType the tag's value as an int
     219     * @param value   the value for the specified tag as a double
    160220     */
    161221    public void setDouble(int tagType, double value)
    162222    {
    163         setObject(tagType, new Double(value));
    164     }
    165 
    166     /**
    167      * Sets a float value for the specified tag.
     223        setObject(tagType, value);
     224    }
     225
     226    /**
     227     * Sets a <code>double[]</code> (array) for the specified tag.
     228     *
     229     * @param tagType the tag identifier
     230     * @param doubles the double array to store
     231     */
     232    public void setDoubleArray(int tagType, @NotNull double[] doubles)
     233    {
     234        setObjectArray(tagType, doubles);
     235    }
     236
     237    /**
     238     * Sets a <code>String</code> value for the specified tag.
     239     *
    168240     * @param tagType the tag's value as an int
    169      * @param value the value for the specified tag as a float
    170      */
    171     public void setFloat(int tagType, float value)
    172     {
    173         setObject(tagType, new Float(value));
    174     }
    175 
    176     /**
    177      * Sets an int value for the specified tag.
    178      * @param tagType the tag's value as an int
    179      * @param value the value for the specified tag as a String
    180      */
    181     public void setString(int tagType, String value)
    182     {
     241     * @param value   the value for the specified tag as a String
     242     */
     243    @java.lang.SuppressWarnings({ "ConstantConditions" })
     244    public void setString(int tagType, @NotNull String value)
     245    {
     246        if (value == null)
     247            throw new NullPointerException("cannot set a null String");
    183248        setObject(tagType, value);
    184249    }
    185250
    186251    /**
    187      * Sets an int value for the specified tag.
    188      * @param tagType the tag's value as an int
    189      * @param value the value for the specified tag as a boolean
    190      */
    191     public void setBoolean(int tagType, boolean value)
    192     {
    193         setObject(tagType, new Boolean(value));
    194     }
    195 
    196     /**
    197      * Sets a long value for the specified tag.
    198      * @param tagType the tag's value as an int
    199      * @param value the value for the specified tag as a long
    200      */
    201     public void setLong(int tagType, long value)
    202     {
    203         setObject(tagType, new Long(value));
    204     }
    205 
    206     /**
    207      * Sets a java.util.Date value for the specified tag.
    208      * @param tagType the tag's value as an int
    209      * @param value the value for the specified tag as a java.util.Date
    210      */
    211     public void setDate(int tagType, java.util.Date value)
    212     {
    213         setObject(tagType, value);
    214     }
    215 
    216     /**
    217      * Sets a Rational value for the specified tag.
    218      * @param tagType the tag's value as an int
    219      * @param rational rational number
    220      */
    221     public void setRational(int tagType, Rational rational)
    222     {
    223         setObject(tagType, rational);
    224     }
    225 
    226     /**
    227      * Sets a Rational array for the specified tag.
    228      * @param tagType the tag identifier
    229      * @param rationals the Rational array to store
    230      */
    231     public void setRationalArray(int tagType, Rational[] rationals)
    232     {
    233         setObjectArray(tagType, rationals);
    234     }
    235 
    236     /**
    237      * Sets an int array for the specified tag.
    238      * @param tagType the tag identifier
    239      * @param ints the int array to store
    240      */
    241     public void setIntArray(int tagType, int[] ints)
    242     {
    243         setObjectArray(tagType, ints);
    244     }
    245 
    246     /**
    247      * Sets a byte array for the specified tag.
    248      * @param tagType the tag identifier
    249      * @param bytes the byte array to store
    250      */
    251     public void setByteArray(int tagType, byte[] bytes)
    252     {
    253         setObjectArray(tagType, bytes);
    254     }
    255 
    256     /**
    257      * Sets a String array for the specified tag.
     252     * Sets a <code>String[]</code> (array) for the specified tag.
     253     *
    258254     * @param tagType the tag identifier
    259255     * @param strings the String array to store
    260256     */
    261     public void setStringArray(int tagType, String[] strings)
     257    public void setStringArray(int tagType, @NotNull String[] strings)
    262258    {
    263259        setObjectArray(tagType, strings);
     
    265261
    266262    /**
    267      * Private helper method, containing common functionality for all 'add'
    268      * methods.
     263     * Sets a <code>boolean</code> value for the specified tag.
     264     *
    269265     * @param tagType the tag's value as an int
    270      * @param value the value for the specified tag
     266     * @param value   the value for the specified tag as a boolean
     267     */
     268    public void setBoolean(int tagType, boolean value)
     269    {
     270        setObject(tagType, value);
     271    }
     272
     273    /**
     274     * Sets a <code>long</code> value for the specified tag.
     275     *
     276     * @param tagType the tag's value as an int
     277     * @param value   the value for the specified tag as a long
     278     */
     279    public void setLong(int tagType, long value)
     280    {
     281        setObject(tagType, value);
     282    }
     283
     284    /**
     285     * Sets a <code>java.util.Date</code> value for the specified tag.
     286     *
     287     * @param tagType the tag's value as an int
     288     * @param value   the value for the specified tag as a java.util.Date
     289     */
     290    public void setDate(int tagType, @NotNull java.util.Date value)
     291    {
     292        setObject(tagType, value);
     293    }
     294
     295    /**
     296     * Sets a <code>Rational</code> value for the specified tag.
     297     *
     298     * @param tagType  the tag's value as an int
     299     * @param rational rational number
     300     */
     301    public void setRational(int tagType, @NotNull Rational rational)
     302    {
     303        setObject(tagType, rational);
     304    }
     305
     306    /**
     307     * Sets a <code>Rational[]</code> (array) for the specified tag.
     308     *
     309     * @param tagType   the tag identifier
     310     * @param rationals the Rational array to store
     311     */
     312    public void setRationalArray(int tagType, @NotNull Rational[] rationals)
     313    {
     314        setObjectArray(tagType, rationals);
     315    }
     316
     317    /**
     318     * Sets a <code>byte[]</code> (array) for the specified tag.
     319     *
     320     * @param tagType the tag identifier
     321     * @param bytes   the byte array to store
     322     */
     323    public void setByteArray(int tagType, @NotNull byte[] bytes)
     324    {
     325        setObjectArray(tagType, bytes);
     326    }
     327
     328    /**
     329     * Sets a <code>Object</code> for the specified tag.
     330     *
     331     * @param tagType the tag's value as an int
     332     * @param value   the value for the specified tag
    271333     * @throws NullPointerException if value is <code>null</code>
    272334     */
    273     public void setObject(int tagType, Object value)
    274     {
    275         if (value==null) {
     335    @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
     336    public void setObject(int tagType, @NotNull Object value)
     337    {
     338        if (value == null)
    276339            throw new NullPointerException("cannot set a null object");
    277         }
    278 
    279         Integer key = new Integer(tagType);
    280         if (!_tagMap.containsKey(key)) {
     340
     341        if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
    281342            _definedTagList.add(new Tag(tagType, this));
    282343        }
    283         _tagMap.put(key, value);
    284     }
    285 
    286     /**
    287      * Private helper method, containing common functionality for all 'add...Array'
    288      * methods.
     344//        else {
     345//            final Object oldValue = _tagMap.get(tagType);
     346//            if (!oldValue.equals(value))
     347//                addError(String.format("Overwritten tag 0x%s (%s).  Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
     348//        }
     349        _tagMap.put(tagType, value);
     350    }
     351
     352    /**
     353     * Sets an array <code>Object</code> for the specified tag.
     354     *
    289355     * @param tagType the tag's value as an int
    290      * @param array the array of values for the specified tag
    291      */
    292     public void setObjectArray(int tagType, Object array)
     356     * @param array   the array of values for the specified tag
     357     */
     358    public void setObjectArray(int tagType, @NotNull Object array)
    293359    {
    294360        // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
     
    299365
    300366    /**
    301      * Returns the specified tag's value as an int, if possible.
     367     * Returns the specified tag's value as an int, if possible.  Every attempt to represent the tag's value as an int
     368     * is taken.  Here is a list of the action taken depending upon the tag's original type:
     369     * <ul>
     370     * <li> int - Return unchanged.
     371     * <li> Number - Return an int value (real numbers are truncated).
     372     * <li> Rational - Truncate any fractional part and returns remaining int.
     373     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR).
     374     * <li> Rational[] - Return int value of first item in array.
     375     * <li> byte[] - Return int value of first item in array.
     376     * <li> int[] - Return int value of first item in array.
     377     * </ul>
     378     *
     379     * @throws MetadataException if no value exists for tagType or if it cannot be converted to an int.
    302380     */
    303381    public int getInt(int tagType) throws MetadataException
    304382    {
    305         Object o = getObject(tagType);
    306         if (o==null) {
    307             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    308         } else if (o instanceof String) {
     383        Integer integer = getInteger(tagType);
     384        if (integer!=null)
     385            return integer;
     386
     387        Object o = getObject(tagType);
     388        if (o == null)
     389            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     390        throw new MetadataException("Tag '" + tagType + "' cannot be converted to int.  It is of type '" + o.getClass() + "'.");
     391    }
     392
     393    /**
     394     * Returns the specified tag's value as an Integer, if possible.  Every attempt to represent the tag's value as an
     395     * Integer is taken.  Here is a list of the action taken depending upon the tag's original type:
     396     * <ul>
     397     * <li> int - Return unchanged
     398     * <li> Number - Return an int value (real numbers are truncated)
     399     * <li> Rational - Truncate any fractional part and returns remaining int
     400     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR)
     401     * <li> Rational[] - Return int value of first item in array if length &gt; 0
     402     * <li> byte[] - Return int value of first item in array if length &gt; 0
     403     * <li> int[] - Return int value of first item in array if length &gt; 0
     404     * </ul>
     405     *
     406     * If the value is not found or cannot be converted to int, <code>null</code> is returned.
     407     */
     408    @Nullable
     409    public Integer getInteger(int tagType)
     410    {
     411        Object o = getObject(tagType);
     412
     413        if (o == null)
     414            return null;
     415
     416        if (o instanceof String) {
    309417            try {
    310418                return Integer.parseInt((String)o);
     
    314422                byte[] bytes = s.getBytes();
    315423                long val = 0;
    316                 for (int i = 0; i < bytes.length; i++) {
     424                for (byte aByte : bytes) {
    317425                    val = val << 8;
    318                     val += bytes[i];
     426                    val += (aByte & 0xff);
    319427                }
    320428                return (int)val;
     
    324432        } else if (o instanceof Rational[]) {
    325433            Rational[] rationals = (Rational[])o;
    326             if (rationals.length==1)
     434            if (rationals.length == 1)
    327435                return rationals[0].intValue();
    328436        } else if (o instanceof byte[]) {
    329437            byte[] bytes = (byte[])o;
    330             if (bytes.length==1)
    331                 return bytes[0];
     438            if (bytes.length == 1)
     439                return (int)bytes[0];
    332440        } else if (o instanceof int[]) {
    333441            int[] ints = (int[])o;
    334             if (ints.length==1)
     442            if (ints.length == 1)
    335443                return ints[0];
    336444        }
    337         throw new MetadataException("Tag '" + tagType + "' cannot be cast to int.  It is of type '" + o.getClass() + "'.");
    338     }
    339 
    340     // TODO get Array methods need to return cloned data, to maintain this directory's integrity
     445        return null;
     446    }
    341447
    342448    /**
    343449     * Gets the specified tag's value as a String array, if possible.  Only supported
    344450     * where the tag is set as String[], String, int[], byte[] or Rational[].
     451     *
    345452     * @param tagType the tag identifier
    346      * @return the tag's value as an array of Strings
    347      * @throws MetadataException if the tag has not been set or cannot be represented
    348      *         as a String[]
    349      */
    350     public String[] getStringArray(int tagType) throws MetadataException
    351     {
    352         Object o = getObject(tagType);
    353         if (o==null) {
    354             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    355         } else if (o instanceof String[]) {
     453     * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned.
     454     */
     455    @Nullable
     456    public String[] getStringArray(int tagType)
     457    {
     458        Object o = getObject(tagType);
     459        if (o == null)
     460            return null;
     461        if (o instanceof String[])
    356462            return (String[])o;
    357         } else if (o instanceof String) {
    358             String[] strings = {(String)o};
    359             return strings;
    360         } else if (o instanceof int[]) {
     463        if (o instanceof String)
     464            return new String[] { (String)o };
     465        if (o instanceof int[]) {
    361466            int[] ints = (int[])o;
    362467            String[] strings = new String[ints.length];
    363             for (int i = 0; i<strings.length; i++) {
     468            for (int i = 0; i < strings.length; i++)
    364469                strings[i] = Integer.toString(ints[i]);
    365             }
    366470            return strings;
    367471        } else if (o instanceof byte[]) {
    368472            byte[] bytes = (byte[])o;
    369473            String[] strings = new String[bytes.length];
    370             for (int i = 0; i<strings.length; i++) {
     474            for (int i = 0; i < strings.length; i++)
    371475                strings[i] = Byte.toString(bytes[i]);
    372             }
    373476            return strings;
    374477        } else if (o instanceof Rational[]) {
    375478            Rational[] rationals = (Rational[])o;
    376479            String[] strings = new String[rationals.length];
    377             for (int i = 0; i<strings.length; i++) {
     480            for (int i = 0; i < strings.length; i++)
    378481                strings[i] = rationals[i].toSimpleString(false);
    379             }
    380482            return strings;
    381483        }
    382         throw new MetadataException("Tag '" + tagType + "' cannot be cast to an String array.  It is of type '" + o.getClass() + "'.");
     484        return null;
    383485    }
    384486
    385487    /**
    386488     * Gets the specified tag's value as an int array, if possible.  Only supported
    387      * where the tag is set as String, int[], byte[] or Rational[].
     489     * where the tag is set as String, Integer, int[], byte[] or Rational[].
     490     *
    388491     * @param tagType the tag identifier
    389492     * @return the tag's value as an int array
    390      * @throws MetadataException if the tag has not been set, or cannot be converted to
    391      *         an int array
    392      */
    393     public int[] getIntArray(int tagType) throws MetadataException
    394     {
    395         Object o = getObject(tagType);
    396         if (o==null) {
    397             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    398         } else if (o instanceof Rational[]) {
     493     */
     494    @Nullable
     495    public int[] getIntArray(int tagType)
     496    {
     497        Object o = getObject(tagType);
     498        if (o == null)
     499            return null;
     500        if (o instanceof Rational[]) {
    399501            Rational[] rationals = (Rational[])o;
    400502            int[] ints = new int[rationals.length];
    401             for (int i = 0; i<ints.length; i++) {
     503            for (int i = 0; i < ints.length; i++) {
    402504                ints[i] = rationals[i].intValue();
    403505            }
    404506            return ints;
    405         } else if (o instanceof int[]) {
     507        }
     508        if (o instanceof int[])
    406509            return (int[])o;
    407         } else if (o instanceof byte[]) {
     510        if (o instanceof byte[]) {
    408511            byte[] bytes = (byte[])o;
    409512            int[] ints = new int[bytes.length];
    410             for (int i = 0; i<bytes.length; i++) {
     513            for (int i = 0; i < bytes.length; i++) {
    411514                byte b = bytes[i];
    412515                ints[i] = b;
    413516            }
    414517            return ints;
    415         } else if (o instanceof String) {
    416             String str = (String)o;
     518        }
     519        if (o instanceof CharSequence) {
     520            CharSequence str = (CharSequence)o;
    417521            int[] ints = new int[str.length()];
    418             for (int i = 0; i<str.length(); i++) {
     522            for (int i = 0; i < str.length(); i++) {
    419523                ints[i] = str.charAt(i);
    420524            }
    421525            return ints;
    422526        }
    423         throw new MetadataException("Tag '" + tagType + "' cannot be cast to an int array.  It is of type '" + o.getClass() + "'.");
     527        if (o instanceof Integer)
     528            return new int[] { (Integer)o };
     529       
     530        return null;
    424531    }
    425532
    426533    /**
    427534     * Gets the specified tag's value as an byte array, if possible.  Only supported
    428      * where the tag is set as String, int[], byte[] or Rational[].
     535     * where the tag is set as String, Integer, int[], byte[] or Rational[].
     536     *
    429537     * @param tagType the tag identifier
    430538     * @return the tag's value as a byte array
    431      * @throws MetadataException if the tag has not been set, or cannot be converted to
    432      *         a byte array
    433      */
    434     public byte[] getByteArray(int tagType) throws MetadataException
    435     {
    436         Object o = getObject(tagType);
    437         if (o==null) {
    438             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
     539     */
     540    @Nullable
     541    public byte[] getByteArray(int tagType)
     542    {
     543        Object o = getObject(tagType);
     544        if (o == null) {
     545            return null;
    439546        } else if (o instanceof Rational[]) {
    440547            Rational[] rationals = (Rational[])o;
    441548            byte[] bytes = new byte[rationals.length];
    442             for (int i = 0; i<bytes.length; i++) {
     549            for (int i = 0; i < bytes.length; i++) {
    443550                bytes[i] = rationals[i].byteValue();
    444551            }
     
    449556            int[] ints = (int[])o;
    450557            byte[] bytes = new byte[ints.length];
    451             for (int i = 0; i<ints.length; i++) {
     558            for (int i = 0; i < ints.length; i++) {
    452559                bytes[i] = (byte)ints[i];
    453560            }
    454561            return bytes;
    455         } else if (o instanceof String) {
    456             String str = (String)o;
     562        } else if (o instanceof CharSequence) {
     563            CharSequence str = (CharSequence)o;
    457564            byte[] bytes = new byte[str.length()];
    458             for (int i = 0; i<str.length(); i++) {
     565            for (int i = 0; i < str.length(); i++) {
    459566                bytes[i] = (byte)str.charAt(i);
    460567            }
    461568            return bytes;
    462569        }
    463         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a byte array.  It is of type '" + o.getClass() + "'.");
    464     }
    465 
    466     /**
    467      * Returns the specified tag's value as a double, if possible.
    468      */
     570        if (o instanceof Integer)
     571            return new byte[] { ((Integer)o).byteValue() };
     572
     573        return null;
     574    }
     575
     576    /** Returns the specified tag's value as a double, if possible. */
    469577    public double getDouble(int tagType) throws MetadataException
    470578    {
    471         Object o = getObject(tagType);
    472         if (o==null) {
    473             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    474         } else if (o instanceof String) {
     579        Double value = getDoubleObject(tagType);
     580        if (value!=null)
     581            return value;
     582        Object o = getObject(tagType);
     583        if (o == null)
     584            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     585        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double.  It is of type '" + o.getClass() + "'.");
     586    }
     587    /** Returns the specified tag's value as a Double.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     588    @Nullable
     589    public Double getDoubleObject(int tagType)
     590    {
     591        Object o = getObject(tagType);
     592        if (o == null)
     593            return null;
     594        if (o instanceof String) {
    475595            try {
    476596                return Double.parseDouble((String)o);
    477597            } catch (NumberFormatException nfe) {
    478                 throw new MetadataException("unable to parse string " + o + " as a double", nfe);
    479             }
    480         } else if (o instanceof Number) {
     598                return null;
     599            }
     600        }
     601        if (o instanceof Number)
    481602            return ((Number)o).doubleValue();
    482         }
    483         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a double.  It is of type '" + o.getClass() + "'.");
    484     }
    485 
    486     /**
    487      * Returns the specified tag's value as a float, if possible.
    488      */
     603
     604        return null;
     605    }
     606
     607    /** Returns the specified tag's value as a float, if possible. */
    489608    public float getFloat(int tagType) throws MetadataException
    490609    {
    491         Object o = getObject(tagType);
    492         if (o==null) {
    493             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    494         } else if (o instanceof String) {
     610        Float value = getFloatObject(tagType);
     611        if (value!=null)
     612            return value;
     613        Object o = getObject(tagType);
     614        if (o == null)
     615            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     616        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float.  It is of type '" + o.getClass() + "'.");
     617    }
     618
     619    /** Returns the specified tag's value as a float.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     620    @Nullable
     621    public Float getFloatObject(int tagType)
     622    {
     623        Object o = getObject(tagType);
     624        if (o == null)
     625            return null;
     626        if (o instanceof String) {
    495627            try {
    496628                return Float.parseFloat((String)o);
    497629            } catch (NumberFormatException nfe) {
    498                 throw new MetadataException("unable to parse string " + o + " as a float", nfe);
    499             }
    500         } else if (o instanceof Number) {
     630                return null;
     631            }
     632        }
     633        if (o instanceof Number)
    501634            return ((Number)o).floatValue();
    502         }
    503         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a float.  It is of type '" + o.getClass() + "'.");
    504     }
    505 
    506     /**
    507      * Returns the specified tag's value as a long, if possible.
    508      */
     635        return null;
     636    }
     637
     638    /** Returns the specified tag's value as a long, if possible. */
    509639    public long getLong(int tagType) throws MetadataException
    510640    {
    511         Object o = getObject(tagType);
    512         if (o==null) {
    513             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    514         } else if (o instanceof String) {
     641        Long value = getLongObject(tagType);
     642        if (value!=null)
     643            return value;
     644        Object o = getObject(tagType);
     645        if (o == null)
     646            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     647        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long.  It is of type '" + o.getClass() + "'.");
     648    }
     649
     650    /** Returns the specified tag's value as a long.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     651    @Nullable
     652    public Long getLongObject(int tagType)
     653    {
     654        Object o = getObject(tagType);
     655        if (o == null)
     656            return null;
     657        if (o instanceof String) {
    515658            try {
    516659                return Long.parseLong((String)o);
    517660            } catch (NumberFormatException nfe) {
    518                 throw new MetadataException("unable to parse string " + o + " as a long", nfe);
    519             }
    520         } else if (o instanceof Number) {
     661                return null;
     662            }
     663        }
     664        if (o instanceof Number)
    521665            return ((Number)o).longValue();
    522         }
    523         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a long.  It is of type '" + o.getClass() + "'.");
    524     }
    525 
    526     /**
    527      * Returns the specified tag's value as a boolean, if possible.
    528      */
     666        return null;
     667    }
     668
     669    /** Returns the specified tag's value as a boolean, if possible. */
    529670    public boolean getBoolean(int tagType) throws MetadataException
    530671    {
    531         Object o = getObject(tagType);
    532         if (o==null) {
    533             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    534         } else if (o instanceof Boolean) {
    535             return ((Boolean)o).booleanValue();
    536         } else if (o instanceof String) {
     672        Boolean value = getBooleanObject(tagType);
     673        if (value!=null)
     674            return value;
     675        Object o = getObject(tagType);
     676        if (o == null)
     677            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     678        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean.  It is of type '" + o.getClass() + "'.");
     679    }
     680
     681    /** Returns the specified tag's value as a boolean.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     682    @Nullable
     683    @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
     684    public Boolean getBooleanObject(int tagType)
     685    {
     686        Object o = getObject(tagType);
     687        if (o == null)
     688            return null;
     689        if (o instanceof Boolean)
     690            return (Boolean)o;
     691        if (o instanceof String) {
    537692            try {
    538693                return Boolean.getBoolean((String)o);
    539694            } catch (NumberFormatException nfe) {
    540                 throw new MetadataException("unable to parse string " + o + " as a boolean", nfe);
    541             }
    542         } else if (o instanceof Number) {
    543             return (((Number)o).doubleValue()!=0);
    544         }
    545         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean.  It is of type '" + o.getClass() + "'.");
    546     }
    547 
    548     /**
    549      * Returns the specified tag's value as a java.util.Date, if possible.
    550      */
    551     public java.util.Date getDate(int tagType) throws MetadataException
    552     {
    553         Object o = getObject(tagType);
    554         if (o==null) {
    555             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    556         } else if (o instanceof java.util.Date) {
     695                return null;
     696            }
     697        }
     698        if (o instanceof Number)
     699            return (((Number)o).doubleValue() != 0);
     700        return null;
     701    }
     702
     703    /**
     704     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
     705     * <p/>
     706     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
     707     * the current {@link TimeZone}.  If the {@link TimeZone} is known, call the overload that accepts one as an argument.
     708     */
     709    @Nullable
     710    public java.util.Date getDate(int tagType)
     711    {
     712        return getDate(tagType, null);
     713    }
     714   
     715    /**
     716     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
     717     * <p/>
     718     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
     719     * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).  Note that this parameter
     720     * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect.
     721     */
     722    @Nullable
     723    public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone)
     724    {
     725        Object o = getObject(tagType);
     726
     727        if (o == null)
     728            return null;
     729
     730        if (o instanceof java.util.Date)
    557731            return (java.util.Date)o;
    558         } else if (o instanceof String) {
    559             // add new dateformat strings to make this method even smarter
    560             // so far, this seems to cover all known date strings
    561             // (for example, AM and PM strings are not supported...)
     732
     733        if (o instanceof String) {
     734            // This seems to cover all known Exif date strings
     735            // Note that "    :  :     :  :  " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html
    562736            String datePatterns[] = {
    563                 "yyyy:MM:dd HH:mm:ss",
    564                 "yyyy:MM:dd HH:mm",
    565                 "yyyy-MM-dd HH:mm:ss",
    566                 "yyyy-MM-dd HH:mm"};
     737                    "yyyy:MM:dd HH:mm:ss",
     738                    "yyyy:MM:dd HH:mm",
     739                    "yyyy-MM-dd HH:mm:ss",
     740                    "yyyy-MM-dd HH:mm",
     741                    "yyyy.MM.dd HH:mm:ss",
     742                    "yyyy.MM.dd HH:mm" };
    567743            String dateString = (String)o;
    568             for (int i = 0; i<datePatterns.length; i++) {
     744            for (String datePattern : datePatterns) {
    569745                try {
    570                     DateFormat parser = new java.text.SimpleDateFormat(datePatterns[i]);
     746                    DateFormat parser = new SimpleDateFormat(datePattern);
     747                    if (timeZone != null)
     748                        parser.setTimeZone(timeZone);
    571749                    return parser.parse(dateString);
    572                 } catch (java.text.ParseException ex) {
     750                } catch (ParseException ex) {
    573751                    // simply try the next pattern
    574752                }
    575753            }
    576754        }
    577         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a java.util.Date.  It is of type '" + o.getClass() + "'.");
    578     }
    579 
    580     /**
    581      * Returns the specified tag's value as a Rational, if possible.
    582      */
    583     public Rational getRational(int tagType) throws MetadataException
    584     {
    585         Object o = getObject(tagType);
    586         if (o==null) {
    587             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    588         } else if (o instanceof Rational) {
     755        return null;
     756    }
     757
     758    /** Returns the specified tag's value as a Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     759    @Nullable
     760    public Rational getRational(int tagType)
     761    {
     762        Object o = getObject(tagType);
     763
     764        if (o == null)
     765            return null;
     766
     767        if (o instanceof Rational)
    589768            return (Rational)o;
    590         }
    591         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational.  It is of type '" + o.getClass() + "'.");
    592     }
    593 
    594     public Rational[] getRationalArray(int tagType) throws MetadataException
    595     {
    596         Object o = getObject(tagType);
    597         if (o==null) {
    598             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    599         } else if (o instanceof Rational[]) {
     769        if (o instanceof Integer)
     770            return new Rational((Integer)o, 1);
     771        if (o instanceof Long)
     772            return new Rational((Long)o, 1);
     773
     774        // NOTE not doing conversions for real number types
     775
     776        return null;
     777    }
     778
     779    /** Returns the specified tag's value as an array of Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     780    @Nullable
     781    public Rational[] getRationalArray(int tagType)
     782    {
     783        Object o = getObject(tagType);
     784        if (o == null)
     785            return null;
     786
     787        if (o instanceof Rational[])
    600788            return (Rational[])o;
    601         }
    602         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational array.  It is of type '" + o.getClass() + "'.");
     789
     790        return null;
    603791    }
    604792
     
    606794     * Returns the specified tag's value as a String.  This value is the 'raw' value.  A more presentable decoding
    607795     * of this value may be obtained from the corresponding Descriptor.
    608      * @return the String reprensentation of the tag's value, or
     796     *
     797     * @return the String representation of the tag's value, or
    609798     *         <code>null</code> if the tag hasn't been defined.
    610799     */
     800    @Nullable
    611801    public String getString(int tagType)
    612802    {
    613803        Object o = getObject(tagType);
    614         if (o==null)
     804        if (o == null)
    615805            return null;
    616806
     
    618808            return ((Rational)o).toSimpleString(true);
    619809
    620         if (o.getClass().isArray())
    621         {
     810        if (o.getClass().isArray()) {
    622811            // handle arrays of objects and primitives
    623812            int arrayLength = Array.getLength(o);
    624             // determine if this is an array of objects i.e. [Lcom.drew.blah
    625             boolean isObjectArray = o.getClass().toString().startsWith("class [L");
    626             StringBuffer sbuffer = new StringBuffer();
    627             for (int i = 0; i<arrayLength; i++)
    628             {
    629                 if (i!=0)
    630                     sbuffer.append(' ');
     813            final Class<?> componentType = o.getClass().getComponentType();
     814            boolean isObjectArray = Object.class.isAssignableFrom(componentType);
     815            boolean isFloatArray = componentType.getName().equals("float");
     816            boolean isDoubleArray = componentType.getName().equals("double");
     817            boolean isIntArray = componentType.getName().equals("int");
     818            boolean isLongArray = componentType.getName().equals("long");
     819            boolean isByteArray = componentType.getName().equals("byte");
     820            StringBuilder string = new StringBuilder();
     821            for (int i = 0; i < arrayLength; i++) {
     822                if (i != 0)
     823                    string.append(' ');
    631824                if (isObjectArray)
    632                     sbuffer.append(Array.get(o, i).toString());
     825                    string.append(Array.get(o, i).toString());
     826                else if (isIntArray)
     827                    string.append(Array.getInt(o, i));
     828                else if (isLongArray)
     829                    string.append(Array.getLong(o, i));
     830                else if (isFloatArray)
     831                    string.append(Array.getFloat(o, i));
     832                else if (isDoubleArray)
     833                    string.append(Array.getDouble(o, i));
     834                else if (isByteArray)
     835                    string.append(Array.getByte(o, i));
    633836                else
    634                     sbuffer.append(Array.getInt(o, i));
    635             }
    636             return sbuffer.toString();
    637         }
    638 
     837                    addError("Unexpected array component type: " + componentType.getName());
     838            }
     839            return string.toString();
     840        }
     841
     842        // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
     843        // the actual data within the file.  It is not inconceivable that whitespace may be significant here, so we
     844        // do not trim.  Also, if support is added for writing data back to files, this may cause issues.
     845        // We leave trimming to the presentation layer.
    639846        return o.toString();
    640847    }
    641848
     849    @Nullable
     850    public String getString(int tagType, String charset)
     851    {
     852        byte[] bytes = getByteArray(tagType);
     853        if (bytes==null)
     854            return null;
     855        try {
     856            return new String(bytes, charset);
     857        } catch (UnsupportedEncodingException e) {
     858            return null;
     859        }
     860    }
     861
    642862    /**
    643863     * Returns the object hashed for the particular tag type specified, if available.
     864     *
    644865     * @param tagType the tag type identifier
    645      * @return the tag's value as an Object if available, else null
    646      */
     866     * @return the tag's value as an Object if available, else <code>null</code>
     867     */
     868    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
     869    @Nullable
    647870    public Object getObject(int tagType)
    648871    {
    649         return _tagMap.get(new Integer(tagType));
     872        return _tagMap.get(Integer.valueOf(tagType));
    650873    }
    651874
     
    654877    /**
    655878     * Returns the name of a specified tag as a String.
     879     *
    656880     * @param tagType the tag type identifier
    657881     * @return the tag's name as a String
    658882     */
     883    @NotNull
    659884    public String getTagName(int tagType)
    660885    {
    661         Integer key = new Integer(tagType);
    662         HashMap nameMap = getTagNameMap();
    663         if (!nameMap.containsKey(key)) {
     886        HashMap<Integer, String> nameMap = getTagNameMap();
     887        if (!nameMap.containsKey(tagType)) {
    664888            String hex = Integer.toHexString(tagType);
    665             while (hex.length()<4) {
     889            while (hex.length() < 4) {
    666890                hex = "0" + hex;
    667891            }
    668892            return "Unknown tag (0x" + hex + ")";
    669893        }
    670         return (String)nameMap.get(key);
     894        return nameMap.get(tagType);
    671895    }
    672896
     
    674898     * Provides a description of a tag's value using the descriptor set by
    675899     * <code>setDescriptor(Descriptor)</code>.
     900     *
    676901     * @param tagType the tag type identifier
    677902     * @return the tag value's description as a String
    678      * @throws MetadataException if a descriptor hasn't been set, or if an error
    679      * occurs during calculation of the description within the Descriptor
    680      */
    681     public String getDescription(int tagType) throws MetadataException
    682     {
    683         if (_descriptor==null) {
    684             throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided");
    685         }
    686 
     903     */
     904    @Nullable
     905    public String getDescription(int tagType)
     906    {
     907        assert(_descriptor != null);
    687908        return _descriptor.getDescription(tagType);
    688909    }
Note: See TracChangeset for help on using the changeset viewer.