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

Last change on this file since 6209 was 6127, checked in by bastiK, 11 years ago

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

File size: 31.4 KB
Line 
1/*
2 * Copyright 2002-2012 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 * http://drewnoakes.com/code/exif/
19 * http://code.google.com/p/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 http://drewnoakes.com
40 */
41public abstract class Directory
42{
43 // TODO get Array methods need to return cloned data, to maintain this directory's integrity
44
45 /** Map of values hashed by type identifiers. */
46 @NotNull
47 protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
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 */
54 @NotNull
55 protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
56
57 @NotNull
58 private final Collection<String> _errorList = new ArrayList<String>(4);
59
60 /** The descriptor used to interpret tag values. */
61 protected TagDescriptor _descriptor;
62
63// ABSTRACT METHODS
64
65 /**
66 * Provides the name of the directory, for display purposes. E.g. <code>Exif</code>
67 *
68 * @return the name of the directory
69 */
70 @NotNull
71 public abstract String getName();
72
73 /**
74 * Provides the map of tag names, hashed by tag type identifier.
75 *
76 * @return the map of tag names
77 */
78 @NotNull
79 protected abstract HashMap<Integer, String> getTagNameMap();
80
81 protected Directory()
82 {}
83
84// VARIOUS METHODS
85
86 /**
87 * Indicates whether the specified tag type has been set.
88 *
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 */
92 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
93 public boolean containsTag(int tagType)
94 {
95 return _tagMap.containsKey(Integer.valueOf(tagType));
96 }
97
98 /**
99 * Returns an Iterator of Tag instances that have been set in this Directory.
100 *
101 * @return an Iterator of Tag instances
102 */
103 @NotNull
104 public Collection<Tag> getTags()
105 {
106 return _definedTagList;
107 }
108
109 /**
110 * Returns the number of tags set in this Directory.
111 *
112 * @return the number of tags set in this Directory
113 */
114 public int getTagCount()
115 {
116 return _definedTagList.size();
117 }
118
119 /**
120 * Sets the descriptor used to interpret tag values.
121 *
122 * @param descriptor the descriptor used to interpret tag values
123 */
124 @java.lang.SuppressWarnings({ "ConstantConditions" })
125 public void setDescriptor(@NotNull TagDescriptor descriptor)
126 {
127 if (descriptor == null)
128 throw new NullPointerException("cannot set a null descriptor");
129 _descriptor = descriptor;
130 }
131
132 /**
133 * Registers an error message with this directory.
134 *
135 * @param message an error message.
136 */
137 public void addError(@NotNull String message)
138 {
139 _errorList.add(message);
140 }
141
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 */
147 public boolean hasErrors()
148 {
149 return _errorList.size() > 0;
150 }
151
152 /**
153 * Used to iterate over any error messages contained in this directory.
154 *
155 * @return an iterable collection of error message strings.
156 */
157 @NotNull
158 public Iterable<String> getErrors()
159 {
160 return _errorList;
161 }
162
163 /** Returns the count of error messages in this directory. */
164 public int getErrorCount()
165 {
166 return _errorList.size();
167 }
168
169// TAG SETTERS
170
171 /**
172 * Sets an <code>int</code> value for the specified tag.
173 *
174 * @param tagType the tag's value as an int
175 * @param value the value for the specified tag as an int
176 */
177 public void setInt(int tagType, int value)
178 {
179 setObject(tagType, value);
180 }
181
182 /**
183 * Sets an <code>int[]</code> (array) for the specified tag.
184 *
185 * @param tagType the tag identifier
186 * @param ints the int array to store
187 */
188 public void setIntArray(int tagType, @NotNull int[] ints)
189 {
190 setObjectArray(tagType, ints);
191 }
192
193 /**
194 * Sets a <code>float</code> value for the specified tag.
195 *
196 * @param tagType the tag's value as an int
197 * @param value the value for the specified tag as a float
198 */
199 public void setFloat(int tagType, float value)
200 {
201 setObject(tagType, value);
202 }
203
204 /**
205 * Sets a <code>float[]</code> (array) for the specified tag.
206 *
207 * @param tagType the tag identifier
208 * @param floats the float array to store
209 */
210 public void setFloatArray(int tagType, @NotNull float[] floats)
211 {
212 setObjectArray(tagType, floats);
213 }
214
215 /**
216 * Sets a <code>double</code> value for the specified tag.
217 *
218 * @param tagType the tag's value as an int
219 * @param value the value for the specified tag as a double
220 */
221 public void setDouble(int tagType, double value)
222 {
223 setObject(tagType, value);
224 }
225
226 /**
227 * Sets a <code>double[]</code> (array) for the specified tag.
228 *
229 * @param tagType the tag identifier
230 * @param doubles the double array to store
231 */
232 public void setDoubleArray(int tagType, @NotNull double[] doubles)
233 {
234 setObjectArray(tagType, doubles);
235 }
236
237 /**
238 * Sets a <code>String</code> value for the specified tag.
239 *
240 * @param tagType the tag's value as an int
241 * @param value the value for the specified tag as a String
242 */
243 @java.lang.SuppressWarnings({ "ConstantConditions" })
244 public void setString(int tagType, @NotNull String value)
245 {
246 if (value == null)
247 throw new NullPointerException("cannot set a null String");
248 setObject(tagType, value);
249 }
250
251 /**
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 *
265 * @param tagType the tag's value as an int
266 * @param value the value for the specified tag as a boolean
267 */
268 public void setBoolean(int tagType, boolean value)
269 {
270 setObject(tagType, value);
271 }
272
273 /**
274 * Sets a <code>long</code> value for the specified tag.
275 *
276 * @param tagType the tag's value as an int
277 * @param value the value for the specified tag as a long
278 */
279 public void setLong(int tagType, long value)
280 {
281 setObject(tagType, value);
282 }
283
284 /**
285 * Sets a <code>java.util.Date</code> value for the specified tag.
286 *
287 * @param tagType the tag's value as an int
288 * @param value the value for the specified tag as a java.util.Date
289 */
290 public void setDate(int tagType, @NotNull java.util.Date value)
291 {
292 setObject(tagType, value);
293 }
294
295 /**
296 * Sets a <code>Rational</code> value for the specified tag.
297 *
298 * @param tagType the tag's value as an int
299 * @param rational rational number
300 */
301 public void setRational(int tagType, @NotNull Rational rational)
302 {
303 setObject(tagType, rational);
304 }
305
306 /**
307 * Sets a <code>Rational[]</code> (array) for the specified tag.
308 *
309 * @param tagType the tag identifier
310 * @param rationals the Rational array to store
311 */
312 public void setRationalArray(int tagType, @NotNull Rational[] rationals)
313 {
314 setObjectArray(tagType, rationals);
315 }
316
317 /**
318 * Sets a <code>byte[]</code> (array) for the specified tag.
319 *
320 * @param tagType the tag identifier
321 * @param bytes the byte array to store
322 */
323 public void setByteArray(int tagType, @NotNull byte[] bytes)
324 {
325 setObjectArray(tagType, bytes);
326 }
327
328 /**
329 * Sets a <code>Object</code> for the specified tag.
330 *
331 * @param tagType the tag's value as an int
332 * @param value the value for the specified tag
333 * @throws NullPointerException if value is <code>null</code>
334 */
335 @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
336 public void setObject(int tagType, @NotNull Object value)
337 {
338 if (value == null)
339 throw new NullPointerException("cannot set a null object");
340
341 if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
342 _definedTagList.add(new Tag(tagType, this));
343 }
344// else {
345// final Object oldValue = _tagMap.get(tagType);
346// if (!oldValue.equals(value))
347// addError(String.format("Overwritten tag 0x%s (%s). Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
348// }
349 _tagMap.put(tagType, value);
350 }
351
352 /**
353 * Sets an array <code>Object</code> for the specified tag.
354 *
355 * @param tagType the tag's value as an int
356 * @param array the array of values for the specified tag
357 */
358 public void setObjectArray(int tagType, @NotNull Object array)
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 /**
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.
380 */
381 public int getInt(int tagType) throws MetadataException
382 {
383 Integer integer = getInteger(tagType);
384 if (integer!=null)
385 return integer;
386
387 Object o = getObject(tagType);
388 if (o == null)
389 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
390 throw new MetadataException("Tag '" + tagType + "' cannot be converted to int. It is of type '" + o.getClass() + "'.");
391 }
392
393 /**
394 * Returns the specified tag's value as an Integer, if possible. Every attempt to represent the tag's value as an
395 * Integer is taken. Here is a list of the action taken depending upon the tag's original type:
396 * <ul>
397 * <li> int - Return unchanged
398 * <li> Number - Return an int value (real numbers are truncated)
399 * <li> Rational - Truncate any fractional part and returns remaining int
400 * <li> String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR)
401 * <li> Rational[] - Return int value of first item in array if length &gt; 0
402 * <li> byte[] - Return int value of first item in array if length &gt; 0
403 * <li> int[] - Return int value of first item in array if length &gt; 0
404 * </ul>
405 *
406 * If the value is not found or cannot be converted to int, <code>null</code> is returned.
407 */
408 @Nullable
409 public Integer getInteger(int tagType)
410 {
411 Object o = getObject(tagType);
412
413 if (o == null)
414 return null;
415
416 if (o instanceof String) {
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 Number) {
431 return ((Number)o).intValue();
432 } else if (o instanceof Rational[]) {
433 Rational[] rationals = (Rational[])o;
434 if (rationals.length == 1)
435 return rationals[0].intValue();
436 } else if (o instanceof byte[]) {
437 byte[] bytes = (byte[])o;
438 if (bytes.length == 1)
439 return (int)bytes[0];
440 } else if (o instanceof int[]) {
441 int[] ints = (int[])o;
442 if (ints.length == 1)
443 return ints[0];
444 }
445 return null;
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[].
451 *
452 * @param tagType the tag identifier
453 * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned.
454 */
455 @Nullable
456 public String[] getStringArray(int tagType)
457 {
458 Object o = getObject(tagType);
459 if (o == null)
460 return null;
461 if (o instanceof String[])
462 return (String[])o;
463 if (o instanceof String)
464 return new String[] { (String)o };
465 if (o instanceof int[]) {
466 int[] ints = (int[])o;
467 String[] strings = new String[ints.length];
468 for (int i = 0; i < strings.length; i++)
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];
474 for (int i = 0; i < strings.length; i++)
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];
480 for (int i = 0; i < strings.length; i++)
481 strings[i] = rationals[i].toSimpleString(false);
482 return strings;
483 }
484 return null;
485 }
486
487 /**
488 * Gets the specified tag's value as an int array, if possible. Only supported
489 * where the tag is set as String, Integer, int[], byte[] or Rational[].
490 *
491 * @param tagType the tag identifier
492 * @return the tag's value as an int array
493 */
494 @Nullable
495 public int[] getIntArray(int tagType)
496 {
497 Object o = getObject(tagType);
498 if (o == null)
499 return null;
500 if (o instanceof Rational[]) {
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 int[])
509 return (int[])o;
510 if (o instanceof byte[]) {
511 byte[] bytes = (byte[])o;
512 int[] ints = new int[bytes.length];
513 for (int i = 0; i < bytes.length; i++) {
514 byte b = bytes[i];
515 ints[i] = b;
516 }
517 return ints;
518 }
519 if (o instanceof CharSequence) {
520 CharSequence str = (CharSequence)o;
521 int[] ints = new int[str.length()];
522 for (int i = 0; i < str.length(); i++) {
523 ints[i] = str.charAt(i);
524 }
525 return ints;
526 }
527 if (o instanceof Integer)
528 return new int[] { (Integer)o };
529
530 return null;
531 }
532
533 /**
534 * Gets the specified tag's value as an byte array, if possible. Only supported
535 * where the tag is set as String, Integer, int[], byte[] or Rational[].
536 *
537 * @param tagType the tag identifier
538 * @return the tag's value as a byte array
539 */
540 @Nullable
541 public byte[] getByteArray(int tagType)
542 {
543 Object o = getObject(tagType);
544 if (o == null) {
545 return null;
546 } else if (o instanceof Rational[]) {
547 Rational[] rationals = (Rational[])o;
548 byte[] bytes = new byte[rationals.length];
549 for (int i = 0; i < bytes.length; i++) {
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];
558 for (int i = 0; i < ints.length; i++) {
559 bytes[i] = (byte)ints[i];
560 }
561 return bytes;
562 } else if (o instanceof CharSequence) {
563 CharSequence str = (CharSequence)o;
564 byte[] bytes = new byte[str.length()];
565 for (int i = 0; i < str.length(); i++) {
566 bytes[i] = (byte)str.charAt(i);
567 }
568 return bytes;
569 }
570 if (o instanceof Integer)
571 return new byte[] { ((Integer)o).byteValue() };
572
573 return null;
574 }
575
576 /** Returns the specified tag's value as a double, if possible. */
577 public double getDouble(int tagType) throws MetadataException
578 {
579 Double value = getDoubleObject(tagType);
580 if (value!=null)
581 return value;
582 Object o = getObject(tagType);
583 if (o == null)
584 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
585 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double. It is of type '" + o.getClass() + "'.");
586 }
587 /** Returns the specified tag's value as a Double. If the tag is not set or cannot be converted, <code>null</code> is returned. */
588 @Nullable
589 public Double getDoubleObject(int tagType)
590 {
591 Object o = getObject(tagType);
592 if (o == null)
593 return null;
594 if (o instanceof String) {
595 try {
596 return Double.parseDouble((String)o);
597 } catch (NumberFormatException nfe) {
598 return null;
599 }
600 }
601 if (o instanceof Number)
602 return ((Number)o).doubleValue();
603
604 return null;
605 }
606
607 /** Returns the specified tag's value as a float, if possible. */
608 public float getFloat(int tagType) throws MetadataException
609 {
610 Float value = getFloatObject(tagType);
611 if (value!=null)
612 return value;
613 Object o = getObject(tagType);
614 if (o == null)
615 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
616 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float. It is of type '" + o.getClass() + "'.");
617 }
618
619 /** Returns the specified tag's value as a float. If the tag is not set or cannot be converted, <code>null</code> is returned. */
620 @Nullable
621 public Float getFloatObject(int tagType)
622 {
623 Object o = getObject(tagType);
624 if (o == null)
625 return null;
626 if (o instanceof String) {
627 try {
628 return Float.parseFloat((String)o);
629 } catch (NumberFormatException nfe) {
630 return null;
631 }
632 }
633 if (o instanceof Number)
634 return ((Number)o).floatValue();
635 return null;
636 }
637
638 /** Returns the specified tag's value as a long, if possible. */
639 public long getLong(int tagType) throws MetadataException
640 {
641 Long value = getLongObject(tagType);
642 if (value!=null)
643 return value;
644 Object o = getObject(tagType);
645 if (o == null)
646 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
647 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long. It is of type '" + o.getClass() + "'.");
648 }
649
650 /** Returns the specified tag's value as a long. If the tag is not set or cannot be converted, <code>null</code> is returned. */
651 @Nullable
652 public Long getLongObject(int tagType)
653 {
654 Object o = getObject(tagType);
655 if (o == null)
656 return null;
657 if (o instanceof String) {
658 try {
659 return Long.parseLong((String)o);
660 } catch (NumberFormatException nfe) {
661 return null;
662 }
663 }
664 if (o instanceof Number)
665 return ((Number)o).longValue();
666 return null;
667 }
668
669 /** Returns the specified tag's value as a boolean, if possible. */
670 public boolean getBoolean(int tagType) throws MetadataException
671 {
672 Boolean value = getBooleanObject(tagType);
673 if (value!=null)
674 return value;
675 Object o = getObject(tagType);
676 if (o == null)
677 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
678 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean. It is of type '" + o.getClass() + "'.");
679 }
680
681 /** Returns the specified tag's value as a boolean. If the tag is not set or cannot be converted, <code>null</code> is returned. */
682 @Nullable
683 @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
684 public Boolean getBooleanObject(int tagType)
685 {
686 Object o = getObject(tagType);
687 if (o == null)
688 return null;
689 if (o instanceof Boolean)
690 return (Boolean)o;
691 if (o instanceof String) {
692 try {
693 return Boolean.getBoolean((String)o);
694 } catch (NumberFormatException nfe) {
695 return null;
696 }
697 }
698 if (o instanceof Number)
699 return (((Number)o).doubleValue() != 0);
700 return null;
701 }
702
703 /**
704 * Returns the specified tag's value as a java.util.Date. If the value is unset or cannot be converted, <code>null</code> is returned.
705 * <p/>
706 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
707 * the current {@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument.
708 */
709 @Nullable
710 public java.util.Date getDate(int tagType)
711 {
712 return getDate(tagType, null);
713 }
714
715 /**
716 * Returns the specified tag's value as a java.util.Date. If the value is unset or cannot be converted, <code>null</code> is returned.
717 * <p/>
718 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
719 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter
720 * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect.
721 */
722 @Nullable
723 public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone)
724 {
725 Object o = getObject(tagType);
726
727 if (o == null)
728 return null;
729
730 if (o instanceof java.util.Date)
731 return (java.util.Date)o;
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
736 String datePatterns[] = {
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" };
743 String dateString = (String)o;
744 for (String datePattern : datePatterns) {
745 try {
746 DateFormat parser = new SimpleDateFormat(datePattern);
747 if (timeZone != null)
748 parser.setTimeZone(timeZone);
749 return parser.parse(dateString);
750 } catch (ParseException ex) {
751 // simply try the next pattern
752 }
753 }
754 }
755 return null;
756 }
757
758 /** Returns the specified tag's value as a Rational. If the value is unset or cannot be converted, <code>null</code> is returned. */
759 @Nullable
760 public Rational getRational(int tagType)
761 {
762 Object o = getObject(tagType);
763
764 if (o == null)
765 return null;
766
767 if (o instanceof Rational)
768 return (Rational)o;
769 if (o instanceof Integer)
770 return new Rational((Integer)o, 1);
771 if (o instanceof Long)
772 return new Rational((Long)o, 1);
773
774 // NOTE not doing conversions for real number types
775
776 return null;
777 }
778
779 /** Returns the specified tag's value as an array of Rational. If the value is unset or cannot be converted, <code>null</code> is returned. */
780 @Nullable
781 public Rational[] getRationalArray(int tagType)
782 {
783 Object o = getObject(tagType);
784 if (o == null)
785 return null;
786
787 if (o instanceof Rational[])
788 return (Rational[])o;
789
790 return null;
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.
796 *
797 * @return the String representation of the tag's value, or
798 * <code>null</code> if the tag hasn't been defined.
799 */
800 @Nullable
801 public String getString(int tagType)
802 {
803 Object o = getObject(tagType);
804 if (o == null)
805 return null;
806
807 if (o instanceof Rational)
808 return ((Rational)o).toSimpleString(true);
809
810 if (o.getClass().isArray()) {
811 // handle arrays of objects and primitives
812 int arrayLength = Array.getLength(o);
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(' ');
824 if (isObjectArray)
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));
836 else
837 addError("Unexpected array component type: " + componentType.getName());
838 }
839 return string.toString();
840 }
841
842 // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
843 // the actual data within the file. It is not inconceivable that whitespace may be significant here, so we
844 // do not trim. Also, if support is added for writing data back to files, this may cause issues.
845 // We leave trimming to the presentation layer.
846 return o.toString();
847 }
848
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
862 /**
863 * Returns the object hashed for the particular tag type specified, if available.
864 *
865 * @param tagType the tag type identifier
866 * @return the tag's value as an Object if available, else <code>null</code>
867 */
868 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
869 @Nullable
870 public Object getObject(int tagType)
871 {
872 return _tagMap.get(Integer.valueOf(tagType));
873 }
874
875// OTHER METHODS
876
877 /**
878 * Returns the name of a specified tag as a String.
879 *
880 * @param tagType the tag type identifier
881 * @return the tag's name as a String
882 */
883 @NotNull
884 public String getTagName(int tagType)
885 {
886 HashMap<Integer, String> nameMap = getTagNameMap();
887 if (!nameMap.containsKey(tagType)) {
888 String hex = Integer.toHexString(tagType);
889 while (hex.length() < 4) {
890 hex = "0" + hex;
891 }
892 return "Unknown tag (0x" + hex + ")";
893 }
894 return nameMap.get(tagType);
895 }
896
897 /**
898 * Provides a description of a tag's value using the descriptor set by
899 * <code>setDescriptor(Descriptor)</code>.
900 *
901 * @param tagType the tag type identifier
902 * @return the tag value's description as a String
903 */
904 @Nullable
905 public String getDescription(int tagType)
906 {
907 assert(_descriptor != null);
908 return _descriptor.getDescription(tagType);
909 }
910}
Note: See TracBrowser for help on using the repository browser.