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

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

fix #11162 - update to metadata-extractor 2.7.2

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