73 *
74 * @see ResourceBundle#getBundle(String,Locale)
75 *
76 */
77 public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
78
79 /**
80 * A <code>String</code> constant containing the standard format
81 * name, <code>"javax_imageio_1.0"</code>.
82 */
83 public static final String standardMetadataFormatName =
84 "javax_imageio_1.0";
85
86 private static IIOMetadataFormat standardFormat = null;
87
88 private String resourceBaseName = this.getClass().getName() + "Resources";
89
90 private String rootName;
91
92 // Element name (String) -> Element
93 private HashMap elementMap = new HashMap();
94
95 class Element {
96 String elementName;
97
98 int childPolicy;
99 int minChildren = 0;
100 int maxChildren = 0;
101
102 // Child names (Strings)
103 List childList = new ArrayList();
104
105 // Parent names (Strings)
106 List parentList = new ArrayList();
107
108 // List of attribute names in the order they were added
109 List attrList = new ArrayList();
110 // Attr name (String) -> Attribute
111 Map attrMap = new HashMap();
112
113 ObjectValue objectValue;
114 }
115
116 class Attribute {
117 String attrName;
118
119 int valueType = VALUE_ARBITRARY;
120 int dataType;
121 boolean required;
122 String defaultValue = null;
123
124 // enumeration
125 List enumeratedValues;
126
127 // range
128 String minValue;
129 String maxValue;
130
131 // list
132 int listMinLength;
133 int listMaxLength;
134 }
135
136 class ObjectValue {
137 int valueType = VALUE_NONE;
138 Class classType = null;
139 Object defaultValue = null;
140
141 // Meaningful only if valueType == VALUE_ENUMERATION
142 List enumeratedValues = null;
143
144 // Meaningful only if valueType == VALUE_RANGE
145 Comparable minValue = null;
146 Comparable maxValue = null;
147
148 // Meaningful only if valueType == VALUE_LIST
149 int arrayMinLength = 0;
150 int arrayMaxLength = 0;
151 }
152
153 /**
154 * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
155 * with a given root element name and child policy (other than
156 * <code>CHILD_POLICY_REPEAT</code>). Additional elements, and
157 * their attributes and <code>Object</code> reference information
158 * may be added using the various <code>add</code> methods.
159 *
160 * @param rootName the name of the root element.
161 * @param childPolicy one of the <code>CHILD_POLICY_*</code> constants,
162 * other than <code>CHILD_POLICY_REPEAT</code>.
163 *
164 * @exception IllegalArgumentException if <code>rootName</code> is
165 * <code>null</code>.
166 * @exception IllegalArgumentException if <code>childPolicy</code> is
255 *
256 * @return a <code>String</code> containing the base name.
257 *
258 * @see #setResourceBaseName
259 */
260 protected String getResourceBaseName() {
261 return resourceBaseName;
262 }
263
264 /**
265 * Utility method for locating an element.
266 *
267 * @param mustAppear if <code>true</code>, throw an
268 * <code>IllegalArgumentException</code> if no such node exists;
269 * if <code>false</code>, just return null.
270 */
271 private Element getElement(String elementName, boolean mustAppear) {
272 if (mustAppear && (elementName == null)) {
273 throw new IllegalArgumentException("element name is null!");
274 }
275 Element element = (Element)elementMap.get(elementName);
276 if (mustAppear && (element == null)) {
277 throw new IllegalArgumentException("No such element: " +
278 elementName);
279 }
280 return element;
281 }
282
283 private Element getElement(String elementName) {
284 return getElement(elementName, true);
285 }
286
287 // Utility method for locating an attribute
288 private Attribute getAttribute(String elementName, String attrName) {
289 Element element = getElement(elementName);
290 Attribute attr = (Attribute)element.attrMap.get(attrName);
291 if (attr == null) {
292 throw new IllegalArgumentException("No such attribute \"" +
293 attrName + "\"!");
294 }
295 return attr;
296 }
297
298 // Setup
299
300 /**
301 * Adds a new element type to this metadata document format with a
302 * child policy other than <code>CHILD_POLICY_REPEAT</code>.
303 *
304 * @param elementName the name of the new element.
305 * @param parentName the name of the element that will be the
306 * parent of the new element.
307 * @param childPolicy one of the <code>CHILD_POLICY_*</code>
308 * constants, other than <code>CHILD_POLICY_REPEAT</code>,
309 * indicating the child policy of the new element.
310 *
391 * is <code>null</code>, or is not a legal element name for this
392 * format.
393 */
394 protected void addChildElement(String elementName, String parentName) {
395 Element parent = getElement(parentName);
396 Element element = getElement(elementName);
397 parent.childList.add(elementName);
398 element.parentList.add(parentName);
399 }
400
401 /**
402 * Removes an element from the format. If no element with the
403 * given name was present, nothing happens and no exception is
404 * thrown.
405 *
406 * @param elementName the name of the element to be removed.
407 */
408 protected void removeElement(String elementName) {
409 Element element = getElement(elementName, false);
410 if (element != null) {
411 Iterator iter = element.parentList.iterator();
412 while (iter.hasNext()) {
413 String parentName = (String)iter.next();
414 Element parent = getElement(parentName, false);
415 if (parent != null) {
416 parent.childList.remove(elementName);
417 }
418 }
419 elementMap.remove(elementName);
420 }
421 }
422
423 /**
424 * Adds a new attribute to a previously defined element that may
425 * be set to an arbitrary value.
426 *
427 * @param elementName the name of the element.
428 * @param attrName the name of the attribute being added.
429 * @param dataType the data type (string format) of the attribute,
430 * one of the <code>DATATYPE_*</code> constants.
431 * @param required <code>true</code> if the attribute must be present.
432 * @param defaultValue the default value for the attribute, or
433 * <code>null</code>.
497 */
498 protected void addAttribute(String elementName,
499 String attrName,
500 int dataType,
501 boolean required,
502 String defaultValue,
503 List<String> enumeratedValues) {
504 Element element = getElement(elementName);
505 if (attrName == null) {
506 throw new IllegalArgumentException("attrName == null!");
507 }
508 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
509 throw new IllegalArgumentException("Invalid value for dataType!");
510 }
511 if (enumeratedValues == null) {
512 throw new IllegalArgumentException("enumeratedValues == null!");
513 }
514 if (enumeratedValues.size() == 0) {
515 throw new IllegalArgumentException("enumeratedValues is empty!");
516 }
517 Iterator iter = enumeratedValues.iterator();
518 while (iter.hasNext()) {
519 Object o = iter.next();
520 if (o == null) {
521 throw new IllegalArgumentException
522 ("enumeratedValues contains a null!");
523 }
524 if (!(o instanceof String)) {
525 throw new IllegalArgumentException
526 ("enumeratedValues contains a non-String value!");
527 }
528 }
529
530 Attribute attr = new Attribute();
531 attr.attrName = attrName;
532 attr.valueType = VALUE_ENUMERATION;
533 attr.dataType = dataType;
534 attr.required = required;
535 attr.defaultValue = defaultValue;
536 attr.enumeratedValues = enumeratedValues;
537
664 * <code>DATATYPE_BOOLEAN</code>.
665 *
666 * @param elementName the name of the element.
667 * @param attrName the name of the attribute being added.
668 * @param hasDefaultValue <code>true</code> if a default value
669 * should be present.
670 * @param defaultValue the default value for the attribute as a
671 * <code>boolean</code>, ignored if <code>hasDefaultValue</code>
672 * is <code>false</code>.
673 *
674 * @exception IllegalArgumentException if <code>elementName</code>
675 * is <code>null</code>, or is not a legal element name for this
676 * format.
677 * @exception IllegalArgumentException if <code>attrName</code> is
678 * <code>null</code>.
679 */
680 protected void addBooleanAttribute(String elementName,
681 String attrName,
682 boolean hasDefaultValue,
683 boolean defaultValue) {
684 List values = new ArrayList();
685 values.add("TRUE");
686 values.add("FALSE");
687
688 String dval = null;
689 if (hasDefaultValue) {
690 dval = defaultValue ? "TRUE" : "FALSE";
691 }
692 addAttribute(elementName,
693 attrName,
694 DATATYPE_BOOLEAN,
695 true,
696 dval,
697 values);
698 }
699
700 /**
701 * Removes an attribute from a previously defined element. If no
702 * attribute with the given name was present in the given element,
703 * nothing happens and no exception is thrown.
704 *
723 * <p> If an <code>Object</code> reference was previously allowed,
724 * the previous settings are overwritten.
725 *
726 * @param elementName the name of the element.
727 * @param classType a <code>Class</code> variable indicating the
728 * legal class type for the object value.
729 * @param required <code>true</code> if an object value must be present.
730 * @param defaultValue the default value for the
731 * <code>Object</code> reference, or <code>null</code>.
732 * @param <T> the type of the object.
733 *
734 * @exception IllegalArgumentException if <code>elementName</code>
735 * is <code>null</code>, or is not a legal element name for this format.
736 */
737 protected <T> void addObjectValue(String elementName,
738 Class<T> classType,
739 boolean required,
740 T defaultValue)
741 {
742 Element element = getElement(elementName);
743 ObjectValue obj = new ObjectValue();
744 obj.valueType = VALUE_ARBITRARY;
745 obj.classType = classType;
746 obj.defaultValue = defaultValue;
747
748 element.objectValue = obj;
749 }
750
751 /**
752 * Allows an <code>Object</code> reference of a given class type
753 * to be stored in nodes implementing the named element. The
754 * value of the <code>Object</code> must be one of the values
755 * given by <code>enumeratedValues</code>.
756 *
757 * <p> If an <code>Object</code> reference was previously allowed,
758 * the previous settings are overwritten.
759 *
760 * @param elementName the name of the element.
761 * @param classType a <code>Class</code> variable indicating the
762 * legal class type for the object value.
763 * @param required <code>true</code> if an object value must be present.
776 * <code>enumeratedValues</code> does not contain at least one
777 * entry.
778 * @exception IllegalArgumentException if
779 * <code>enumeratedValues</code> contains an element that is not
780 * an instance of the class type denoted by <code>classType</code>
781 * or is <code>null</code>.
782 */
783 protected <T> void addObjectValue(String elementName,
784 Class<T> classType,
785 boolean required,
786 T defaultValue,
787 List<? extends T> enumeratedValues)
788 {
789 Element element = getElement(elementName);
790 if (enumeratedValues == null) {
791 throw new IllegalArgumentException("enumeratedValues == null!");
792 }
793 if (enumeratedValues.size() == 0) {
794 throw new IllegalArgumentException("enumeratedValues is empty!");
795 }
796 Iterator iter = enumeratedValues.iterator();
797 while (iter.hasNext()) {
798 Object o = iter.next();
799 if (o == null) {
800 throw new IllegalArgumentException("enumeratedValues contains a null!");
801 }
802 if (!classType.isInstance(o)) {
803 throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
804 }
805 }
806
807 ObjectValue obj = new ObjectValue();
808 obj.valueType = VALUE_ENUMERATION;
809 obj.classType = classType;
810 obj.defaultValue = defaultValue;
811 obj.enumeratedValues = enumeratedValues;
812
813 element.objectValue = obj;
814 }
815
816 /**
817 * Allows an <code>Object</code> reference of a given class type
818 * to be stored in nodes implementing the named element. The
819 * value of the <code>Object</code> must be within the range given
820 * by <code>minValue</code> and <code>maxValue</code>.
821 * Furthermore, the class type must implement the
822 * <code>Comparable</code> interface.
823 *
824 * <p> If an <code>Object</code> reference was previously allowed,
825 * the previous settings are overwritten.
826 *
827 * @param elementName the name of the element.
837 * @param minInclusive <code>true</code> if <code>minValue</code>
838 * is inclusive.
839 * @param maxInclusive <code>true</code> if <code>maxValue</code>
840 * is inclusive.
841 * @param <T> the type of the object.
842 *
843 * @exception IllegalArgumentException if <code>elementName</code>
844 * is <code>null</code>, or is not a legal element name for this
845 * format.
846 */
847 protected <T extends Object & Comparable<? super T>> void
848 addObjectValue(String elementName,
849 Class<T> classType,
850 T defaultValue,
851 Comparable<? super T> minValue,
852 Comparable<? super T> maxValue,
853 boolean minInclusive,
854 boolean maxInclusive)
855 {
856 Element element = getElement(elementName);
857 ObjectValue obj = new ObjectValue();
858 obj.valueType = VALUE_RANGE;
859 if (minInclusive) {
860 obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
861 }
862 if (maxInclusive) {
863 obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
864 }
865 obj.classType = classType;
866 obj.defaultValue = defaultValue;
867 obj.minValue = minValue;
868 obj.maxValue = maxValue;
869
870 element.objectValue = obj;
871 }
872
873 /**
874 * Allows an <code>Object</code> reference of a given class type
875 * to be stored in nodes implementing the named element. The
876 * value of the <code>Object</code> must an array of objects of
877 * class type given by <code>classType</code>, with at least
878 * <code>arrayMinLength</code> and at most
879 * <code>arrayMaxLength</code> elements.
880 *
881 * <p> If an <code>Object</code> reference was previously allowed,
882 * the previous settings are overwritten.
883 *
884 * @param elementName the name of the element.
885 * @param classType a <code>Class</code> variable indicating the
886 * legal class type for the object value.
887 * @param arrayMinLength the smallest legal length for the array.
888 * @param arrayMaxLength the largest legal length for the array.
889 *
890 * @exception IllegalArgumentException if <code>elementName</code> is
891 * not a legal element name for this format.
892 */
893 protected void addObjectValue(String elementName,
894 Class<?> classType,
895 int arrayMinLength,
896 int arrayMaxLength) {
897 Element element = getElement(elementName);
898 ObjectValue obj = new ObjectValue();
899 obj.valueType = VALUE_LIST;
900 obj.classType = classType;
901 obj.arrayMinLength = arrayMinLength;
902 obj.arrayMaxLength = arrayMaxLength;
903
904 element.objectValue = obj;
905 }
906
907 /**
908 * Disallows an <code>Object</code> reference from being stored in
909 * nodes implementing the named element.
910 *
911 * @param elementName the name of the element.
912 *
913 * @exception IllegalArgumentException if <code>elementName</code> is
914 * not a legal element name for this format.
915 */
916 protected void removeObjectValue(String elementName) {
917 Element element = getElement(elementName);
918 element.objectValue = null;
945 Element element = getElement(elementName);
946 if (element.childPolicy != CHILD_POLICY_REPEAT) {
947 throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
948 }
949 return element.maxChildren;
950 }
951
952 private String getResource(String key, Locale locale) {
953 if (locale == null) {
954 locale = Locale.getDefault();
955 }
956
957 /**
958 * If an applet supplies an implementation of IIOMetadataFormat and
959 * resource bundles, then the resource bundle will need to be
960 * accessed via the applet class loader. So first try the context
961 * class loader to locate the resource bundle.
962 * If that throws MissingResourceException, then try the
963 * system class loader.
964 */
965 ClassLoader loader = (ClassLoader)
966 java.security.AccessController.doPrivileged(
967 new java.security.PrivilegedAction() {
968 public Object run() {
969 return Thread.currentThread().getContextClassLoader();
970 }
971 });
972
973 ResourceBundle bundle = null;
974 try {
975 bundle = ResourceBundle.getBundle(resourceBaseName,
976 locale, loader);
977 } catch (MissingResourceException mre) {
978 try {
979 bundle = ResourceBundle.getBundle(resourceBaseName, locale);
980 } catch (MissingResourceException mre1) {
981 return null;
982 }
983 }
984
985 try {
986 return bundle.getString(key);
987 } catch (MissingResourceException e) {
988 return null;
1020 * @see #setResourceBaseName
1021 */
1022 public String getElementDescription(String elementName,
1023 Locale locale) {
1024 Element element = getElement(elementName);
1025 return getResource(elementName, locale);
1026 }
1027
1028 // Children
1029
1030 public int getChildPolicy(String elementName) {
1031 Element element = getElement(elementName);
1032 return element.childPolicy;
1033 }
1034
1035 public String[] getChildNames(String elementName) {
1036 Element element = getElement(elementName);
1037 if (element.childPolicy == CHILD_POLICY_EMPTY) {
1038 return null;
1039 }
1040 return (String[])element.childList.toArray(new String[0]);
1041 }
1042
1043 // Attributes
1044
1045 public String[] getAttributeNames(String elementName) {
1046 Element element = getElement(elementName);
1047 List names = element.attrList;
1048
1049 String[] result = new String[names.size()];
1050 return (String[])names.toArray(result);
1051 }
1052
1053 public int getAttributeValueType(String elementName, String attrName) {
1054 Attribute attr = getAttribute(elementName, attrName);
1055 return attr.valueType;
1056 }
1057
1058 public int getAttributeDataType(String elementName, String attrName) {
1059 Attribute attr = getAttribute(elementName, attrName);
1060 return attr.dataType;
1061 }
1062
1063 public boolean isAttributeRequired(String elementName, String attrName) {
1064 Attribute attr = getAttribute(elementName, attrName);
1065 return attr.required;
1066 }
1067
1068 public String getAttributeDefaultValue(String elementName,
1069 String attrName) {
1070 Attribute attr = getAttribute(elementName, attrName);
1071 return attr.defaultValue;
1072 }
1073
1074 public String[] getAttributeEnumerations(String elementName,
1075 String attrName) {
1076 Attribute attr = getAttribute(elementName, attrName);
1077 if (attr.valueType != VALUE_ENUMERATION) {
1078 throw new IllegalArgumentException
1079 ("Attribute not an enumeration!");
1080 }
1081
1082 List values = attr.enumeratedValues;
1083 Iterator iter = values.iterator();
1084 String[] result = new String[values.size()];
1085 return (String[])values.toArray(result);
1086 }
1087
1088 public String getAttributeMinValue(String elementName, String attrName) {
1089 Attribute attr = getAttribute(elementName, attrName);
1090 if (attr.valueType != VALUE_RANGE &&
1091 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1092 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1093 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1094 throw new IllegalArgumentException("Attribute not a range!");
1095 }
1096
1097 return attr.minValue;
1098 }
1099
1100 public String getAttributeMaxValue(String elementName, String attrName) {
1101 Attribute attr = getAttribute(elementName, attrName);
1102 if (attr.valueType != VALUE_RANGE &&
1103 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1104 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1105 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1153 * @param locale the <code>Locale</code> for which localization
1154 * will be attempted, or <code>null</code>.
1155 *
1156 * @return the attribute description.
1157 *
1158 * @exception IllegalArgumentException if <code>elementName</code>
1159 * is <code>null</code>, or is not a legal element name for this format.
1160 * @exception IllegalArgumentException if <code>attrName</code> is
1161 * <code>null</code> or is not a legal attribute name for this
1162 * element.
1163 *
1164 * @see #setResourceBaseName
1165 */
1166 public String getAttributeDescription(String elementName,
1167 String attrName,
1168 Locale locale) {
1169 Element element = getElement(elementName);
1170 if (attrName == null) {
1171 throw new IllegalArgumentException("attrName == null!");
1172 }
1173 Attribute attr = (Attribute)element.attrMap.get(attrName);
1174 if (attr == null) {
1175 throw new IllegalArgumentException("No such attribute!");
1176 }
1177
1178 String key = elementName + "/" + attrName;
1179 return getResource(key, locale);
1180 }
1181
1182 private ObjectValue getObjectValue(String elementName) {
1183 Element element = getElement(elementName);
1184 ObjectValue objv = element.objectValue;
1185 if (objv == null) {
1186 throw new IllegalArgumentException("No object within element " +
1187 elementName + "!");
1188 }
1189 return objv;
1190 }
1191
1192 public int getObjectValueType(String elementName) {
1193 Element element = getElement(elementName);
1194 ObjectValue objv = element.objectValue;
1195 if (objv == null) {
1196 return VALUE_NONE;
1197 }
1198 return objv.valueType;
1199 }
1200
1201 public Class<?> getObjectClass(String elementName) {
1202 ObjectValue objv = getObjectValue(elementName);
1203 return objv.classType;
1204 }
1205
1206 public Object getObjectDefaultValue(String elementName) {
1207 ObjectValue objv = getObjectValue(elementName);
1208 return objv.defaultValue;
1209 }
1210
1211 public Object[] getObjectEnumerations(String elementName) {
1212 ObjectValue objv = getObjectValue(elementName);
1213 if (objv.valueType != VALUE_ENUMERATION) {
1214 throw new IllegalArgumentException("Not an enumeration!");
1215 }
1216 List vlist = objv.enumeratedValues;
1217 Object[] values = new Object[vlist.size()];
1218 return vlist.toArray(values);
1219 }
1220
1221 public Comparable<?> getObjectMinValue(String elementName) {
1222 ObjectValue objv = getObjectValue(elementName);
1223 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1224 throw new IllegalArgumentException("Not a range!");
1225 }
1226 return objv.minValue;
1227 }
1228
1229 public Comparable<?> getObjectMaxValue(String elementName) {
1230 ObjectValue objv = getObjectValue(elementName);
1231 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1232 throw new IllegalArgumentException("Not a range!");
1233 }
1234 return objv.maxValue;
1235 }
1236
1237 public int getObjectArrayMinLength(String elementName) {
1238 ObjectValue objv = getObjectValue(elementName);
1239 if (objv.valueType != VALUE_LIST) {
1240 throw new IllegalArgumentException("Not a list!");
1241 }
1242 return objv.arrayMinLength;
1243 }
1244
1245 public int getObjectArrayMaxLength(String elementName) {
1246 ObjectValue objv = getObjectValue(elementName);
1247 if (objv.valueType != VALUE_LIST) {
1248 throw new IllegalArgumentException("Not a list!");
1249 }
1250 return objv.arrayMaxLength;
1251 }
1252
1253 // Standard format descriptor
1254
1255 private synchronized static void createStandardFormat() {
1256 if (standardFormat == null) {
1257 standardFormat = new StandardMetadataFormat();
1258 }
1259 }
1260
1261 /**
1262 * Returns an <code>IIOMetadataFormat</code> object describing the
1263 * standard, plug-in neutral <code>javax.imageio_1.0</code>
1264 * metadata document format described in the comment of the
1265 * <code>javax.imageio.metadata</code> package.
1266 *
|
73 *
74 * @see ResourceBundle#getBundle(String,Locale)
75 *
76 */
77 public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
78
79 /**
80 * A <code>String</code> constant containing the standard format
81 * name, <code>"javax_imageio_1.0"</code>.
82 */
83 public static final String standardMetadataFormatName =
84 "javax_imageio_1.0";
85
86 private static IIOMetadataFormat standardFormat = null;
87
88 private String resourceBaseName = this.getClass().getName() + "Resources";
89
90 private String rootName;
91
92 // Element name (String) -> Element
93 private HashMap<String, Element> elementMap = new HashMap<>();
94
95 class Element {
96 String elementName;
97
98 int childPolicy;
99 int minChildren = 0;
100 int maxChildren = 0;
101
102 // Child names (Strings)
103 List<String> childList = new ArrayList<>();
104
105 // Parent names (Strings)
106 List<String> parentList = new ArrayList<>();
107
108 // List of attribute names in the order they were added
109 List<String> attrList = new ArrayList<>();
110 // Attr name (String) -> Attribute
111 Map<String, Attribute> attrMap = new HashMap<>();
112
113 ObjectValue<?> objectValue;
114 }
115
116 class Attribute {
117 String attrName;
118
119 int valueType = VALUE_ARBITRARY;
120 int dataType;
121 boolean required;
122 String defaultValue = null;
123
124 // enumeration
125 List<String> enumeratedValues;
126
127 // range
128 String minValue;
129 String maxValue;
130
131 // list
132 int listMinLength;
133 int listMaxLength;
134 }
135
136 class ObjectValue<T> {
137 int valueType = VALUE_NONE;
138 // ? extends T So that ObjectValue<Object> can take Class<?>
139 Class<? extends T> classType = null;
140 T defaultValue = null;
141
142 // Meaningful only if valueType == VALUE_ENUMERATION
143 List<? extends T> enumeratedValues = null;
144
145 // Meaningful only if valueType == VALUE_RANGE
146 Comparable<? super T> minValue = null;
147 Comparable<? super T> maxValue = null;
148
149 // Meaningful only if valueType == VALUE_LIST
150 int arrayMinLength = 0;
151 int arrayMaxLength = 0;
152 }
153
154 /**
155 * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
156 * with a given root element name and child policy (other than
157 * <code>CHILD_POLICY_REPEAT</code>). Additional elements, and
158 * their attributes and <code>Object</code> reference information
159 * may be added using the various <code>add</code> methods.
160 *
161 * @param rootName the name of the root element.
162 * @param childPolicy one of the <code>CHILD_POLICY_*</code> constants,
163 * other than <code>CHILD_POLICY_REPEAT</code>.
164 *
165 * @exception IllegalArgumentException if <code>rootName</code> is
166 * <code>null</code>.
167 * @exception IllegalArgumentException if <code>childPolicy</code> is
256 *
257 * @return a <code>String</code> containing the base name.
258 *
259 * @see #setResourceBaseName
260 */
261 protected String getResourceBaseName() {
262 return resourceBaseName;
263 }
264
265 /**
266 * Utility method for locating an element.
267 *
268 * @param mustAppear if <code>true</code>, throw an
269 * <code>IllegalArgumentException</code> if no such node exists;
270 * if <code>false</code>, just return null.
271 */
272 private Element getElement(String elementName, boolean mustAppear) {
273 if (mustAppear && (elementName == null)) {
274 throw new IllegalArgumentException("element name is null!");
275 }
276 Element element = elementMap.get(elementName);
277 if (mustAppear && (element == null)) {
278 throw new IllegalArgumentException("No such element: " +
279 elementName);
280 }
281 return element;
282 }
283
284 private Element getElement(String elementName) {
285 return getElement(elementName, true);
286 }
287
288 // Utility method for locating an attribute
289 private Attribute getAttribute(String elementName, String attrName) {
290 Element element = getElement(elementName);
291 Attribute attr = element.attrMap.get(attrName);
292 if (attr == null) {
293 throw new IllegalArgumentException("No such attribute \"" +
294 attrName + "\"!");
295 }
296 return attr;
297 }
298
299 // Setup
300
301 /**
302 * Adds a new element type to this metadata document format with a
303 * child policy other than <code>CHILD_POLICY_REPEAT</code>.
304 *
305 * @param elementName the name of the new element.
306 * @param parentName the name of the element that will be the
307 * parent of the new element.
308 * @param childPolicy one of the <code>CHILD_POLICY_*</code>
309 * constants, other than <code>CHILD_POLICY_REPEAT</code>,
310 * indicating the child policy of the new element.
311 *
392 * is <code>null</code>, or is not a legal element name for this
393 * format.
394 */
395 protected void addChildElement(String elementName, String parentName) {
396 Element parent = getElement(parentName);
397 Element element = getElement(elementName);
398 parent.childList.add(elementName);
399 element.parentList.add(parentName);
400 }
401
402 /**
403 * Removes an element from the format. If no element with the
404 * given name was present, nothing happens and no exception is
405 * thrown.
406 *
407 * @param elementName the name of the element to be removed.
408 */
409 protected void removeElement(String elementName) {
410 Element element = getElement(elementName, false);
411 if (element != null) {
412 Iterator<String> iter = element.parentList.iterator();
413 while (iter.hasNext()) {
414 String parentName = iter.next();
415 Element parent = getElement(parentName, false);
416 if (parent != null) {
417 parent.childList.remove(elementName);
418 }
419 }
420 elementMap.remove(elementName);
421 }
422 }
423
424 /**
425 * Adds a new attribute to a previously defined element that may
426 * be set to an arbitrary value.
427 *
428 * @param elementName the name of the element.
429 * @param attrName the name of the attribute being added.
430 * @param dataType the data type (string format) of the attribute,
431 * one of the <code>DATATYPE_*</code> constants.
432 * @param required <code>true</code> if the attribute must be present.
433 * @param defaultValue the default value for the attribute, or
434 * <code>null</code>.
498 */
499 protected void addAttribute(String elementName,
500 String attrName,
501 int dataType,
502 boolean required,
503 String defaultValue,
504 List<String> enumeratedValues) {
505 Element element = getElement(elementName);
506 if (attrName == null) {
507 throw new IllegalArgumentException("attrName == null!");
508 }
509 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
510 throw new IllegalArgumentException("Invalid value for dataType!");
511 }
512 if (enumeratedValues == null) {
513 throw new IllegalArgumentException("enumeratedValues == null!");
514 }
515 if (enumeratedValues.size() == 0) {
516 throw new IllegalArgumentException("enumeratedValues is empty!");
517 }
518 Iterator<String> iter = enumeratedValues.iterator();
519 while (iter.hasNext()) {
520 Object o = iter.next();
521 if (o == null) {
522 throw new IllegalArgumentException
523 ("enumeratedValues contains a null!");
524 }
525 if (!(o instanceof String)) {
526 throw new IllegalArgumentException
527 ("enumeratedValues contains a non-String value!");
528 }
529 }
530
531 Attribute attr = new Attribute();
532 attr.attrName = attrName;
533 attr.valueType = VALUE_ENUMERATION;
534 attr.dataType = dataType;
535 attr.required = required;
536 attr.defaultValue = defaultValue;
537 attr.enumeratedValues = enumeratedValues;
538
665 * <code>DATATYPE_BOOLEAN</code>.
666 *
667 * @param elementName the name of the element.
668 * @param attrName the name of the attribute being added.
669 * @param hasDefaultValue <code>true</code> if a default value
670 * should be present.
671 * @param defaultValue the default value for the attribute as a
672 * <code>boolean</code>, ignored if <code>hasDefaultValue</code>
673 * is <code>false</code>.
674 *
675 * @exception IllegalArgumentException if <code>elementName</code>
676 * is <code>null</code>, or is not a legal element name for this
677 * format.
678 * @exception IllegalArgumentException if <code>attrName</code> is
679 * <code>null</code>.
680 */
681 protected void addBooleanAttribute(String elementName,
682 String attrName,
683 boolean hasDefaultValue,
684 boolean defaultValue) {
685 List<String> values = new ArrayList<>();
686 values.add("TRUE");
687 values.add("FALSE");
688
689 String dval = null;
690 if (hasDefaultValue) {
691 dval = defaultValue ? "TRUE" : "FALSE";
692 }
693 addAttribute(elementName,
694 attrName,
695 DATATYPE_BOOLEAN,
696 true,
697 dval,
698 values);
699 }
700
701 /**
702 * Removes an attribute from a previously defined element. If no
703 * attribute with the given name was present in the given element,
704 * nothing happens and no exception is thrown.
705 *
724 * <p> If an <code>Object</code> reference was previously allowed,
725 * the previous settings are overwritten.
726 *
727 * @param elementName the name of the element.
728 * @param classType a <code>Class</code> variable indicating the
729 * legal class type for the object value.
730 * @param required <code>true</code> if an object value must be present.
731 * @param defaultValue the default value for the
732 * <code>Object</code> reference, or <code>null</code>.
733 * @param <T> the type of the object.
734 *
735 * @exception IllegalArgumentException if <code>elementName</code>
736 * is <code>null</code>, or is not a legal element name for this format.
737 */
738 protected <T> void addObjectValue(String elementName,
739 Class<T> classType,
740 boolean required,
741 T defaultValue)
742 {
743 Element element = getElement(elementName);
744 ObjectValue<T> obj = new ObjectValue<>();
745 obj.valueType = VALUE_ARBITRARY;
746 obj.classType = classType;
747 obj.defaultValue = defaultValue;
748
749 element.objectValue = obj;
750 }
751
752 /**
753 * Allows an <code>Object</code> reference of a given class type
754 * to be stored in nodes implementing the named element. The
755 * value of the <code>Object</code> must be one of the values
756 * given by <code>enumeratedValues</code>.
757 *
758 * <p> If an <code>Object</code> reference was previously allowed,
759 * the previous settings are overwritten.
760 *
761 * @param elementName the name of the element.
762 * @param classType a <code>Class</code> variable indicating the
763 * legal class type for the object value.
764 * @param required <code>true</code> if an object value must be present.
777 * <code>enumeratedValues</code> does not contain at least one
778 * entry.
779 * @exception IllegalArgumentException if
780 * <code>enumeratedValues</code> contains an element that is not
781 * an instance of the class type denoted by <code>classType</code>
782 * or is <code>null</code>.
783 */
784 protected <T> void addObjectValue(String elementName,
785 Class<T> classType,
786 boolean required,
787 T defaultValue,
788 List<? extends T> enumeratedValues)
789 {
790 Element element = getElement(elementName);
791 if (enumeratedValues == null) {
792 throw new IllegalArgumentException("enumeratedValues == null!");
793 }
794 if (enumeratedValues.size() == 0) {
795 throw new IllegalArgumentException("enumeratedValues is empty!");
796 }
797 Iterator<? extends T> iter = enumeratedValues.iterator();
798 while (iter.hasNext()) {
799 Object o = iter.next();
800 if (o == null) {
801 throw new IllegalArgumentException("enumeratedValues contains a null!");
802 }
803 if (!classType.isInstance(o)) {
804 throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
805 }
806 }
807
808 ObjectValue<T> obj = new ObjectValue<>();
809 obj.valueType = VALUE_ENUMERATION;
810 obj.classType = classType;
811 obj.defaultValue = defaultValue;
812 obj.enumeratedValues = enumeratedValues;
813
814 element.objectValue = obj;
815 }
816
817 /**
818 * Allows an <code>Object</code> reference of a given class type
819 * to be stored in nodes implementing the named element. The
820 * value of the <code>Object</code> must be within the range given
821 * by <code>minValue</code> and <code>maxValue</code>.
822 * Furthermore, the class type must implement the
823 * <code>Comparable</code> interface.
824 *
825 * <p> If an <code>Object</code> reference was previously allowed,
826 * the previous settings are overwritten.
827 *
828 * @param elementName the name of the element.
838 * @param minInclusive <code>true</code> if <code>minValue</code>
839 * is inclusive.
840 * @param maxInclusive <code>true</code> if <code>maxValue</code>
841 * is inclusive.
842 * @param <T> the type of the object.
843 *
844 * @exception IllegalArgumentException if <code>elementName</code>
845 * is <code>null</code>, or is not a legal element name for this
846 * format.
847 */
848 protected <T extends Object & Comparable<? super T>> void
849 addObjectValue(String elementName,
850 Class<T> classType,
851 T defaultValue,
852 Comparable<? super T> minValue,
853 Comparable<? super T> maxValue,
854 boolean minInclusive,
855 boolean maxInclusive)
856 {
857 Element element = getElement(elementName);
858 ObjectValue<T> obj = new ObjectValue<>();
859 obj.valueType = VALUE_RANGE;
860 if (minInclusive) {
861 obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
862 }
863 if (maxInclusive) {
864 obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
865 }
866 obj.classType = classType;
867 obj.defaultValue = defaultValue;
868 obj.minValue = minValue;
869 obj.maxValue = maxValue;
870
871 element.objectValue = obj;
872 }
873
874 /**
875 * Allows an <code>Object</code> reference of a given class type
876 * to be stored in nodes implementing the named element. The
877 * value of the <code>Object</code> must an array of objects of
878 * class type given by <code>classType</code>, with at least
879 * <code>arrayMinLength</code> and at most
880 * <code>arrayMaxLength</code> elements.
881 *
882 * <p> If an <code>Object</code> reference was previously allowed,
883 * the previous settings are overwritten.
884 *
885 * @param elementName the name of the element.
886 * @param classType a <code>Class</code> variable indicating the
887 * legal class type for the object value.
888 * @param arrayMinLength the smallest legal length for the array.
889 * @param arrayMaxLength the largest legal length for the array.
890 *
891 * @exception IllegalArgumentException if <code>elementName</code> is
892 * not a legal element name for this format.
893 */
894 protected void addObjectValue(String elementName,
895 Class<?> classType,
896 int arrayMinLength,
897 int arrayMaxLength) {
898 Element element = getElement(elementName);
899 ObjectValue<Object> obj = new ObjectValue<>();
900 obj.valueType = VALUE_LIST;
901 obj.classType = classType;
902 obj.arrayMinLength = arrayMinLength;
903 obj.arrayMaxLength = arrayMaxLength;
904
905 element.objectValue = obj;
906 }
907
908 /**
909 * Disallows an <code>Object</code> reference from being stored in
910 * nodes implementing the named element.
911 *
912 * @param elementName the name of the element.
913 *
914 * @exception IllegalArgumentException if <code>elementName</code> is
915 * not a legal element name for this format.
916 */
917 protected void removeObjectValue(String elementName) {
918 Element element = getElement(elementName);
919 element.objectValue = null;
946 Element element = getElement(elementName);
947 if (element.childPolicy != CHILD_POLICY_REPEAT) {
948 throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
949 }
950 return element.maxChildren;
951 }
952
953 private String getResource(String key, Locale locale) {
954 if (locale == null) {
955 locale = Locale.getDefault();
956 }
957
958 /**
959 * If an applet supplies an implementation of IIOMetadataFormat and
960 * resource bundles, then the resource bundle will need to be
961 * accessed via the applet class loader. So first try the context
962 * class loader to locate the resource bundle.
963 * If that throws MissingResourceException, then try the
964 * system class loader.
965 */
966 ClassLoader loader =
967 java.security.AccessController.doPrivileged(
968 new java.security.PrivilegedAction<ClassLoader>() {
969 public ClassLoader run() {
970 return Thread.currentThread().getContextClassLoader();
971 }
972 });
973
974 ResourceBundle bundle = null;
975 try {
976 bundle = ResourceBundle.getBundle(resourceBaseName,
977 locale, loader);
978 } catch (MissingResourceException mre) {
979 try {
980 bundle = ResourceBundle.getBundle(resourceBaseName, locale);
981 } catch (MissingResourceException mre1) {
982 return null;
983 }
984 }
985
986 try {
987 return bundle.getString(key);
988 } catch (MissingResourceException e) {
989 return null;
1021 * @see #setResourceBaseName
1022 */
1023 public String getElementDescription(String elementName,
1024 Locale locale) {
1025 Element element = getElement(elementName);
1026 return getResource(elementName, locale);
1027 }
1028
1029 // Children
1030
1031 public int getChildPolicy(String elementName) {
1032 Element element = getElement(elementName);
1033 return element.childPolicy;
1034 }
1035
1036 public String[] getChildNames(String elementName) {
1037 Element element = getElement(elementName);
1038 if (element.childPolicy == CHILD_POLICY_EMPTY) {
1039 return null;
1040 }
1041 return element.childList.toArray(new String[0]);
1042 }
1043
1044 // Attributes
1045
1046 public String[] getAttributeNames(String elementName) {
1047 Element element = getElement(elementName);
1048 List<String> names = element.attrList;
1049
1050 String[] result = new String[names.size()];
1051 return names.toArray(result);
1052 }
1053
1054 public int getAttributeValueType(String elementName, String attrName) {
1055 Attribute attr = getAttribute(elementName, attrName);
1056 return attr.valueType;
1057 }
1058
1059 public int getAttributeDataType(String elementName, String attrName) {
1060 Attribute attr = getAttribute(elementName, attrName);
1061 return attr.dataType;
1062 }
1063
1064 public boolean isAttributeRequired(String elementName, String attrName) {
1065 Attribute attr = getAttribute(elementName, attrName);
1066 return attr.required;
1067 }
1068
1069 public String getAttributeDefaultValue(String elementName,
1070 String attrName) {
1071 Attribute attr = getAttribute(elementName, attrName);
1072 return attr.defaultValue;
1073 }
1074
1075 public String[] getAttributeEnumerations(String elementName,
1076 String attrName) {
1077 Attribute attr = getAttribute(elementName, attrName);
1078 if (attr.valueType != VALUE_ENUMERATION) {
1079 throw new IllegalArgumentException
1080 ("Attribute not an enumeration!");
1081 }
1082
1083 List<String> values = attr.enumeratedValues;
1084 String[] result = new String[values.size()];
1085 return values.toArray(result);
1086 }
1087
1088 public String getAttributeMinValue(String elementName, String attrName) {
1089 Attribute attr = getAttribute(elementName, attrName);
1090 if (attr.valueType != VALUE_RANGE &&
1091 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1092 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1093 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1094 throw new IllegalArgumentException("Attribute not a range!");
1095 }
1096
1097 return attr.minValue;
1098 }
1099
1100 public String getAttributeMaxValue(String elementName, String attrName) {
1101 Attribute attr = getAttribute(elementName, attrName);
1102 if (attr.valueType != VALUE_RANGE &&
1103 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1104 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1105 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1153 * @param locale the <code>Locale</code> for which localization
1154 * will be attempted, or <code>null</code>.
1155 *
1156 * @return the attribute description.
1157 *
1158 * @exception IllegalArgumentException if <code>elementName</code>
1159 * is <code>null</code>, or is not a legal element name for this format.
1160 * @exception IllegalArgumentException if <code>attrName</code> is
1161 * <code>null</code> or is not a legal attribute name for this
1162 * element.
1163 *
1164 * @see #setResourceBaseName
1165 */
1166 public String getAttributeDescription(String elementName,
1167 String attrName,
1168 Locale locale) {
1169 Element element = getElement(elementName);
1170 if (attrName == null) {
1171 throw new IllegalArgumentException("attrName == null!");
1172 }
1173 Attribute attr = element.attrMap.get(attrName);
1174 if (attr == null) {
1175 throw new IllegalArgumentException("No such attribute!");
1176 }
1177
1178 String key = elementName + "/" + attrName;
1179 return getResource(key, locale);
1180 }
1181
1182 private ObjectValue<?> getObjectValue(String elementName) {
1183 Element element = getElement(elementName);
1184 ObjectValue<?> objv = element.objectValue;
1185 if (objv == null) {
1186 throw new IllegalArgumentException("No object within element " +
1187 elementName + "!");
1188 }
1189 return objv;
1190 }
1191
1192 public int getObjectValueType(String elementName) {
1193 Element element = getElement(elementName);
1194 ObjectValue<?> objv = element.objectValue;
1195 if (objv == null) {
1196 return VALUE_NONE;
1197 }
1198 return objv.valueType;
1199 }
1200
1201 public Class<?> getObjectClass(String elementName) {
1202 ObjectValue<?> objv = getObjectValue(elementName);
1203 return objv.classType;
1204 }
1205
1206 public Object getObjectDefaultValue(String elementName) {
1207 ObjectValue<?> objv = getObjectValue(elementName);
1208 return objv.defaultValue;
1209 }
1210
1211 public Object[] getObjectEnumerations(String elementName) {
1212 ObjectValue<?> objv = getObjectValue(elementName);
1213 if (objv.valueType != VALUE_ENUMERATION) {
1214 throw new IllegalArgumentException("Not an enumeration!");
1215 }
1216 List<?> vlist = objv.enumeratedValues;
1217 Object[] values = new Object[vlist.size()];
1218 return vlist.toArray(values);
1219 }
1220
1221 public Comparable<?> getObjectMinValue(String elementName) {
1222 ObjectValue<?> objv = getObjectValue(elementName);
1223 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1224 throw new IllegalArgumentException("Not a range!");
1225 }
1226 return objv.minValue;
1227 }
1228
1229 public Comparable<?> getObjectMaxValue(String elementName) {
1230 ObjectValue<?> objv = getObjectValue(elementName);
1231 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1232 throw new IllegalArgumentException("Not a range!");
1233 }
1234 return objv.maxValue;
1235 }
1236
1237 public int getObjectArrayMinLength(String elementName) {
1238 ObjectValue<?> objv = getObjectValue(elementName);
1239 if (objv.valueType != VALUE_LIST) {
1240 throw new IllegalArgumentException("Not a list!");
1241 }
1242 return objv.arrayMinLength;
1243 }
1244
1245 public int getObjectArrayMaxLength(String elementName) {
1246 ObjectValue<?> objv = getObjectValue(elementName);
1247 if (objv.valueType != VALUE_LIST) {
1248 throw new IllegalArgumentException("Not a list!");
1249 }
1250 return objv.arrayMaxLength;
1251 }
1252
1253 // Standard format descriptor
1254
1255 private synchronized static void createStandardFormat() {
1256 if (standardFormat == null) {
1257 standardFormat = new StandardMetadataFormat();
1258 }
1259 }
1260
1261 /**
1262 * Returns an <code>IIOMetadataFormat</code> object describing the
1263 * standard, plug-in neutral <code>javax.imageio_1.0</code>
1264 * metadata document format described in the comment of the
1265 * <code>javax.imageio.metadata</code> package.
1266 *
|