Index: trunk/src/com/drew/metadata/Directory.java
===================================================================
--- trunk/src/com/drew/metadata/Directory.java	(revision 10862)
+++ trunk/src/com/drew/metadata/Directory.java	(revision 13061)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2016 Drew Noakes
+ * Copyright 2002-2017 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,4 +24,5 @@
 import com.drew.lang.annotations.NotNull;
 import com.drew.lang.annotations.Nullable;
+import com.drew.lang.annotations.SuppressWarnings;
 
 import java.io.UnsupportedEncodingException;
@@ -41,7 +42,8 @@
  * @author Drew Noakes https://drewnoakes.com
  */
+@java.lang.SuppressWarnings("WeakerAccess")
 public abstract class Directory
 {
-    private static final DecimalFormat _floatFormat = new DecimalFormat("0.###");
+    private static final String _floatFormatPattern = "0.###";
 
     /** Map of values hashed by type identifiers. */
@@ -260,4 +262,18 @@
 
     /**
+     * Sets a <code>StringValue</code> value for the specified tag.
+     *
+     * @param tagType the tag's value as an int
+     * @param value   the value for the specified tag as a StringValue
+     */
+    @java.lang.SuppressWarnings({ "ConstantConditions" })
+    public void setStringValue(int tagType, @NotNull StringValue value)
+    {
+        if (value == null)
+            throw new NullPointerException("cannot set a null StringValue");
+        setObject(tagType, value);
+    }
+
+    /**
      * Sets a <code>String</code> value for the specified tag.
      *
@@ -280,4 +296,15 @@
      */
     public void setStringArray(int tagType, @NotNull String[] strings)
+    {
+        setObjectArray(tagType, strings);
+    }
+
+    /**
+     * Sets a <code>StringValue[]</code> (array) for the specified tag.
+     *
+     * @param tagType the tag identifier
+     * @param strings the StringValue array to store
+     */
+    public void setStringValueArray(int tagType, @NotNull StringValue[] strings)
     {
         setObjectArray(tagType, strings);
@@ -440,10 +467,10 @@
         if (o instanceof Number) {
             return ((Number)o).intValue();
-        } else if (o instanceof String) {
+        } else if (o instanceof String || o instanceof StringValue) {
             try {
-                return Integer.parseInt((String)o);
+                return Integer.parseInt(o.toString());
             } catch (NumberFormatException nfe) {
                 // convert the char array to an int
-                String s = (String)o;
+                String s = o.toString();
                 byte[] bytes = s.getBytes();
                 long val = 0;
@@ -466,4 +493,8 @@
             if (ints.length == 1)
                 return ints[0];
+        } else if (o instanceof short[]) {
+            short[] shorts = (short[])o;
+            if (shorts.length == 1)
+                return (int)shorts[0];
         }
         return null;
@@ -472,5 +503,5 @@
     /**
      * 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[].
+     * where the tag is set as StringValue[], String[], StringValue, String, int[], byte[] or Rational[].
      *
      * @param tagType the tag identifier
@@ -487,4 +518,13 @@
         if (o instanceof String)
             return new String[] { (String)o };
+        if (o instanceof StringValue)
+            return new String[] { o.toString() };
+        if (o instanceof StringValue[]) {
+            StringValue[] stringValues = (StringValue[])o;
+            String[] strings = new String[stringValues.length];
+            for (int i = 0; i < strings.length; i++)
+                strings[i] = stringValues[i].toString();
+            return strings;
+        }
         if (o instanceof int[]) {
             int[] ints = (int[])o;
@@ -493,5 +533,6 @@
                 strings[i] = Integer.toString(ints[i]);
             return strings;
-        } else if (o instanceof byte[]) {
+        }
+        if (o instanceof byte[]) {
             byte[] bytes = (byte[])o;
             String[] strings = new String[bytes.length];
@@ -499,5 +540,6 @@
                 strings[i] = Byte.toString(bytes[i]);
             return strings;
-        } else if (o instanceof Rational[]) {
+        }
+        if (o instanceof Rational[]) {
             Rational[] rationals = (Rational[])o;
             String[] strings = new String[rationals.length];
@@ -506,4 +548,24 @@
             return strings;
         }
+        return null;
+    }
+
+    /**
+     * Gets the specified tag's value as a StringValue array, if possible.
+     * Only succeeds if the tag is set as StringValue[], or StringValue.
+     *
+     * @param tagType the tag identifier
+     * @return the tag's value as an array of StringValues. If the value is unset or cannot be converted, <code>null</code> is returned.
+     */
+    @Nullable
+    public StringValue[] getStringValueArray(int tagType)
+    {
+        Object o = getObject(tagType);
+        if (o == null)
+            return null;
+        if (o instanceof StringValue[])
+            return (StringValue[])o;
+        if (o instanceof StringValue)
+            return new StringValue[] {(StringValue) o};
         return null;
     }
@@ -575,4 +637,6 @@
         if (o == null) {
             return null;
+        } else if (o instanceof StringValue) {
+            return ((StringValue)o).getBytes();
         } else if (o instanceof Rational[]) {
             Rational[] rationals = (Rational[])o;
@@ -630,7 +694,7 @@
         if (o == null)
             return null;
-        if (o instanceof String) {
+        if (o instanceof String || o instanceof StringValue) {
             try {
-                return Double.parseDouble((String)o);
+                return Double.parseDouble(o.toString());
             } catch (NumberFormatException nfe) {
                 return null;
@@ -662,7 +726,7 @@
         if (o == null)
             return null;
-        if (o instanceof String) {
+        if (o instanceof String || o instanceof StringValue) {
             try {
-                return Float.parseFloat((String)o);
+                return Float.parseFloat(o.toString());
             } catch (NumberFormatException nfe) {
                 return null;
@@ -678,5 +742,5 @@
     {
         Long value = getLongObject(tagType);
-        if (value!=null)
+        if (value != null)
             return value;
         Object o = getObject(tagType);
@@ -693,7 +757,7 @@
         if (o == null)
             return null;
-        if (o instanceof String) {
+        if (o instanceof String || o instanceof StringValue) {
             try {
-                return Long.parseLong((String)o);
+                return Long.parseLong(o.toString());
             } catch (NumberFormatException nfe) {
                 return null;
@@ -709,5 +773,5 @@
     {
         Boolean value = getBooleanObject(tagType);
-        if (value!=null)
+        if (value != null)
             return value;
         Object o = getObject(tagType);
@@ -719,4 +783,5 @@
     /** Returns the specified tag's value as a boolean.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     @Nullable
+    @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
     public Boolean getBooleanObject(int tagType)
     {
@@ -726,7 +791,7 @@
         if (o instanceof Boolean)
             return (Boolean)o;
-        if (o instanceof String) {
+        if (o instanceof String || o instanceof StringValue) {
             try {
-                return Boolean.getBoolean((String)o);
+                return Boolean.getBoolean(o.toString());
             } catch (NumberFormatException nfe) {
                 return null;
@@ -788,5 +853,5 @@
         java.util.Date date = null;
 
-        if (o instanceof String) {
+        if ((o instanceof String) || (o instanceof StringValue)) {
             // This seems to cover all known Exif and Xmp date strings
             // 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
@@ -802,6 +867,8 @@
                     "yyyy-MM-dd",
                     "yyyy-MM",
+                    "yyyyMMdd", // as used in IPTC data
                     "yyyy" };
-            String dateString = (String)o;
+
+            String dateString = o.toString();
 
             // if the date string has subsecond information, it supersedes the subsecond parameter
@@ -945,5 +1012,5 @@
                     if (i != 0)
                         string.append(' ');
-                    string.append(_floatFormat.format(Array.getFloat(o, i)));
+                    string.append(new DecimalFormat(_floatFormatPattern).format(Array.getFloat(o, i)));
                 }
             } else if (componentType.getName().equals("double")) {
@@ -951,5 +1018,5 @@
                     if (i != 0)
                         string.append(' ');
-                    string.append(_floatFormat.format(Array.getDouble(o, i)));
+                    string.append(new DecimalFormat(_floatFormatPattern).format(Array.getDouble(o, i)));
                 }
             } else if (componentType.getName().equals("byte")) {
@@ -967,8 +1034,8 @@
 
         if (o instanceof Double)
-            return _floatFormat.format(((Double)o).doubleValue());
+            return new DecimalFormat(_floatFormatPattern).format(((Double)o).doubleValue());
 
         if (o instanceof Float)
-            return _floatFormat.format(((Float)o).floatValue());
+            return new DecimalFormat(_floatFormatPattern).format(((Float)o).floatValue());
 
         // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
@@ -990,4 +1057,13 @@
             return null;
         }
+    }
+
+    @Nullable
+    public StringValue getStringValue(int tagType)
+    {
+        Object o = getObject(tagType);
+        if (o instanceof StringValue)
+            return (StringValue)o;
+        return null;
     }
 
