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

Last change on this file since 9870 was 8243, checked in by Don-vip, 9 years ago

fix #11359 - update to metadata-extractor 2.8.1

File size: 32.0 KB
Line 
1/*
2 * Copyright 2002-2015 Drew Noakes
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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 * https://drewnoakes.com/code/exif/
19 * https://github.com/drewnoakes/metadata-extractor
20 */
21package com.drew.metadata;
22
23import com.drew.lang.Rational;
24import com.drew.lang.annotations.NotNull;
25import com.drew.lang.annotations.Nullable;
26import com.drew.lang.annotations.SuppressWarnings;
27
28import java.io.UnsupportedEncodingException;
29import java.lang.reflect.Array;
30import java.text.DateFormat;
31import java.text.ParseException;
32import java.text.SimpleDateFormat;
33import java.util.*;
34
35/**
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 https://drewnoakes.com
40 */
41public abstract class Directory
42{
43 /** Map of values hashed by type identifiers. */
44 @NotNull
45 protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
46
47 /**
48 * A convenient list holding tag values in the order in which they were stored.
49 * This is used for creation of an iterator, and for counting the number of
50 * defined tags.
51 */
52 @NotNull
53 protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
54
55 @NotNull
56 private final Collection<String> _errorList = new ArrayList<String>(4);
57
58 /** The descriptor used to interpret tag values. */
59 protected TagDescriptor _descriptor;
60
61// ABSTRACT METHODS
62
63 /**
64 * Provides the name of the directory, for display purposes. E.g. <code>Exif</code>
65 *
66 * @return the name of the directory
67 */
68 @NotNull
69 public abstract String getName();
70
71 /**
72 * Provides the map of tag names, hashed by tag type identifier.
73 *
74 * @return the map of tag names
75 */
76 @NotNull
77 protected abstract HashMap<Integer, String> getTagNameMap();
78
79 protected Directory()
80 {}
81
82// VARIOUS METHODS
83
84 /**
85 * Gets a value indicating whether the directory is empty, meaning it contains no errors and no tag values.
86 */
87 public boolean isEmpty()
88 {
89 return _errorList.isEmpty() && _definedTagList.isEmpty();
90 }
91
92 /**
93 * Indicates whether the specified tag type has been set.
94 *
95 * @param tagType the tag type to check for
96 * @return true if a value exists for the specified tag type, false if not
97 */
98 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
99 public boolean containsTag(int tagType)
100 {
101 return _tagMap.containsKey(Integer.valueOf(tagType));
102 }
103
104 /**
105 * Returns an Iterator of Tag instances that have been set in this Directory.
106 *
107 * @return an Iterator of Tag instances
108 */
109 @NotNull
110 public Collection<Tag> getTags()
111 {
112 return Collections.unmodifiableCollection(_definedTagList);
113 }
114
115 /**
116 * Returns the number of tags set in this Directory.
117 *
118 * @return the number of tags set in this Directory
119 */
120 public int getTagCount()
121 {
122 return _definedTagList.size();
123 }
124
125 /**
126 * Sets the descriptor used to interpret tag values.
127 *
128 * @param descriptor the descriptor used to interpret tag values
129 */
130 @java.lang.SuppressWarnings({ "ConstantConditions" })
131 public void setDescriptor(@NotNull TagDescriptor descriptor)
132 {
133 if (descriptor == null)
134 throw new NullPointerException("cannot set a null descriptor");
135 _descriptor = descriptor;
136 }
137
138 /**
139 * Registers an error message with this directory.
140 *
141 * @param message an error message.
142 */
143 public void addError(@NotNull String message)
144 {
145 _errorList.add(message);
146 }
147
148 /**
149 * Gets a value indicating whether this directory has any error messages.
150 *
151 * @return true if the directory contains errors, otherwise false
152 */
153 public boolean hasErrors()
154 {
155 return _errorList.size() > 0;
156 }
157
158 /**
159 * Used to iterate over any error messages contained in this directory.
160 *
161 * @return an iterable collection of error message strings.
162 */
163 @NotNull
164 public Iterable<String> getErrors()
165 {
166 return Collections.unmodifiableCollection(_errorList);
167 }
168
169 /** Returns the count of error messages in this directory. */
170 public int getErrorCount()
171 {
172 return _errorList.size();
173 }
174
175// TAG SETTERS
176
177 /**
178 * Sets an <code>int</code> value for the specified tag.
179 *
180 * @param tagType the tag's value as an int
181 * @param value the value for the specified tag as an int
182 */
183 public void setInt(int tagType, int value)
184 {
185 setObject(tagType, value);
186 }
187
188 /**
189 * Sets an <code>int[]</code> (array) for the specified tag.
190 *
191 * @param tagType the tag identifier
192 * @param ints the int array to store
193 */
194 public void setIntArray(int tagType, @NotNull int[] ints)
195 {
196 setObjectArray(tagType, ints);
197 }
198
199 /**
200 * Sets a <code>float</code> value for the specified tag.
201 *
202 * @param tagType the tag's value as an int
203 * @param value the value for the specified tag as a float
204 */
205 public void setFloat(int tagType, float value)
206 {
207 setObject(tagType, value);
208 }
209
210 /**
211 * Sets a <code>float[]</code> (array) for the specified tag.
212 *
213 * @param tagType the tag identifier
214 * @param floats the float array to store
215 */
216 public void setFloatArray(int tagType, @NotNull float[] floats)
217 {
218 setObjectArray(tagType, floats);
219 }
220
221 /**
222 * Sets a <code>double</code> value for the specified tag.
223 *
224 * @param tagType the tag's value as an int
225 * @param value the value for the specified tag as a double
226 */
227 public void setDouble(int tagType, double value)
228 {
229 setObject(tagType, value);
230 }
231
232 /**
233 * Sets a <code>double[]</code> (array) for the specified tag.
234 *
235 * @param tagType the tag identifier
236 * @param doubles the double array to store
237 */
238 public void setDoubleArray(int tagType, @NotNull double[] doubles)
239 {
240 setObjectArray(tagType, doubles);
241 }
242
243 /**
244 * Sets a <code>String</code> value for the specified tag.
245 *
246 * @param tagType the tag's value as an int
247 * @param value the value for the specified tag as a String
248 */
249 @java.lang.SuppressWarnings({ "ConstantConditions" })
250 public void setString(int tagType, @NotNull String value)
251 {
252 if (value == null)
253 throw new NullPointerException("cannot set a null String");
254 setObject(tagType, value);
255 }
256
257 /**
258 * Sets a <code>String[]</code> (array) for the specified tag.
259 *
260 * @param tagType the tag identifier
261 * @param strings the String array to store
262 */
263 public void setStringArray(int tagType, @NotNull String[] strings)
264 {
265 setObjectArray(tagType, strings);
266 }
267
268 /**
269 * Sets a <code>boolean</code> value for the specified tag.
270 *
271 * @param tagType the tag's value as an int
272 * @param value the value for the specified tag as a boolean
273 */
274 public void setBoolean(int tagType, boolean value)
275 {
276 setObject(tagType, value);
277 }
278
279 /**
280 * Sets a <code>long</code> value for the specified tag.
281 *
282 * @param tagType the tag's value as an int
283 * @param value the value for the specified tag as a long
284 */
285 public void setLong(int tagType, long value)
286 {
287 setObject(tagType, value);
288 }
289
290 /**
291 * Sets a <code>java.util.Date</code> value for the specified tag.
292 *
293 * @param tagType the tag's value as an int
294 * @param value the value for the specified tag as a java.util.Date
295 */
296 public void setDate(int tagType, @NotNull java.util.Date value)
297 {
298 setObject(tagType, value);
299 }
300
301 /**
302 * Sets a <code>Rational</code> value for the specified tag.
303 *
304 * @param tagType the tag's value as an int
305 * @param rational rational number
306 */
307 public void setRational(int tagType, @NotNull Rational rational)
308 {
309 setObject(tagType, rational);
310 }
311
312 /**
313 * Sets a <code>Rational[]</code> (array) for the specified tag.
314 *
315 * @param tagType the tag identifier
316 * @param rationals the Rational array to store
317 */
318 public void setRationalArray(int tagType, @NotNull Rational[] rationals)
319 {
320 setObjectArray(tagType, rationals);
321 }
322
323 /**
324 * Sets a <code>byte[]</code> (array) for the specified tag.
325 *
326 * @param tagType the tag identifier
327 * @param bytes the byte array to store
328 */
329 public void setByteArray(int tagType, @NotNull byte[] bytes)
330 {
331 setObjectArray(tagType, bytes);
332 }
333
334 /**
335 * Sets a <code>Object</code> for the specified tag.
336 *
337 * @param tagType the tag's value as an int
338 * @param value the value for the specified tag
339 * @throws NullPointerException if value is <code>null</code>
340 */
341 @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
342 public void setObject(int tagType, @NotNull Object value)
343 {
344 if (value == null)
345 throw new NullPointerException("cannot set a null object");
346
347 if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
348 _definedTagList.add(new Tag(tagType, this));
349 }
350// else {
351// final Object oldValue = _tagMap.get(tagType);
352// if (!oldValue.equals(value))
353// addError(String.format("Overwritten tag 0x%s (%s). Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
354// }
355 _tagMap.put(tagType, value);
356 }
357
358 /**
359 * Sets an array <code>Object</code> for the specified tag.
360 *
361 * @param tagType the tag's value as an int
362 * @param array the array of values for the specified tag
363 */
364 public void setObjectArray(int tagType, @NotNull Object array)
365 {
366 // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
367 setObject(tagType, array);
368 }
369
370// TAG GETTERS
371
372 /**
373 * Returns the specified tag's value as an int, if possible. Every attempt to represent the tag's value as an int
374 * is taken. Here is a list of the action taken depending upon the tag's original type:
375 * <ul>
376 * <li> int - Return unchanged.
377 * <li> Number - Return an int value (real numbers are truncated).
378 * <li> Rational - Truncate any fractional part and returns remaining int.
379 * <li> String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR).
380 * <li> Rational[] - Return int value of first item in array.
381 * <li> byte[] - Return int value of first item in array.
382 * <li> int[] - Return int value of first item in array.
383 * </ul>
384 *
385 * @throws MetadataException if no value exists for tagType or if it cannot be converted to an int.
386 */
387 public int getInt(int tagType) throws MetadataException
388 {
389 Integer integer = getInteger(tagType);
390 if (integer!=null)
391 return integer;
392
393 Object o = getObject(tagType);
394 if (o == null)
395 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
396 throw new MetadataException("Tag '" + tagType + "' cannot be converted to int. It is of type '" + o.getClass() + "'.");
397 }
398
399 /**
400 * Returns the specified tag's value as an Integer, if possible. Every attempt to represent the tag's value as an
401 * Integer is taken. Here is a list of the action taken depending upon the tag's original type:
402 * <ul>
403 * <li> int - Return unchanged
404 * <li> Number - Return an int value (real numbers are truncated)
405 * <li> Rational - Truncate any fractional part and returns remaining int
406 * <li> String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR)
407 * <li> Rational[] - Return int value of first item in array if length &gt; 0
408 * <li> byte[] - Return int value of first item in array if length &gt; 0
409 * <li> int[] - Return int value of first item in array if length &gt; 0
410 * </ul>
411 *
412 * If the value is not found or cannot be converted to int, <code>null</code> is returned.
413 */
414 @Nullable
415 public Integer getInteger(int tagType)
416 {
417 Object o = getObject(tagType);
418
419 if (o == null)
420 return null;
421
422 if (o instanceof Number) {
423 return ((Number)o).intValue();
424 } else if (o instanceof String) {
425 try {
426 return Integer.parseInt((String)o);
427 } catch (NumberFormatException nfe) {
428 // convert the char array to an int
429 String s = (String)o;
430 byte[] bytes = s.getBytes();
431 long val = 0;
432 for (byte aByte : bytes) {
433 val = val << 8;
434 val += (aByte & 0xff);
435 }
436 return (int)val;
437 }
438 } else if (o instanceof Rational[]) {
439 Rational[] rationals = (Rational[])o;
440 if (rationals.length == 1)
441 return rationals[0].intValue();
442 } else if (o instanceof byte[]) {
443 byte[] bytes = (byte[])o;
444 if (bytes.length == 1)
445 return (int)bytes[0];
446 } else if (o instanceof int[]) {
447 int[] ints = (int[])o;
448 if (ints.length == 1)
449 return ints[0];
450 }
451 return null;
452 }
453
454 /**
455 * Gets the specified tag's value as a String array, if possible. Only supported
456 * where the tag is set as String[], String, int[], byte[] or Rational[].
457 *
458 * @param tagType the tag identifier
459 * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned.
460 */
461 @Nullable
462 public String[] getStringArray(int tagType)
463 {
464 Object o = getObject(tagType);
465 if (o == null)
466 return null;
467 if (o instanceof String[])
468 return (String[])o;
469 if (o instanceof String)
470 return new String[] { (String)o };
471 if (o instanceof int[]) {
472 int[] ints = (int[])o;
473 String[] strings = new String[ints.length];
474 for (int i = 0; i < strings.length; i++)
475 strings[i] = Integer.toString(ints[i]);
476 return strings;
477 } else if (o instanceof byte[]) {
478 byte[] bytes = (byte[])o;
479 String[] strings = new String[bytes.length];
480 for (int i = 0; i < strings.length; i++)
481 strings[i] = Byte.toString(bytes[i]);
482 return strings;
483 } else if (o instanceof Rational[]) {
484 Rational[] rationals = (Rational[])o;
485 String[] strings = new String[rationals.length];
486 for (int i = 0; i < strings.length; i++)
487 strings[i] = rationals[i].toSimpleString(false);
488 return strings;
489 }
490 return null;
491 }
492
493 /**
494 * Gets the specified tag's value as an int array, if possible. Only supported
495 * where the tag is set as String, Integer, int[], byte[] or Rational[].
496 *
497 * @param tagType the tag identifier
498 * @return the tag's value as an int array
499 */
500 @Nullable
501 public int[] getIntArray(int tagType)
502 {
503 Object o = getObject(tagType);
504 if (o == null)
505 return null;
506 if (o instanceof int[])
507 return (int[])o;
508 if (o instanceof Rational[]) {
509 Rational[] rationals = (Rational[])o;
510 int[] ints = new int[rationals.length];
511 for (int i = 0; i < ints.length; i++) {
512 ints[i] = rationals[i].intValue();
513 }
514 return ints;
515 }
516 if (o instanceof short[]) {
517 short[] shorts = (short[])o;
518 int[] ints = new int[shorts.length];
519 for (int i = 0; i < shorts.length; i++) {
520 ints[i] = shorts[i];
521 }
522 return ints;
523 }
524 if (o instanceof byte[]) {
525 byte[] bytes = (byte[])o;
526 int[] ints = new int[bytes.length];
527 for (int i = 0; i < bytes.length; i++) {
528 ints[i] = bytes[i];
529 }
530 return ints;
531 }
532 if (o instanceof CharSequence) {
533 CharSequence str = (CharSequence)o;
534 int[] ints = new int[str.length()];
535 for (int i = 0; i < str.length(); i++) {
536 ints[i] = str.charAt(i);
537 }
538 return ints;
539 }
540 if (o instanceof Integer)
541 return new int[] { (Integer)o };
542
543 return null;
544 }
545
546 /**
547 * Gets the specified tag's value as an byte array, if possible. Only supported
548 * where the tag is set as String, Integer, int[], byte[] or Rational[].
549 *
550 * @param tagType the tag identifier
551 * @return the tag's value as a byte array
552 */
553 @Nullable
554 public byte[] getByteArray(int tagType)
555 {
556 Object o = getObject(tagType);
557 if (o == null) {
558 return null;
559 } else if (o instanceof Rational[]) {
560 Rational[] rationals = (Rational[])o;
561 byte[] bytes = new byte[rationals.length];
562 for (int i = 0; i < bytes.length; i++) {
563 bytes[i] = rationals[i].byteValue();
564 }
565 return bytes;
566 } else if (o instanceof byte[]) {
567 return (byte[])o;
568 } else if (o instanceof int[]) {
569 int[] ints = (int[])o;
570 byte[] bytes = new byte[ints.length];
571 for (int i = 0; i < ints.length; i++) {
572 bytes[i] = (byte)ints[i];
573 }
574 return bytes;
575 } else if (o instanceof short[]) {
576 short[] shorts = (short[])o;
577 byte[] bytes = new byte[shorts.length];
578 for (int i = 0; i < shorts.length; i++) {
579 bytes[i] = (byte)shorts[i];
580 }
581 return bytes;
582 } else if (o instanceof CharSequence) {
583 CharSequence str = (CharSequence)o;
584 byte[] bytes = new byte[str.length()];
585 for (int i = 0; i < str.length(); i++) {
586 bytes[i] = (byte)str.charAt(i);
587 }
588 return bytes;
589 }
590 if (o instanceof Integer)
591 return new byte[] { ((Integer)o).byteValue() };
592
593 return null;
594 }
595
596 /** Returns the specified tag's value as a double, if possible. */
597 public double getDouble(int tagType) throws MetadataException
598 {
599 Double value = getDoubleObject(tagType);
600 if (value!=null)
601 return value;
602 Object o = getObject(tagType);
603 if (o == null)
604 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
605 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double. It is of type '" + o.getClass() + "'.");
606 }
607 /** Returns the specified tag's value as a Double. If the tag is not set or cannot be converted, <code>null</code> is returned. */
608 @Nullable
609 public Double getDoubleObject(int tagType)
610 {
611 Object o = getObject(tagType);
612 if (o == null)
613 return null;
614 if (o instanceof String) {
615 try {
616 return Double.parseDouble((String)o);
617 } catch (NumberFormatException nfe) {
618 return null;
619 }
620 }
621 if (o instanceof Number)
622 return ((Number)o).doubleValue();
623
624 return null;
625 }
626
627 /** Returns the specified tag's value as a float, if possible. */
628 public float getFloat(int tagType) throws MetadataException
629 {
630 Float value = getFloatObject(tagType);
631 if (value!=null)
632 return value;
633 Object o = getObject(tagType);
634 if (o == null)
635 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
636 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float. It is of type '" + o.getClass() + "'.");
637 }
638
639 /** Returns the specified tag's value as a float. If the tag is not set or cannot be converted, <code>null</code> is returned. */
640 @Nullable
641 public Float getFloatObject(int tagType)
642 {
643 Object o = getObject(tagType);
644 if (o == null)
645 return null;
646 if (o instanceof String) {
647 try {
648 return Float.parseFloat((String)o);
649 } catch (NumberFormatException nfe) {
650 return null;
651 }
652 }
653 if (o instanceof Number)
654 return ((Number)o).floatValue();
655 return null;
656 }
657
658 /** Returns the specified tag's value as a long, if possible. */
659 public long getLong(int tagType) throws MetadataException
660 {
661 Long value = getLongObject(tagType);
662 if (value!=null)
663 return value;
664 Object o = getObject(tagType);
665 if (o == null)
666 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
667 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long. It is of type '" + o.getClass() + "'.");
668 }
669
670 /** Returns the specified tag's value as a long. If the tag is not set or cannot be converted, <code>null</code> is returned. */
671 @Nullable
672 public Long getLongObject(int tagType)
673 {
674 Object o = getObject(tagType);
675 if (o == null)
676 return null;
677 if (o instanceof String) {
678 try {
679 return Long.parseLong((String)o);
680 } catch (NumberFormatException nfe) {
681 return null;
682 }
683 }
684 if (o instanceof Number)
685 return ((Number)o).longValue();
686 return null;
687 }
688
689 /** Returns the specified tag's value as a boolean, if possible. */
690 public boolean getBoolean(int tagType) throws MetadataException
691 {
692 Boolean value = getBooleanObject(tagType);
693 if (value!=null)
694 return value;
695 Object o = getObject(tagType);
696 if (o == null)
697 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
698 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean. It is of type '" + o.getClass() + "'.");
699 }
700
701 /** Returns the specified tag's value as a boolean. If the tag is not set or cannot be converted, <code>null</code> is returned. */
702 @Nullable
703 @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
704 public Boolean getBooleanObject(int tagType)
705 {
706 Object o = getObject(tagType);
707 if (o == null)
708 return null;
709 if (o instanceof Boolean)
710 return (Boolean)o;
711 if (o instanceof String) {
712 try {
713 return Boolean.getBoolean((String)o);
714 } catch (NumberFormatException nfe) {
715 return null;
716 }
717 }
718 if (o instanceof Number)
719 return (((Number)o).doubleValue() != 0);
720 return null;
721 }
722
723 /**
724 * 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.
725 * <p>
726 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
727 * the current {@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument.
728 */
729 @Nullable
730 public java.util.Date getDate(int tagType)
731 {
732 return getDate(tagType, null);
733 }
734
735 /**
736 * 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.
737 * <p>
738 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
739 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter
740 * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect.
741 */
742 @Nullable
743 public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone)
744 {
745 Object o = getObject(tagType);
746
747 if (o == null)
748 return null;
749
750 if (o instanceof java.util.Date)
751 return (java.util.Date)o;
752
753 if (o instanceof String) {
754 // This seems to cover all known Exif date strings
755 // 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
756 String datePatterns[] = {
757 "yyyy:MM:dd HH:mm:ss",
758 "yyyy:MM:dd HH:mm",
759 "yyyy-MM-dd HH:mm:ss",
760 "yyyy-MM-dd HH:mm",
761 "yyyy.MM.dd HH:mm:ss",
762 "yyyy.MM.dd HH:mm" };
763 String dateString = (String)o;
764 for (String datePattern : datePatterns) {
765 try {
766 DateFormat parser = new SimpleDateFormat(datePattern);
767 if (timeZone != null)
768 parser.setTimeZone(timeZone);
769 else
770 parser.setTimeZone(TimeZone.getTimeZone("GMT")); // don't interpret zone time
771
772 return parser.parse(dateString);
773 } catch (ParseException ex) {
774 // simply try the next pattern
775 }
776 }
777 }
778 return null;
779 }
780
781 /** Returns the specified tag's value as a Rational. If the value is unset or cannot be converted, <code>null</code> is returned. */
782 @Nullable
783 public Rational getRational(int tagType)
784 {
785 Object o = getObject(tagType);
786
787 if (o == null)
788 return null;
789
790 if (o instanceof Rational)
791 return (Rational)o;
792 if (o instanceof Integer)
793 return new Rational((Integer)o, 1);
794 if (o instanceof Long)
795 return new Rational((Long)o, 1);
796
797 // NOTE not doing conversions for real number types
798
799 return null;
800 }
801
802 /** 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. */
803 @Nullable
804 public Rational[] getRationalArray(int tagType)
805 {
806 Object o = getObject(tagType);
807 if (o == null)
808 return null;
809
810 if (o instanceof Rational[])
811 return (Rational[])o;
812
813 return null;
814 }
815
816 /**
817 * Returns the specified tag's value as a String. This value is the 'raw' value. A more presentable decoding
818 * of this value may be obtained from the corresponding Descriptor.
819 *
820 * @return the String representation of the tag's value, or
821 * <code>null</code> if the tag hasn't been defined.
822 */
823 @Nullable
824 public String getString(int tagType)
825 {
826 Object o = getObject(tagType);
827 if (o == null)
828 return null;
829
830 if (o instanceof Rational)
831 return ((Rational)o).toSimpleString(true);
832
833 if (o.getClass().isArray()) {
834 // handle arrays of objects and primitives
835 int arrayLength = Array.getLength(o);
836 final Class<?> componentType = o.getClass().getComponentType();
837 boolean isObjectArray = Object.class.isAssignableFrom(componentType);
838 boolean isFloatArray = componentType.getName().equals("float");
839 boolean isDoubleArray = componentType.getName().equals("double");
840 boolean isIntArray = componentType.getName().equals("int");
841 boolean isLongArray = componentType.getName().equals("long");
842 boolean isByteArray = componentType.getName().equals("byte");
843 boolean isShortArray = componentType.getName().equals("short");
844 StringBuilder string = new StringBuilder();
845 for (int i = 0; i < arrayLength; i++) {
846 if (i != 0)
847 string.append(' ');
848 if (isObjectArray)
849 string.append(Array.get(o, i).toString());
850 else if (isIntArray)
851 string.append(Array.getInt(o, i));
852 else if (isShortArray)
853 string.append(Array.getShort(o, i));
854 else if (isLongArray)
855 string.append(Array.getLong(o, i));
856 else if (isFloatArray)
857 string.append(Array.getFloat(o, i));
858 else if (isDoubleArray)
859 string.append(Array.getDouble(o, i));
860 else if (isByteArray)
861 string.append(Array.getByte(o, i));
862 else
863 addError("Unexpected array component type: " + componentType.getName());
864 }
865 return string.toString();
866 }
867
868 // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
869 // the actual data within the file. It is not inconceivable that whitespace may be significant here, so we
870 // do not trim. Also, if support is added for writing data back to files, this may cause issues.
871 // We leave trimming to the presentation layer.
872 return o.toString();
873 }
874
875 @Nullable
876 public String getString(int tagType, String charset)
877 {
878 byte[] bytes = getByteArray(tagType);
879 if (bytes==null)
880 return null;
881 try {
882 return new String(bytes, charset);
883 } catch (UnsupportedEncodingException e) {
884 return null;
885 }
886 }
887
888 /**
889 * Returns the object hashed for the particular tag type specified, if available.
890 *
891 * @param tagType the tag type identifier
892 * @return the tag's value as an Object if available, else <code>null</code>
893 */
894 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
895 @Nullable
896 public Object getObject(int tagType)
897 {
898 return _tagMap.get(Integer.valueOf(tagType));
899 }
900
901// OTHER METHODS
902
903 /**
904 * Returns the name of a specified tag as a String.
905 *
906 * @param tagType the tag type identifier
907 * @return the tag's name as a String
908 */
909 @NotNull
910 public String getTagName(int tagType)
911 {
912 HashMap<Integer, String> nameMap = getTagNameMap();
913 if (!nameMap.containsKey(tagType)) {
914 String hex = Integer.toHexString(tagType);
915 while (hex.length() < 4) {
916 hex = "0" + hex;
917 }
918 return "Unknown tag (0x" + hex + ")";
919 }
920 return nameMap.get(tagType);
921 }
922
923 /**
924 * Gets whether the specified tag is known by the directory and has a name.
925 *
926 * @param tagType the tag type identifier
927 * @return whether this directory has a name for the specified tag
928 */
929 public boolean hasTagName(int tagType)
930 {
931 return getTagNameMap().containsKey(tagType);
932 }
933
934 /**
935 * Provides a description of a tag's value using the descriptor set by
936 * <code>setDescriptor(Descriptor)</code>.
937 *
938 * @param tagType the tag type identifier
939 * @return the tag value's description as a String
940 */
941 @Nullable
942 public String getDescription(int tagType)
943 {
944 assert(_descriptor != null);
945 return _descriptor.getDescription(tagType);
946 }
947
948 @Override
949 public String toString()
950 {
951 return String.format("%s Directory (%d %s)",
952 getName(),
953 _tagMap.size(),
954 _tagMap.size() == 1
955 ? "tag"
956 : "tags");
957 }
958}
Note: See TracBrowser for help on using the repository browser.