source: josm/trunk/src/com/drew/metadata/Directory.java@ 7828

Last change on this file since 7828 was 6127, checked in by bastiK, 12 years ago

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

File size: 31.4 KB
RevLine 
[4231]1/*
[6127]2 * Copyright 2002-2012 Drew Noakes
[4231]3 *
[6127]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
[4231]7 *
[6127]8 * http://www.apache.org/licenses/LICENSE-2.0
[4231]9 *
[6127]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/
[4231]20 */
21package com.drew.metadata;
22
23import com.drew.lang.Rational;
[6127]24import com.drew.lang.annotations.NotNull;
25import com.drew.lang.annotations.Nullable;
26import com.drew.lang.annotations.SuppressWarnings;
[4231]27
[6127]28import java.io.UnsupportedEncodingException;
[4231]29import java.lang.reflect.Array;
30import java.text.DateFormat;
[6127]31import java.text.ParseException;
32import java.text.SimpleDateFormat;
33import java.util.*;
[4231]34
35/**
[6127]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
[4231]40 */
[6127]41public abstract class Directory
[4231]42{
[6127]43 // TODO get Array methods need to return cloned data, to maintain this directory's integrity
[4231]44
[6127]45 /** Map of values hashed by type identifiers. */
46 @NotNull
47 protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
[4231]48
49 /**
50 * A convenient list holding tag values in the order in which they were stored.
51 * This is used for creation of an iterator, and for counting the number of
52 * defined tags.
53 */
[6127]54 @NotNull
55 protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
[4231]56
[6127]57 @NotNull
58 private final Collection<String> _errorList = new ArrayList<String>(4);
[4231]59
[6127]60 /** The descriptor used to interpret tag values. */
61 protected TagDescriptor _descriptor;
62
[4231]63// ABSTRACT METHODS
64
65 /**
66 * Provides the name of the directory, for display purposes. E.g. <code>Exif</code>
[6127]67 *
[4231]68 * @return the name of the directory
69 */
[6127]70 @NotNull
[4231]71 public abstract String getName();
72
73 /**
74 * Provides the map of tag names, hashed by tag type identifier.
[6127]75 *
[4231]76 * @return the map of tag names
77 */
[6127]78 @NotNull
79 protected abstract HashMap<Integer, String> getTagNameMap();
[4231]80
[6127]81 protected Directory()
82 {}
[4231]83
84// VARIOUS METHODS
85
86 /**
87 * Indicates whether the specified tag type has been set.
[6127]88 *
[4231]89 * @param tagType the tag type to check for
90 * @return true if a value exists for the specified tag type, false if not
91 */
[6127]92 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
[4231]93 public boolean containsTag(int tagType)
94 {
[6127]95 return _tagMap.containsKey(Integer.valueOf(tagType));
[4231]96 }
97
98 /**
99 * Returns an Iterator of Tag instances that have been set in this Directory.
[6127]100 *
[4231]101 * @return an Iterator of Tag instances
102 */
[6127]103 @NotNull
104 public Collection<Tag> getTags()
[4231]105 {
[6127]106 return _definedTagList;
[4231]107 }
108
109 /**
110 * Returns the number of tags set in this Directory.
[6127]111 *
[4231]112 * @return the number of tags set in this Directory
113 */
114 public int getTagCount()
115 {
116 return _definedTagList.size();
117 }
118
119 /**
[6127]120 * Sets the descriptor used to interpret tag values.
121 *
122 * @param descriptor the descriptor used to interpret tag values
[4231]123 */
[6127]124 @java.lang.SuppressWarnings({ "ConstantConditions" })
125 public void setDescriptor(@NotNull TagDescriptor descriptor)
[4231]126 {
[6127]127 if (descriptor == null)
[4231]128 throw new NullPointerException("cannot set a null descriptor");
129 _descriptor = descriptor;
130 }
131
[6127]132 /**
133 * Registers an error message with this directory.
134 *
135 * @param message an error message.
136 */
137 public void addError(@NotNull String message)
[4231]138 {
139 _errorList.add(message);
140 }
141
[6127]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 */
[4231]147 public boolean hasErrors()
148 {
[6127]149 return _errorList.size() > 0;
[4231]150 }
151
[6127]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()
[4231]159 {
[6127]160 return _errorList;
[4231]161 }
162
[6127]163 /** Returns the count of error messages in this directory. */
[4231]164 public int getErrorCount()
165 {
166 return _errorList.size();
167 }
168
169// TAG SETTERS
170
171 /**
[6127]172 * Sets an <code>int</code> value for the specified tag.
173 *
[4231]174 * @param tagType the tag's value as an int
[6127]175 * @param value the value for the specified tag as an int
[4231]176 */
177 public void setInt(int tagType, int value)
178 {
[6127]179 setObject(tagType, value);
[4231]180 }
181
182 /**
[6127]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
[4231]187 */
[6127]188 public void setIntArray(int tagType, @NotNull int[] ints)
[4231]189 {
[6127]190 setObjectArray(tagType, ints);
[4231]191 }
192
193 /**
[6127]194 * Sets a <code>float</code> value for the specified tag.
195 *
[4231]196 * @param tagType the tag's value as an int
[6127]197 * @param value the value for the specified tag as a float
[4231]198 */
199 public void setFloat(int tagType, float value)
200 {
[6127]201 setObject(tagType, value);
[4231]202 }
203
204 /**
[6127]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 *
[4231]218 * @param tagType the tag's value as an int
[6127]219 * @param value the value for the specified tag as a double
[4231]220 */
[6127]221 public void setDouble(int tagType, double value)
[4231]222 {
223 setObject(tagType, value);
224 }
225
226 /**
[6127]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
[4231]231 */
[6127]232 public void setDoubleArray(int tagType, @NotNull double[] doubles)
[4231]233 {
[6127]234 setObjectArray(tagType, doubles);
[4231]235 }
236
237 /**
[6127]238 * Sets a <code>String</code> value for the specified tag.
239 *
[4231]240 * @param tagType the tag's value as an int
[6127]241 * @param value the value for the specified tag as a String
[4231]242 */
[6127]243 @java.lang.SuppressWarnings({ "ConstantConditions" })
244 public void setString(int tagType, @NotNull String value)
[4231]245 {
[6127]246 if (value == null)
247 throw new NullPointerException("cannot set a null String");
248 setObject(tagType, value);
[4231]249 }
250
251 /**
[6127]252 * Sets a <code>String[]</code> (array) for the specified tag.
253 *
254 * @param tagType the tag identifier
255 * @param strings the String array to store
256 */
257 public void setStringArray(int tagType, @NotNull String[] strings)
258 {
259 setObjectArray(tagType, strings);
260 }
261
262 /**
263 * Sets a <code>boolean</code> value for the specified tag.
264 *
[4231]265 * @param tagType the tag's value as an int
[6127]266 * @param value the value for the specified tag as a boolean
[4231]267 */
[6127]268 public void setBoolean(int tagType, boolean value)
[4231]269 {
270 setObject(tagType, value);
271 }
272
273 /**
[6127]274 * Sets a <code>long</code> value for the specified tag.
275 *
[4231]276 * @param tagType the tag's value as an int
[6127]277 * @param value the value for the specified tag as a long
[4231]278 */
[6127]279 public void setLong(int tagType, long value)
[4231]280 {
[6127]281 setObject(tagType, value);
[4231]282 }
283
284 /**
[6127]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
[4231]289 */
[6127]290 public void setDate(int tagType, @NotNull java.util.Date value)
[4231]291 {
[6127]292 setObject(tagType, value);
[4231]293 }
294
295 /**
[6127]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
[4231]300 */
[6127]301 public void setRational(int tagType, @NotNull Rational rational)
[4231]302 {
[6127]303 setObject(tagType, rational);
[4231]304 }
305
306 /**
[6127]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
[4231]311 */
[6127]312 public void setRationalArray(int tagType, @NotNull Rational[] rationals)
[4231]313 {
[6127]314 setObjectArray(tagType, rationals);
[4231]315 }
316
317 /**
[6127]318 * Sets a <code>byte[]</code> (array) for the specified tag.
319 *
[4231]320 * @param tagType the tag identifier
[6127]321 * @param bytes the byte array to store
[4231]322 */
[6127]323 public void setByteArray(int tagType, @NotNull byte[] bytes)
[4231]324 {
[6127]325 setObjectArray(tagType, bytes);
[4231]326 }
327
328 /**
[6127]329 * Sets a <code>Object</code> for the specified tag.
330 *
[4231]331 * @param tagType the tag's value as an int
[6127]332 * @param value the value for the specified tag
[4231]333 * @throws NullPointerException if value is <code>null</code>
334 */
[6127]335 @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
336 public void setObject(int tagType, @NotNull Object value)
[4231]337 {
[6127]338 if (value == null)
[4231]339 throw new NullPointerException("cannot set a null object");
340
[6127]341 if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
[4231]342 _definedTagList.add(new Tag(tagType, this));
343 }
[6127]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);
[4231]350 }
351
352 /**
[6127]353 * Sets an array <code>Object</code> for the specified tag.
354 *
[4231]355 * @param tagType the tag's value as an int
[6127]356 * @param array the array of values for the specified tag
[4231]357 */
[6127]358 public void setObjectArray(int tagType, @NotNull Object array)
[4231]359 {
360 // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
361 setObject(tagType, array);
362 }
363
364// TAG GETTERS
365
366 /**
[6127]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.
[4231]380 */
381 public int getInt(int tagType) throws MetadataException
382 {
[6127]383 Integer integer = getInteger(tagType);
384 if (integer!=null)
385 return integer;
386
[4231]387 Object o = getObject(tagType);
[6127]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) {
[4231]417 try {
418 return Integer.parseInt((String)o);
419 } catch (NumberFormatException nfe) {
420 // convert the char array to an int
421 String s = (String)o;
422 byte[] bytes = s.getBytes();
423 long val = 0;
[6127]424 for (byte aByte : bytes) {
[4231]425 val = val << 8;
[6127]426 val += (aByte & 0xff);
[4231]427 }
428 return (int)val;
429 }
430 } else if (o instanceof Number) {
431 return ((Number)o).intValue();
432 } else if (o instanceof Rational[]) {
433 Rational[] rationals = (Rational[])o;
[6127]434 if (rationals.length == 1)
[4231]435 return rationals[0].intValue();
436 } else if (o instanceof byte[]) {
437 byte[] bytes = (byte[])o;
[6127]438 if (bytes.length == 1)
439 return (int)bytes[0];
[4231]440 } else if (o instanceof int[]) {
441 int[] ints = (int[])o;
[6127]442 if (ints.length == 1)
[4231]443 return ints[0];
444 }
[6127]445 return null;
[4231]446 }
447
448 /**
449 * Gets the specified tag's value as a String array, if possible. Only supported
450 * where the tag is set as String[], String, int[], byte[] or Rational[].
[6127]451 *
[4231]452 * @param tagType the tag identifier
[6127]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.
[4231]454 */
[6127]455 @Nullable
456 public String[] getStringArray(int tagType)
[4231]457 {
458 Object o = getObject(tagType);
[6127]459 if (o == null)
460 return null;
461 if (o instanceof String[])
[4231]462 return (String[])o;
[6127]463 if (o instanceof String)
464 return new String[] { (String)o };
465 if (o instanceof int[]) {
[4231]466 int[] ints = (int[])o;
467 String[] strings = new String[ints.length];
[6127]468 for (int i = 0; i < strings.length; i++)
[4231]469 strings[i] = Integer.toString(ints[i]);
470 return strings;
471 } else if (o instanceof byte[]) {
472 byte[] bytes = (byte[])o;
473 String[] strings = new String[bytes.length];
[6127]474 for (int i = 0; i < strings.length; i++)
[4231]475 strings[i] = Byte.toString(bytes[i]);
476 return strings;
477 } else if (o instanceof Rational[]) {
478 Rational[] rationals = (Rational[])o;
479 String[] strings = new String[rationals.length];
[6127]480 for (int i = 0; i < strings.length; i++)
[4231]481 strings[i] = rationals[i].toSimpleString(false);
482 return strings;
483 }
[6127]484 return null;
[4231]485 }
486
487 /**
488 * Gets the specified tag's value as an int array, if possible. Only supported
[6127]489 * where the tag is set as String, Integer, int[], byte[] or Rational[].
490 *
[4231]491 * @param tagType the tag identifier
492 * @return the tag's value as an int array
493 */
[6127]494 @Nullable
495 public int[] getIntArray(int tagType)
[4231]496 {
497 Object o = getObject(tagType);
[6127]498 if (o == null)
499 return null;
500 if (o instanceof Rational[]) {
[4231]501 Rational[] rationals = (Rational[])o;
502 int[] ints = new int[rationals.length];
[6127]503 for (int i = 0; i < ints.length; i++) {
[4231]504 ints[i] = rationals[i].intValue();
505 }
506 return ints;
[6127]507 }
508 if (o instanceof int[])
[4231]509 return (int[])o;
[6127]510 if (o instanceof byte[]) {
[4231]511 byte[] bytes = (byte[])o;
512 int[] ints = new int[bytes.length];
[6127]513 for (int i = 0; i < bytes.length; i++) {
[4231]514 byte b = bytes[i];
515 ints[i] = b;
516 }
517 return ints;
[6127]518 }
519 if (o instanceof CharSequence) {
520 CharSequence str = (CharSequence)o;
[4231]521 int[] ints = new int[str.length()];
[6127]522 for (int i = 0; i < str.length(); i++) {
[4231]523 ints[i] = str.charAt(i);
524 }
525 return ints;
526 }
[6127]527 if (o instanceof Integer)
528 return new int[] { (Integer)o };
529
530 return null;
[4231]531 }
532
533 /**
534 * Gets the specified tag's value as an byte array, if possible. Only supported
[6127]535 * where the tag is set as String, Integer, int[], byte[] or Rational[].
536 *
[4231]537 * @param tagType the tag identifier
538 * @return the tag's value as a byte array
539 */
[6127]540 @Nullable
541 public byte[] getByteArray(int tagType)
[4231]542 {
543 Object o = getObject(tagType);
[6127]544 if (o == null) {
545 return null;
[4231]546 } else if (o instanceof Rational[]) {
547 Rational[] rationals = (Rational[])o;
548 byte[] bytes = new byte[rationals.length];
[6127]549 for (int i = 0; i < bytes.length; i++) {
[4231]550 bytes[i] = rationals[i].byteValue();
551 }
552 return bytes;
553 } else if (o instanceof byte[]) {
554 return (byte[])o;
555 } else if (o instanceof int[]) {
556 int[] ints = (int[])o;
557 byte[] bytes = new byte[ints.length];
[6127]558 for (int i = 0; i < ints.length; i++) {
[4231]559 bytes[i] = (byte)ints[i];
560 }
561 return bytes;
[6127]562 } else if (o instanceof CharSequence) {
563 CharSequence str = (CharSequence)o;
[4231]564 byte[] bytes = new byte[str.length()];
[6127]565 for (int i = 0; i < str.length(); i++) {
[4231]566 bytes[i] = (byte)str.charAt(i);
567 }
568 return bytes;
569 }
[6127]570 if (o instanceof Integer)
571 return new byte[] { ((Integer)o).byteValue() };
572
573 return null;
[4231]574 }
575
[6127]576 /** Returns the specified tag's value as a double, if possible. */
[4231]577 public double getDouble(int tagType) throws MetadataException
578 {
[6127]579 Double value = getDoubleObject(tagType);
580 if (value!=null)
581 return value;
[4231]582 Object o = getObject(tagType);
[6127]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) {
[4231]595 try {
596 return Double.parseDouble((String)o);
597 } catch (NumberFormatException nfe) {
[6127]598 return null;
[4231]599 }
[6127]600 }
601 if (o instanceof Number)
[4231]602 return ((Number)o).doubleValue();
[6127]603
604 return null;
[4231]605 }
606
[6127]607 /** Returns the specified tag's value as a float, if possible. */
[4231]608 public float getFloat(int tagType) throws MetadataException
609 {
[6127]610 Float value = getFloatObject(tagType);
611 if (value!=null)
612 return value;
[4231]613 Object o = getObject(tagType);
[6127]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) {
[4231]627 try {
628 return Float.parseFloat((String)o);
629 } catch (NumberFormatException nfe) {
[6127]630 return null;
[4231]631 }
[6127]632 }
633 if (o instanceof Number)
[4231]634 return ((Number)o).floatValue();
[6127]635 return null;
[4231]636 }
637
[6127]638 /** Returns the specified tag's value as a long, if possible. */
[4231]639 public long getLong(int tagType) throws MetadataException
640 {
[6127]641 Long value = getLongObject(tagType);
642 if (value!=null)
643 return value;
[4231]644 Object o = getObject(tagType);
[6127]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) {
[4231]658 try {
659 return Long.parseLong((String)o);
660 } catch (NumberFormatException nfe) {
[6127]661 return null;
[4231]662 }
[6127]663 }
664 if (o instanceof Number)
[4231]665 return ((Number)o).longValue();
[6127]666 return null;
[4231]667 }
668
[6127]669 /** Returns the specified tag's value as a boolean, if possible. */
[4231]670 public boolean getBoolean(int tagType) throws MetadataException
671 {
[6127]672 Boolean value = getBooleanObject(tagType);
673 if (value!=null)
674 return value;
[4231]675 Object o = getObject(tagType);
[6127]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) {
[4231]692 try {
693 return Boolean.getBoolean((String)o);
694 } catch (NumberFormatException nfe) {
[6127]695 return null;
[4231]696 }
697 }
[6127]698 if (o instanceof Number)
699 return (((Number)o).doubleValue() != 0);
700 return null;
[4231]701 }
702
703 /**
[6127]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.
[4231]708 */
[6127]709 @Nullable
710 public java.util.Date getDate(int tagType)
[4231]711 {
[6127]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 {
[4231]725 Object o = getObject(tagType);
[6127]726
727 if (o == null)
728 return null;
729
730 if (o instanceof java.util.Date)
[4231]731 return (java.util.Date)o;
[6127]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
[4231]736 String datePatterns[] = {
[6127]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" };
[4231]743 String dateString = (String)o;
[6127]744 for (String datePattern : datePatterns) {
[4231]745 try {
[6127]746 DateFormat parser = new SimpleDateFormat(datePattern);
747 if (timeZone != null)
748 parser.setTimeZone(timeZone);
[4231]749 return parser.parse(dateString);
[6127]750 } catch (ParseException ex) {
[4231]751 // simply try the next pattern
752 }
753 }
754 }
[6127]755 return null;
[4231]756 }
757
[6127]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)
[4231]761 {
762 Object o = getObject(tagType);
[6127]763
764 if (o == null)
765 return null;
766
767 if (o instanceof Rational)
[4231]768 return (Rational)o;
[6127]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;
[4231]777 }
778
[6127]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)
[4231]782 {
783 Object o = getObject(tagType);
[6127]784 if (o == null)
785 return null;
786
787 if (o instanceof Rational[])
[4231]788 return (Rational[])o;
[6127]789
790 return null;
[4231]791 }
792
793 /**
794 * Returns the specified tag's value as a String. This value is the 'raw' value. A more presentable decoding
795 * of this value may be obtained from the corresponding Descriptor.
[6127]796 *
797 * @return the String representation of the tag's value, or
[4231]798 * <code>null</code> if the tag hasn't been defined.
799 */
[6127]800 @Nullable
[4231]801 public String getString(int tagType)
802 {
803 Object o = getObject(tagType);
[6127]804 if (o == null)
[4231]805 return null;
806
807 if (o instanceof Rational)
808 return ((Rational)o).toSimpleString(true);
809
[6127]810 if (o.getClass().isArray()) {
[4231]811 // handle arrays of objects and primitives
812 int arrayLength = Array.getLength(o);
[6127]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(' ');
[4231]824 if (isObjectArray)
[6127]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));
[4231]836 else
[6127]837 addError("Unexpected array component type: " + componentType.getName());
[4231]838 }
[6127]839 return string.toString();
[4231]840 }
841
[6127]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.
[4231]846 return o.toString();
847 }
848
[6127]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
[4231]862 /**
863 * Returns the object hashed for the particular tag type specified, if available.
[6127]864 *
[4231]865 * @param tagType the tag type identifier
[6127]866 * @return the tag's value as an Object if available, else <code>null</code>
[4231]867 */
[6127]868 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
869 @Nullable
[4231]870 public Object getObject(int tagType)
871 {
[6127]872 return _tagMap.get(Integer.valueOf(tagType));
[4231]873 }
874
875// OTHER METHODS
876
877 /**
878 * Returns the name of a specified tag as a String.
[6127]879 *
[4231]880 * @param tagType the tag type identifier
881 * @return the tag's name as a String
882 */
[6127]883 @NotNull
[4231]884 public String getTagName(int tagType)
885 {
[6127]886 HashMap<Integer, String> nameMap = getTagNameMap();
887 if (!nameMap.containsKey(tagType)) {
[4231]888 String hex = Integer.toHexString(tagType);
[6127]889 while (hex.length() < 4) {
[4231]890 hex = "0" + hex;
891 }
892 return "Unknown tag (0x" + hex + ")";
893 }
[6127]894 return nameMap.get(tagType);
[4231]895 }
896
897 /**
898 * Provides a description of a tag's value using the descriptor set by
899 * <code>setDescriptor(Descriptor)</code>.
[6127]900 *
[4231]901 * @param tagType the tag type identifier
902 * @return the tag value's description as a String
903 */
[6127]904 @Nullable
905 public String getDescription(int tagType)
[4231]906 {
[6127]907 assert(_descriptor != null);
[4231]908 return _descriptor.getDescription(tagType);
909 }
910}
Note: See TracBrowser for help on using the repository browser.