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

Last change on this file since 4553 was 4231, checked in by stoecker, 13 years ago

add signpost and metadata extractor code to repository directly

File size: 24.2 KB
Line 
1/*
2 * This is public domain software - that is, you can do whatever you want
3 * with it, and include it software that is licensed under the GNU or the
4 * BSD license, or whatever other licence you choose, including proprietary
5 * closed source licenses. I do ask that you leave this header in tact.
6 *
7 * If you make modifications to this code that you think would benefit the
8 * wider community, please send me a copy and I'll post it on my site.
9 *
10 * If you make use of this code, I'd appreciate hearing about it.
11 * drew@drewnoakes.com
12 * Latest version of this software kept at
13 * http://drewnoakes.com/
14 *
15 * Created by dnoakes on 25-Nov-2002 20:30:39 using IntelliJ IDEA.
16 */
17package com.drew.metadata;
18
19import com.drew.lang.Rational;
20
21import java.io.Serializable;
22import java.lang.reflect.Array;
23import java.text.DateFormat;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.Iterator;
27import java.util.List;
28
29/**
30 * Base class for all Metadata directory types with supporting methods for setting and
31 * getting tag values.
32 */
33public abstract class Directory implements Serializable
34{
35 /**
36 * Map of values hashed by type identifiers.
37 */
38 protected final HashMap _tagMap;
39
40 /**
41 * The descriptor used to interperet tag values.
42 */
43 protected TagDescriptor _descriptor;
44
45 /**
46 * A convenient list holding tag values in the order in which they were stored.
47 * This is used for creation of an iterator, and for counting the number of
48 * defined tags.
49 */
50 protected final List _definedTagList;
51
52 private List _errorList;
53
54// ABSTRACT METHODS
55
56 /**
57 * Provides the name of the directory, for display purposes. E.g. <code>Exif</code>
58 * @return the name of the directory
59 */
60 public abstract String getName();
61
62 /**
63 * Provides the map of tag names, hashed by tag type identifier.
64 * @return the map of tag names
65 */
66 protected abstract HashMap getTagNameMap();
67
68// CONSTRUCTORS
69
70 /**
71 * Creates a new Directory.
72 */
73 public Directory()
74 {
75 _tagMap = new HashMap();
76 _definedTagList = new ArrayList();
77 }
78
79// VARIOUS METHODS
80
81 /**
82 * Indicates whether the specified tag type has been set.
83 * @param tagType the tag type to check for
84 * @return true if a value exists for the specified tag type, false if not
85 */
86 public boolean containsTag(int tagType)
87 {
88 return _tagMap.containsKey(new Integer(tagType));
89 }
90
91 /**
92 * Returns an Iterator of Tag instances that have been set in this Directory.
93 * @return an Iterator of Tag instances
94 */
95 public Iterator getTagIterator()
96 {
97 return _definedTagList.iterator();
98 }
99
100 /**
101 * Returns the number of tags set in this Directory.
102 * @return the number of tags set in this Directory
103 */
104 public int getTagCount()
105 {
106 return _definedTagList.size();
107 }
108
109 /**
110 * Sets the descriptor used to interperet tag values.
111 * @param descriptor the descriptor used to interperet tag values
112 */
113 public void setDescriptor(TagDescriptor descriptor)
114 {
115 if (descriptor==null) {
116 throw new NullPointerException("cannot set a null descriptor");
117 }
118 _descriptor = descriptor;
119 }
120
121 public void addError(String message)
122 {
123 if (_errorList==null) {
124 _errorList = new ArrayList();
125 }
126 _errorList.add(message);
127 }
128
129 public boolean hasErrors()
130 {
131 return (_errorList!=null && _errorList.size()>0);
132 }
133
134 public Iterator getErrors()
135 {
136 return _errorList.iterator();
137 }
138
139 public int getErrorCount()
140 {
141 return _errorList.size();
142 }
143
144// TAG SETTERS
145
146 /**
147 * Sets an int value for the specified tag.
148 * @param tagType the tag's value as an int
149 * @param value the value for the specified tag as an int
150 */
151 public void setInt(int tagType, int value)
152 {
153 setObject(tagType, new Integer(value));
154 }
155
156 /**
157 * Sets a double value for the specified tag.
158 * @param tagType the tag's value as an int
159 * @param value the value for the specified tag as a double
160 */
161 public void setDouble(int tagType, double value)
162 {
163 setObject(tagType, new Double(value));
164 }
165
166 /**
167 * Sets a float value for the specified tag.
168 * @param tagType the tag's value as an int
169 * @param value the value for the specified tag as a float
170 */
171 public void setFloat(int tagType, float value)
172 {
173 setObject(tagType, new Float(value));
174 }
175
176 /**
177 * Sets an int value for the specified tag.
178 * @param tagType the tag's value as an int
179 * @param value the value for the specified tag as a String
180 */
181 public void setString(int tagType, String value)
182 {
183 setObject(tagType, value);
184 }
185
186 /**
187 * Sets an int value for the specified tag.
188 * @param tagType the tag's value as an int
189 * @param value the value for the specified tag as a boolean
190 */
191 public void setBoolean(int tagType, boolean value)
192 {
193 setObject(tagType, new Boolean(value));
194 }
195
196 /**
197 * Sets a long value for the specified tag.
198 * @param tagType the tag's value as an int
199 * @param value the value for the specified tag as a long
200 */
201 public void setLong(int tagType, long value)
202 {
203 setObject(tagType, new Long(value));
204 }
205
206 /**
207 * Sets a java.util.Date value for the specified tag.
208 * @param tagType the tag's value as an int
209 * @param value the value for the specified tag as a java.util.Date
210 */
211 public void setDate(int tagType, java.util.Date value)
212 {
213 setObject(tagType, value);
214 }
215
216 /**
217 * Sets a Rational value for the specified tag.
218 * @param tagType the tag's value as an int
219 * @param rational rational number
220 */
221 public void setRational(int tagType, Rational rational)
222 {
223 setObject(tagType, rational);
224 }
225
226 /**
227 * Sets a Rational array for the specified tag.
228 * @param tagType the tag identifier
229 * @param rationals the Rational array to store
230 */
231 public void setRationalArray(int tagType, Rational[] rationals)
232 {
233 setObjectArray(tagType, rationals);
234 }
235
236 /**
237 * Sets an int array for the specified tag.
238 * @param tagType the tag identifier
239 * @param ints the int array to store
240 */
241 public void setIntArray(int tagType, int[] ints)
242 {
243 setObjectArray(tagType, ints);
244 }
245
246 /**
247 * Sets a byte array for the specified tag.
248 * @param tagType the tag identifier
249 * @param bytes the byte array to store
250 */
251 public void setByteArray(int tagType, byte[] bytes)
252 {
253 setObjectArray(tagType, bytes);
254 }
255
256 /**
257 * Sets a String array for the specified tag.
258 * @param tagType the tag identifier
259 * @param strings the String array to store
260 */
261 public void setStringArray(int tagType, String[] strings)
262 {
263 setObjectArray(tagType, strings);
264 }
265
266 /**
267 * Private helper method, containing common functionality for all 'add'
268 * methods.
269 * @param tagType the tag's value as an int
270 * @param value the value for the specified tag
271 * @throws NullPointerException if value is <code>null</code>
272 */
273 public void setObject(int tagType, Object value)
274 {
275 if (value==null) {
276 throw new NullPointerException("cannot set a null object");
277 }
278
279 Integer key = new Integer(tagType);
280 if (!_tagMap.containsKey(key)) {
281 _definedTagList.add(new Tag(tagType, this));
282 }
283 _tagMap.put(key, value);
284 }
285
286 /**
287 * Private helper method, containing common functionality for all 'add...Array'
288 * methods.
289 * @param tagType the tag's value as an int
290 * @param array the array of values for the specified tag
291 */
292 public void setObjectArray(int tagType, Object array)
293 {
294 // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
295 setObject(tagType, array);
296 }
297
298// TAG GETTERS
299
300 /**
301 * Returns the specified tag's value as an int, if possible.
302 */
303 public int getInt(int tagType) throws MetadataException
304 {
305 Object o = getObject(tagType);
306 if (o==null) {
307 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
308 } else if (o instanceof String) {
309 try {
310 return Integer.parseInt((String)o);
311 } catch (NumberFormatException nfe) {
312 // convert the char array to an int
313 String s = (String)o;
314 byte[] bytes = s.getBytes();
315 long val = 0;
316 for (int i = 0; i < bytes.length; i++) {
317 val = val << 8;
318 val += bytes[i];
319 }
320 return (int)val;
321 }
322 } else if (o instanceof Number) {
323 return ((Number)o).intValue();
324 } else if (o instanceof Rational[]) {
325 Rational[] rationals = (Rational[])o;
326 if (rationals.length==1)
327 return rationals[0].intValue();
328 } else if (o instanceof byte[]) {
329 byte[] bytes = (byte[])o;
330 if (bytes.length==1)
331 return bytes[0];
332 } else if (o instanceof int[]) {
333 int[] ints = (int[])o;
334 if (ints.length==1)
335 return ints[0];
336 }
337 throw new MetadataException("Tag '" + tagType + "' cannot be cast to int. It is of type '" + o.getClass() + "'.");
338 }
339
340 // TODO get Array methods need to return cloned data, to maintain this directory's integrity
341
342 /**
343 * Gets the specified tag's value as a String array, if possible. Only supported
344 * where the tag is set as String[], String, int[], byte[] or Rational[].
345 * @param tagType the tag identifier
346 * @return the tag's value as an array of Strings
347 * @throws MetadataException if the tag has not been set or cannot be represented
348 * as a String[]
349 */
350 public String[] getStringArray(int tagType) throws MetadataException
351 {
352 Object o = getObject(tagType);
353 if (o==null) {
354 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
355 } else if (o instanceof String[]) {
356 return (String[])o;
357 } else if (o instanceof String) {
358 String[] strings = {(String)o};
359 return strings;
360 } else if (o instanceof int[]) {
361 int[] ints = (int[])o;
362 String[] strings = new String[ints.length];
363 for (int i = 0; i<strings.length; i++) {
364 strings[i] = Integer.toString(ints[i]);
365 }
366 return strings;
367 } else if (o instanceof byte[]) {
368 byte[] bytes = (byte[])o;
369 String[] strings = new String[bytes.length];
370 for (int i = 0; i<strings.length; i++) {
371 strings[i] = Byte.toString(bytes[i]);
372 }
373 return strings;
374 } else if (o instanceof Rational[]) {
375 Rational[] rationals = (Rational[])o;
376 String[] strings = new String[rationals.length];
377 for (int i = 0; i<strings.length; i++) {
378 strings[i] = rationals[i].toSimpleString(false);
379 }
380 return strings;
381 }
382 throw new MetadataException("Tag '" + tagType + "' cannot be cast to an String array. It is of type '" + o.getClass() + "'.");
383 }
384
385 /**
386 * Gets the specified tag's value as an int array, if possible. Only supported
387 * where the tag is set as String, int[], byte[] or Rational[].
388 * @param tagType the tag identifier
389 * @return the tag's value as an int array
390 * @throws MetadataException if the tag has not been set, or cannot be converted to
391 * an int array
392 */
393 public int[] getIntArray(int tagType) throws MetadataException
394 {
395 Object o = getObject(tagType);
396 if (o==null) {
397 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
398 } else if (o instanceof Rational[]) {
399 Rational[] rationals = (Rational[])o;
400 int[] ints = new int[rationals.length];
401 for (int i = 0; i<ints.length; i++) {
402 ints[i] = rationals[i].intValue();
403 }
404 return ints;
405 } else if (o instanceof int[]) {
406 return (int[])o;
407 } else if (o instanceof byte[]) {
408 byte[] bytes = (byte[])o;
409 int[] ints = new int[bytes.length];
410 for (int i = 0; i<bytes.length; i++) {
411 byte b = bytes[i];
412 ints[i] = b;
413 }
414 return ints;
415 } else if (o instanceof String) {
416 String str = (String)o;
417 int[] ints = new int[str.length()];
418 for (int i = 0; i<str.length(); i++) {
419 ints[i] = str.charAt(i);
420 }
421 return ints;
422 }
423 throw new MetadataException("Tag '" + tagType + "' cannot be cast to an int array. It is of type '" + o.getClass() + "'.");
424 }
425
426 /**
427 * Gets the specified tag's value as an byte array, if possible. Only supported
428 * where the tag is set as String, int[], byte[] or Rational[].
429 * @param tagType the tag identifier
430 * @return the tag's value as a byte array
431 * @throws MetadataException if the tag has not been set, or cannot be converted to
432 * a byte array
433 */
434 public byte[] getByteArray(int tagType) throws MetadataException
435 {
436 Object o = getObject(tagType);
437 if (o==null) {
438 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
439 } else if (o instanceof Rational[]) {
440 Rational[] rationals = (Rational[])o;
441 byte[] bytes = new byte[rationals.length];
442 for (int i = 0; i<bytes.length; i++) {
443 bytes[i] = rationals[i].byteValue();
444 }
445 return bytes;
446 } else if (o instanceof byte[]) {
447 return (byte[])o;
448 } else if (o instanceof int[]) {
449 int[] ints = (int[])o;
450 byte[] bytes = new byte[ints.length];
451 for (int i = 0; i<ints.length; i++) {
452 bytes[i] = (byte)ints[i];
453 }
454 return bytes;
455 } else if (o instanceof String) {
456 String str = (String)o;
457 byte[] bytes = new byte[str.length()];
458 for (int i = 0; i<str.length(); i++) {
459 bytes[i] = (byte)str.charAt(i);
460 }
461 return bytes;
462 }
463 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a byte array. It is of type '" + o.getClass() + "'.");
464 }
465
466 /**
467 * Returns the specified tag's value as a double, if possible.
468 */
469 public double getDouble(int tagType) throws MetadataException
470 {
471 Object o = getObject(tagType);
472 if (o==null) {
473 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
474 } else if (o instanceof String) {
475 try {
476 return Double.parseDouble((String)o);
477 } catch (NumberFormatException nfe) {
478 throw new MetadataException("unable to parse string " + o + " as a double", nfe);
479 }
480 } else if (o instanceof Number) {
481 return ((Number)o).doubleValue();
482 }
483 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a double. It is of type '" + o.getClass() + "'.");
484 }
485
486 /**
487 * Returns the specified tag's value as a float, if possible.
488 */
489 public float getFloat(int tagType) throws MetadataException
490 {
491 Object o = getObject(tagType);
492 if (o==null) {
493 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
494 } else if (o instanceof String) {
495 try {
496 return Float.parseFloat((String)o);
497 } catch (NumberFormatException nfe) {
498 throw new MetadataException("unable to parse string " + o + " as a float", nfe);
499 }
500 } else if (o instanceof Number) {
501 return ((Number)o).floatValue();
502 }
503 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a float. It is of type '" + o.getClass() + "'.");
504 }
505
506 /**
507 * Returns the specified tag's value as a long, if possible.
508 */
509 public long getLong(int tagType) throws MetadataException
510 {
511 Object o = getObject(tagType);
512 if (o==null) {
513 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
514 } else if (o instanceof String) {
515 try {
516 return Long.parseLong((String)o);
517 } catch (NumberFormatException nfe) {
518 throw new MetadataException("unable to parse string " + o + " as a long", nfe);
519 }
520 } else if (o instanceof Number) {
521 return ((Number)o).longValue();
522 }
523 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a long. It is of type '" + o.getClass() + "'.");
524 }
525
526 /**
527 * Returns the specified tag's value as a boolean, if possible.
528 */
529 public boolean getBoolean(int tagType) throws MetadataException
530 {
531 Object o = getObject(tagType);
532 if (o==null) {
533 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
534 } else if (o instanceof Boolean) {
535 return ((Boolean)o).booleanValue();
536 } else if (o instanceof String) {
537 try {
538 return Boolean.getBoolean((String)o);
539 } catch (NumberFormatException nfe) {
540 throw new MetadataException("unable to parse string " + o + " as a boolean", nfe);
541 }
542 } else if (o instanceof Number) {
543 return (((Number)o).doubleValue()!=0);
544 }
545 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean. It is of type '" + o.getClass() + "'.");
546 }
547
548 /**
549 * Returns the specified tag's value as a java.util.Date, if possible.
550 */
551 public java.util.Date getDate(int tagType) throws MetadataException
552 {
553 Object o = getObject(tagType);
554 if (o==null) {
555 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
556 } else if (o instanceof java.util.Date) {
557 return (java.util.Date)o;
558 } else if (o instanceof String) {
559 // add new dateformat strings to make this method even smarter
560 // so far, this seems to cover all known date strings
561 // (for example, AM and PM strings are not supported...)
562 String datePatterns[] = {
563 "yyyy:MM:dd HH:mm:ss",
564 "yyyy:MM:dd HH:mm",
565 "yyyy-MM-dd HH:mm:ss",
566 "yyyy-MM-dd HH:mm"};
567 String dateString = (String)o;
568 for (int i = 0; i<datePatterns.length; i++) {
569 try {
570 DateFormat parser = new java.text.SimpleDateFormat(datePatterns[i]);
571 return parser.parse(dateString);
572 } catch (java.text.ParseException ex) {
573 // simply try the next pattern
574 }
575 }
576 }
577 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a java.util.Date. It is of type '" + o.getClass() + "'.");
578 }
579
580 /**
581 * Returns the specified tag's value as a Rational, if possible.
582 */
583 public Rational getRational(int tagType) throws MetadataException
584 {
585 Object o = getObject(tagType);
586 if (o==null) {
587 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
588 } else if (o instanceof Rational) {
589 return (Rational)o;
590 }
591 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational. It is of type '" + o.getClass() + "'.");
592 }
593
594 public Rational[] getRationalArray(int tagType) throws MetadataException
595 {
596 Object o = getObject(tagType);
597 if (o==null) {
598 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
599 } else if (o instanceof Rational[]) {
600 return (Rational[])o;
601 }
602 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational array. It is of type '" + o.getClass() + "'.");
603 }
604
605 /**
606 * Returns the specified tag's value as a String. This value is the 'raw' value. A more presentable decoding
607 * of this value may be obtained from the corresponding Descriptor.
608 * @return the String reprensentation of the tag's value, or
609 * <code>null</code> if the tag hasn't been defined.
610 */
611 public String getString(int tagType)
612 {
613 Object o = getObject(tagType);
614 if (o==null)
615 return null;
616
617 if (o instanceof Rational)
618 return ((Rational)o).toSimpleString(true);
619
620 if (o.getClass().isArray())
621 {
622 // handle arrays of objects and primitives
623 int arrayLength = Array.getLength(o);
624 // determine if this is an array of objects i.e. [Lcom.drew.blah
625 boolean isObjectArray = o.getClass().toString().startsWith("class [L");
626 StringBuffer sbuffer = new StringBuffer();
627 for (int i = 0; i<arrayLength; i++)
628 {
629 if (i!=0)
630 sbuffer.append(' ');
631 if (isObjectArray)
632 sbuffer.append(Array.get(o, i).toString());
633 else
634 sbuffer.append(Array.getInt(o, i));
635 }
636 return sbuffer.toString();
637 }
638
639 return o.toString();
640 }
641
642 /**
643 * Returns the object hashed for the particular tag type specified, if available.
644 * @param tagType the tag type identifier
645 * @return the tag's value as an Object if available, else null
646 */
647 public Object getObject(int tagType)
648 {
649 return _tagMap.get(new Integer(tagType));
650 }
651
652// OTHER METHODS
653
654 /**
655 * Returns the name of a specified tag as a String.
656 * @param tagType the tag type identifier
657 * @return the tag's name as a String
658 */
659 public String getTagName(int tagType)
660 {
661 Integer key = new Integer(tagType);
662 HashMap nameMap = getTagNameMap();
663 if (!nameMap.containsKey(key)) {
664 String hex = Integer.toHexString(tagType);
665 while (hex.length()<4) {
666 hex = "0" + hex;
667 }
668 return "Unknown tag (0x" + hex + ")";
669 }
670 return (String)nameMap.get(key);
671 }
672
673 /**
674 * Provides a description of a tag's value using the descriptor set by
675 * <code>setDescriptor(Descriptor)</code>.
676 * @param tagType the tag type identifier
677 * @return the tag value's description as a String
678 * @throws MetadataException if a descriptor hasn't been set, or if an error
679 * occurs during calculation of the description within the Descriptor
680 */
681 public String getDescription(int tagType) throws MetadataException
682 {
683 if (_descriptor==null) {
684 throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided");
685 }
686
687 return _descriptor.getDescription(tagType);
688 }
689}
Note: See TracBrowser for help on using the repository browser.