Changeset 10862 in josm for trunk/src/com/drew/metadata/Directory.java
- Timestamp:
- 2016-08-20T20:58:03+02:00 (10 years ago)
- File:
-
- 1 edited
-
trunk/src/com/drew/metadata/Directory.java (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/metadata/Directory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 24 24 import com.drew.lang.annotations.NotNull; 25 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.lang.annotations.SuppressWarnings;27 26 28 27 import java.io.UnsupportedEncodingException; 29 28 import java.lang.reflect.Array; 30 29 import java.text.DateFormat; 30 import java.text.DecimalFormat; 31 31 import java.text.ParseException; 32 32 import java.text.SimpleDateFormat; 33 33 import java.util.*; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 34 36 35 37 /** … … 41 43 public abstract class Directory 42 44 { 45 private static final DecimalFormat _floatFormat = new DecimalFormat("0.###"); 46 43 47 /** Map of values hashed by type identifiers. */ 44 48 @NotNull … … 59 63 protected TagDescriptor _descriptor; 60 64 65 @Nullable 66 private Directory _parent; 67 61 68 // ABSTRACT METHODS 62 69 … … 171 178 { 172 179 return _errorList.size(); 180 } 181 182 @Nullable 183 public Directory getParent() 184 { 185 return _parent; 186 } 187 188 public void setParent(@NotNull Directory parent) 189 { 190 _parent = parent; 173 191 } 174 192 … … 701 719 /** 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 720 @Nullable 703 @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")704 721 public Boolean getBooleanObject(int tagType) 705 722 { … … 725 742 * <p> 726 743 * 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.744 * the GMT {@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument. 728 745 */ 729 746 @Nullable 730 747 public java.util.Date getDate(int tagType) 731 748 { 732 return getDate(tagType, null); 749 return getDate(tagType, null, null); 733 750 } 734 751 … … 738 755 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 739 756 * 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.757 * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect. 741 758 */ 742 759 @Nullable 743 760 public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone) 744 761 { 745 Object o = getObject(tagType); 746 747 if (o == null) 748 return null; 762 return getDate(tagType, null, timeZone); 763 } 764 765 /** 766 * 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. 767 * <p> 768 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 769 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter 770 * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect. 771 * In addition, the {@code subsecond} parameter, which specifies the number of digits after the decimal point in the seconds, 772 * is set to the returned Date. This parameter is only considered if the underlying value is a string and is has 773 * no subsecond information, otherwise it has no effect. 774 * 775 * @param tagType the tag identifier 776 * @param subsecond the subsecond value for the Date 777 * @param timeZone the time zone to use 778 * @return a Date representing the time value 779 */ 780 @Nullable 781 public java.util.Date getDate(int tagType, @Nullable String subsecond, @Nullable TimeZone timeZone) 782 { 783 Object o = getObject(tagType); 749 784 750 785 if (o instanceof java.util.Date) 751 786 return (java.util.Date)o; 752 787 788 java.util.Date date = null; 789 753 790 if (o instanceof String) { 754 // This seems to cover all known Exif date strings 791 // This seems to cover all known Exif and Xmp date strings 755 792 // 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 793 String datePatterns[] = { … … 760 797 "yyyy-MM-dd HH:mm", 761 798 "yyyy.MM.dd HH:mm:ss", 762 "yyyy.MM.dd HH:mm" }; 799 "yyyy.MM.dd HH:mm", 800 "yyyy-MM-dd'T'HH:mm:ss", 801 "yyyy-MM-dd'T'HH:mm", 802 "yyyy-MM-dd", 803 "yyyy-MM", 804 "yyyy" }; 763 805 String dateString = (String)o; 806 807 // if the date string has subsecond information, it supersedes the subsecond parameter 808 Pattern subsecondPattern = Pattern.compile("(\\d\\d:\\d\\d:\\d\\d)(\\.\\d+)"); 809 Matcher subsecondMatcher = subsecondPattern.matcher(dateString); 810 if (subsecondMatcher.find()) { 811 subsecond = subsecondMatcher.group(2).substring(1); 812 dateString = subsecondMatcher.replaceAll("$1"); 813 } 814 815 // if the date string has time zone information, it supersedes the timeZone parameter 816 Pattern timeZonePattern = Pattern.compile("(Z|[+-]\\d\\d:\\d\\d)$"); 817 Matcher timeZoneMatcher = timeZonePattern.matcher(dateString); 818 if (timeZoneMatcher.find()) { 819 timeZone = TimeZone.getTimeZone("GMT" + timeZoneMatcher.group().replaceAll("Z", "")); 820 dateString = timeZoneMatcher.replaceAll(""); 821 } 822 764 823 for (String datePattern : datePatterns) { 765 824 try { … … 770 829 parser.setTimeZone(TimeZone.getTimeZone("GMT")); // don't interpret zone time 771 830 772 return parser.parse(dateString); 831 date = parser.parse(dateString); 832 break; 773 833 } catch (ParseException ex) { 774 834 // simply try the next pattern … … 776 836 } 777 837 } 778 return null; 838 839 if (date == null) 840 return null; 841 842 if (subsecond == null) 843 return date; 844 845 try { 846 int millisecond = (int) (Double.parseDouble("." + subsecond) * 1000); 847 if (millisecond >= 0 && millisecond < 1000) { 848 Calendar calendar = Calendar.getInstance(); 849 calendar.setTime(date); 850 calendar.set(Calendar.MILLISECOND, millisecond); 851 return calendar.getTime(); 852 } 853 return date; 854 } catch (NumberFormatException e) { 855 return date; 856 } 779 857 } 780 858 … … 835 913 int arrayLength = Array.getLength(o); 836 914 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"); 915 844 916 StringBuilder string = new StringBuilder(); 845 for (int i = 0; i < arrayLength; i++) { 846 if (i != 0) 847 string.append(' '); 848 if (isObjectArray) 917 918 if (Object.class.isAssignableFrom(componentType)) { 919 // object array 920 for (int i = 0; i < arrayLength; i++) { 921 if (i != 0) 922 string.append(' '); 849 923 string.append(Array.get(o, i).toString()); 850 else if (isIntArray) 924 } 925 } else if (componentType.getName().equals("int")) { 926 for (int i = 0; i < arrayLength; i++) { 927 if (i != 0) 928 string.append(' '); 851 929 string.append(Array.getInt(o, i)); 852 else if (isShortArray) 930 } 931 } else if (componentType.getName().equals("short")) { 932 for (int i = 0; i < arrayLength; i++) { 933 if (i != 0) 934 string.append(' '); 853 935 string.append(Array.getShort(o, i)); 854 else if (isLongArray) 936 } 937 } else if (componentType.getName().equals("long")) { 938 for (int i = 0; i < arrayLength; i++) { 939 if (i != 0) 940 string.append(' '); 855 941 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 } 942 } 943 } else if (componentType.getName().equals("float")) { 944 for (int i = 0; i < arrayLength; i++) { 945 if (i != 0) 946 string.append(' '); 947 string.append(_floatFormat.format(Array.getFloat(o, i))); 948 } 949 } else if (componentType.getName().equals("double")) { 950 for (int i = 0; i < arrayLength; i++) { 951 if (i != 0) 952 string.append(' '); 953 string.append(_floatFormat.format(Array.getDouble(o, i))); 954 } 955 } else if (componentType.getName().equals("byte")) { 956 for (int i = 0; i < arrayLength; i++) { 957 if (i != 0) 958 string.append(' '); 959 string.append(Array.getByte(o, i) & 0xff); 960 } 961 } else { 962 addError("Unexpected array component type: " + componentType.getName()); 963 } 964 865 965 return string.toString(); 866 966 } 967 968 if (o instanceof Double) 969 return _floatFormat.format(((Double)o).doubleValue()); 970 971 if (o instanceof Float) 972 return _floatFormat.format(((Float)o).floatValue()); 867 973 868 974 // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
Note:
See TracChangeset
for help on using the changeset viewer.
