blob: a94fe1648c0bc5a4a1432e4698d21537b3c083f2 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.imageio.metadata;
27
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.HashMap;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Locale;
34import java.util.Map;
35import java.util.MissingResourceException;
36import java.util.ResourceBundle;
37import javax.imageio.ImageTypeSpecifier;
38import com.sun.imageio.plugins.common.StandardMetadataFormat;
39
40/**
41 * A concrete class providing a reusable implementation of the
42 * <code>IIOMetadataFormat</code> interface. In addition, a static
43 * instance representing the standard, plug-in neutral
44 * <code>javax_imageio_1.0</code> format is provided by the
45 * <code>getStandardFormatInstance</code> method.
46 *
47 * <p> In order to supply localized descriptions of elements and
48 * attributes, a <code>ResourceBundle</code> with a base name of
49 * <code>this.getClass().getName() + "Resources"</code> should be
50 * supplied via the usual mechanism used by
51 * <code>ResourceBundle.getBundle</code>. Briefly, the subclasser
52 * supplies one or more additional classes according to a naming
53 * convention (by default, the fully-qualified name of the subclass
54 * extending <code>IIMetadataFormatImpl</code>, plus the string
55 * "Resources", plus the country, language, and variant codes
56 * separated by underscores). At run time, calls to
57 * <code>getElementDescription</code> or
58 * <code>getAttributeDescription</code> will attempt to load such
59 * classes dynamically according to the supplied locale, and will use
60 * either the element name, or the element name followed by a '/'
61 * character followed by the attribute name as a key. This key will
62 * be supplied to the <code>ResourceBundle</code>'s
63 * <code>getString</code> method, and the resulting localized
64 * description of the node or attribute is returned.
65 *
66 * <p> The subclass may supply a different base name for the resource
67 * bundles using the <code>setResourceBaseName</code> method.
68 *
69 * <p> A subclass may choose its own localization mechanism, if so
70 * desired, by overriding the supplied implementations of
71 * <code>getElementDescription</code> and
72 * <code>getAttributeDescription</code>.
73 *
74 * @see ResourceBundle#getBundle(String,Locale)
75 *
76 */
77public 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
167 * not one of the predefined constants.
168 */
169 public IIOMetadataFormatImpl(String rootName,
170 int childPolicy) {
171 if (rootName == null) {
172 throw new IllegalArgumentException("rootName == null!");
173 }
174 if (childPolicy < CHILD_POLICY_EMPTY ||
175 childPolicy > CHILD_POLICY_MAX ||
176 childPolicy == CHILD_POLICY_REPEAT) {
177 throw new IllegalArgumentException("Invalid value for childPolicy!");
178 }
179
180 this.rootName = rootName;
181
182 Element root = new Element();
183 root.elementName = rootName;
184 root.childPolicy = childPolicy;
185
186 elementMap.put(rootName, root);
187 }
188
189 /**
190 * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
191 * with a given root element name and a child policy of
192 * <code>CHILD_POLICY_REPEAT</code>. Additional elements, and
193 * their attributes and <code>Object</code> reference information
194 * may be added using the various <code>add</code> methods.
195 *
196 * @param rootName the name of the root element.
197 * @param minChildren the minimum number of children of the node.
198 * @param maxChildren the maximum number of children of the node.
199 *
200 * @exception IllegalArgumentException if <code>rootName</code> is
201 * <code>null</code>.
202 * @exception IllegalArgumentException if <code>minChildren</code>
203 * is negative or larger than <code>maxChildren</code>.
204 */
205 public IIOMetadataFormatImpl(String rootName,
206 int minChildren,
207 int maxChildren) {
208 if (rootName == null) {
209 throw new IllegalArgumentException("rootName == null!");
210 }
211 if (minChildren < 0) {
212 throw new IllegalArgumentException("minChildren < 0!");
213 }
214 if (minChildren > maxChildren) {
215 throw new IllegalArgumentException("minChildren > maxChildren!");
216 }
217
218 Element root = new Element();
219 root.elementName = rootName;
220 root.childPolicy = CHILD_POLICY_REPEAT;
221 root.minChildren = minChildren;
222 root.maxChildren = maxChildren;
223
224 this.rootName = rootName;
225 elementMap.put(rootName, root);
226 }
227
228 /**
229 * Sets a new base name for locating <code>ResourceBundle</code>s
230 * containing descriptions of elements and attributes for this
231 * format.
232 *
233 * <p> Prior to the first time this method is called, the base
234 * name will be equal to <code>this.getClass().getName() +
235 * "Resources"</code>.
236 *
237 * @param resourceBaseName a <code>String</code> containg the new
238 * base name.
239 *
240 * @exception IllegalArgumentException if
241 * <code>resourceBaseName</code> is <code>null</code>.
242 *
243 * @see #getResourceBaseName
244 */
245 protected void setResourceBaseName(String resourceBaseName) {
246 if (resourceBaseName == null) {
247 throw new IllegalArgumentException("resourceBaseName == null!");
248 }
249 this.resourceBaseName = resourceBaseName;
250 }
251
252 /**
253 * Returns the currently set base name for locating
254 * <code>ResourceBundle</code>s.
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 *
311 * @exception IllegalArgumentException if <code>parentName</code>
312 * is <code>null</code>, or is not a legal element name for this
313 * format.
314 * @exception IllegalArgumentException if <code>childPolicy</code>
315 * is not one of the predefined constants.
316 */
317 protected void addElement(String elementName,
318 String parentName,
319 int childPolicy) {
320 Element parent = getElement(parentName);
321 if (childPolicy < CHILD_POLICY_EMPTY ||
322 childPolicy > CHILD_POLICY_MAX ||
323 childPolicy == CHILD_POLICY_REPEAT) {
324 throw new IllegalArgumentException
325 ("Invalid value for childPolicy!");
326 }
327
328 Element element = new Element();
329 element.elementName = elementName;
330 element.childPolicy = childPolicy;
331
332 parent.childList.add(elementName);
333 element.parentList.add(parentName);
334
335 elementMap.put(elementName, element);
336 }
337
338 /**
339 * Adds a new element type to this metadata document format with a
340 * child policy of <code>CHILD_POLICY_REPEAT</code>.
341 *
342 * @param elementName the name of the new element.
343 * @param parentName the name of the element that will be the
344 * parent of the new element.
345 * @param minChildren the minimum number of children of the node.
346 * @param maxChildren the maximum number of children of the node.
347 *
348 * @exception IllegalArgumentException if <code>parentName</code>
349 * is <code>null</code>, or is not a legal element name for this
350 * format.
351 * @exception IllegalArgumentException if <code>minChildren</code>
352 * is negative or larger than <code>maxChildren</code>.
353 */
354 protected void addElement(String elementName,
355 String parentName,
356 int minChildren,
357 int maxChildren) {
358 Element parent = getElement(parentName);
359 if (minChildren < 0) {
360 throw new IllegalArgumentException("minChildren < 0!");
361 }
362 if (minChildren > maxChildren) {
363 throw new IllegalArgumentException("minChildren > maxChildren!");
364 }
365
366 Element element = new Element();
367 element.elementName = elementName;
368 element.childPolicy = CHILD_POLICY_REPEAT;
369 element.minChildren = minChildren;
370 element.maxChildren = maxChildren;
371
372 parent.childList.add(elementName);
373 element.parentList.add(parentName);
374
375 elementMap.put(elementName, element);
376 }
377
378 /**
379 * Adds an existing element to the list of legal children for a
380 * given parent node type.
381 *
382 * @param parentName the name of the element that will be the
383 * new parent of the element.
384 * @param elementName the name of the element to be addded as a
385 * child.
386 *
387 * @exception IllegalArgumentException if <code>elementName</code>
388 * is <code>null</code>, or is not a legal element name for this
389 * format.
390 * @exception IllegalArgumentException if <code>parentName</code>
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>.
434 *
435 * @exception IllegalArgumentException if <code>elementName</code>
436 * is <code>null</code>, or is not a legal element name for this
437 * format.
438 * @exception IllegalArgumentException if <code>attrName</code> is
439 * <code>null</code>.
440 * @exception IllegalArgumentException if <code>dataType</code> is
441 * not one of the predefined constants.
442 */
443 protected void addAttribute(String elementName,
444 String attrName,
445 int dataType,
446 boolean required,
447 String defaultValue) {
448 Element element = getElement(elementName);
449 if (attrName == null) {
450 throw new IllegalArgumentException("attrName == null!");
451 }
452 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
453 throw new IllegalArgumentException("Invalid value for dataType!");
454 }
455
456 Attribute attr = new Attribute();
457 attr.attrName = attrName;
458 attr.valueType = VALUE_ARBITRARY;
459 attr.dataType = dataType;
460 attr.required = required;
461 attr.defaultValue = defaultValue;
462
463 element.attrList.add(attrName);
464 element.attrMap.put(attrName, attr);
465 }
466
467 /**
468 * Adds a new attribute to a previously defined element that will
469 * be defined by a set of enumerated values.
470 *
471 * @param elementName the name of the element.
472 * @param attrName the name of the attribute being added.
473 * @param dataType the data type (string format) of the attribute,
474 * one of the <code>DATATYPE_*</code> constants.
475 * @param required <code>true</code> if the attribute must be present.
476 * @param defaultValue the default value for the attribute, or
477 * <code>null</code>.
478 * @param enumeratedValues a <code>List</code> of
479 * <code>String</code>s containing the legal values for the
480 * attribute.
481 *
482 * @exception IllegalArgumentException if <code>elementName</code>
483 * is <code>null</code>, or is not a legal element name for this
484 * format.
485 * @exception IllegalArgumentException if <code>attrName</code> is
486 * <code>null</code>.
487 * @exception IllegalArgumentException if <code>dataType</code> is
488 * not one of the predefined constants.
489 * @exception IllegalArgumentException if
490 * <code>enumeratedValues</code> is <code>null</code>.
491 * @exception IllegalArgumentException if
492 * <code>enumeratedValues</code> does not contain at least one
493 * entry.
494 * @exception IllegalArgumentException if
495 * <code>enumeratedValues</code> contains an element that is not a
496 * <code>String</code> or is <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
538 element.attrList.add(attrName);
539 element.attrMap.put(attrName, attr);
540 }
541
542 /**
543 * Adds a new attribute to a previously defined element that will
544 * be defined by a range of values.
545 *
546 * @param elementName the name of the element.
547 * @param attrName the name of the attribute being added.
548 * @param dataType the data type (string format) of the attribute,
549 * one of the <code>DATATYPE_*</code> constants.
550 * @param required <code>true</code> if the attribute must be present.
551 * @param defaultValue the default value for the attribute, or
552 * <code>null</code>.
553 * @param minValue the smallest (inclusive or exclusive depending
554 * on the value of <code>minInclusive</code>) legal value for the
555 * attribute, as a <code>String</code>.
556 * @param maxValue the largest (inclusive or exclusive depending
557 * on the value of <code>minInclusive</code>) legal value for the
558 * attribute, as a <code>String</code>.
559 * @param minInclusive <code>true</code> if <code>minValue</code>
560 * is inclusive.
561 * @param maxInclusive <code>true</code> if <code>maxValue</code>
562 * is inclusive.
563 *
564 * @exception IllegalArgumentException if <code>elementName</code>
565 * is <code>null</code>, or is not a legal element name for this
566 * format.
567 * @exception IllegalArgumentException if <code>attrName</code> is
568 * <code>null</code>.
569 * @exception IllegalArgumentException if <code>dataType</code> is
570 * not one of the predefined constants.
571 */
572 protected void addAttribute(String elementName,
573 String attrName,
574 int dataType,
575 boolean required,
576 String defaultValue,
577 String minValue,
578 String maxValue,
579 boolean minInclusive,
580 boolean maxInclusive) {
581 Element element = getElement(elementName);
582 if (attrName == null) {
583 throw new IllegalArgumentException("attrName == null!");
584 }
585 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
586 throw new IllegalArgumentException("Invalid value for dataType!");
587 }
588
589 Attribute attr = new Attribute();
590 attr.attrName = attrName;
591 attr.valueType = VALUE_RANGE;
592 if (minInclusive) {
593 attr.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
594 }
595 if (maxInclusive) {
596 attr.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
597 }
598 attr.dataType = dataType;
599 attr.required = required;
600 attr.defaultValue = defaultValue;
601 attr.minValue = minValue;
602 attr.maxValue = maxValue;
603
604 element.attrList.add(attrName);
605 element.attrMap.put(attrName, attr);
606 }
607
608 /**
609 * Adds a new attribute to a previously defined element that will
610 * be defined by a list of values.
611 *
612 * @param elementName the name of the element.
613 * @param attrName the name of the attribute being added.
614 * @param dataType the data type (string format) of the attribute,
615 * one of the <code>DATATYPE_*</code> constants.
616 * @param required <code>true</code> if the attribute must be present.
617 * @param listMinLength the smallest legal number of list items.
618 * @param listMaxLength the largest legal number of list items.
619 *
620 * @exception IllegalArgumentException if <code>elementName</code>
621 * is <code>null</code>, or is not a legal element name for this
622 * format.
623 * @exception IllegalArgumentException if <code>attrName</code> is
624 * <code>null</code>.
625 * @exception IllegalArgumentException if <code>dataType</code> is
626 * not one of the predefined constants.
627 * @exception IllegalArgumentException if
628 * <code>listMinLength</code> is negative or larger than
629 * <code>listMaxLength</code>.
630 */
631 protected void addAttribute(String elementName,
632 String attrName,
633 int dataType,
634 boolean required,
635 int listMinLength,
636 int listMaxLength) {
637 Element element = getElement(elementName);
638 if (attrName == null) {
639 throw new IllegalArgumentException("attrName == null!");
640 }
641 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
642 throw new IllegalArgumentException("Invalid value for dataType!");
643 }
644 if (listMinLength < 0 || listMinLength > listMaxLength) {
645 throw new IllegalArgumentException("Invalid list bounds!");
646 }
647
648 Attribute attr = new Attribute();
649 attr.attrName = attrName;
650 attr.valueType = VALUE_LIST;
651 attr.dataType = dataType;
652 attr.required = required;
653 attr.listMinLength = listMinLength;
654 attr.listMaxLength = listMaxLength;
655
656 element.attrList.add(attrName);
657 element.attrMap.put(attrName, attr);
658 }
659
660 /**
661 * Adds a new attribute to a previously defined element that will
662 * be defined by the enumerated values <code>TRUE</code> and
663 * <code>FALSE</code>, with a datatype of
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 *
705 * @param elementName the name of the element.
706 * @param attrName the name of the attribute being removed.
707 *
708 * @exception IllegalArgumentException if <code>elementName</code>
709 * is <code>null</code>, or is not a legal element name for this format.
710 */
711 protected void removeAttribute(String elementName, String attrName) {
712 Element element = getElement(elementName);
713 element.attrList.remove(attrName);
714 element.attrMap.remove(attrName);
715 }
716
717 /**
718 * Allows an <code>Object</code> reference of a given class type
719 * to be stored in nodes implementing the named element. The
720 * value of the <code>Object</code> is unconstrained other than by
721 * its class type.
722 *
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 *
733 * @exception IllegalArgumentException if <code>elementName</code>
734 * is <code>null</code>, or is not a legal element name for this format.
735 */
736 protected <T> void addObjectValue(String elementName,
737 Class<T> classType,
738 boolean required,
739 T defaultValue)
740 {
741 Element element = getElement(elementName);
742 ObjectValue obj = new ObjectValue();
743 obj.valueType = VALUE_ARBITRARY;
744 obj.classType = classType;
745 obj.defaultValue = defaultValue;
746
747 element.objectValue = obj;
748 }
749
750 /**
751 * Allows an <code>Object</code> reference of a given class type
752 * to be stored in nodes implementing the named element. The
753 * value of the <code>Object</code> must be one of the values
754 * given by <code>enumeratedValues</code>.
755 *
756 * <p> If an <code>Object</code> reference was previously allowed,
757 * the previous settings are overwritten.
758 *
759 * @param elementName the name of the element.
760 * @param classType a <code>Class</code> variable indicating the
761 * legal class type for the object value.
762 * @param required <code>true</code> if an object value must be present.
763 * @param defaultValue the default value for the
764 * <code>Object</code> reference, or <code>null</code>.
765 * @param enumeratedValues a <code>List</code> of
766 * <code>Object</code>s containing the legal values for the
767 * object reference.
768 *
769 * @exception IllegalArgumentException if <code>elementName</code>
770 * is <code>null</code>, or is not a legal element name for this format.
771 * @exception IllegalArgumentException if
772 * <code>enumeratedValues</code> is <code>null</code>.
773 * @exception IllegalArgumentException if
774 * <code>enumeratedValues</code> does not contain at least one
775 * entry.
776 * @exception IllegalArgumentException if
777 * <code>enumeratedValues</code> contains an element that is not
778 * an instance of the class type denoted by <code>classType</code>
779 * or is <code>null</code>.
780 */
781 protected <T> void addObjectValue(String elementName,
782 Class<T> classType,
783 boolean required,
784 T defaultValue,
785 List<? extends T> enumeratedValues)
786 {
787 Element element = getElement(elementName);
788 if (enumeratedValues == null) {
789 throw new IllegalArgumentException("enumeratedValues == null!");
790 }
791 if (enumeratedValues.size() == 0) {
792 throw new IllegalArgumentException("enumeratedValues is empty!");
793 }
794 Iterator iter = enumeratedValues.iterator();
795 while (iter.hasNext()) {
796 Object o = iter.next();
797 if (o == null) {
798 throw new IllegalArgumentException("enumeratedValues contains a null!");
799 }
800 if (!classType.isInstance(o)) {
801 throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
802 }
803 }
804
805 ObjectValue obj = new ObjectValue();
806 obj.valueType = VALUE_ENUMERATION;
807 obj.classType = classType;
808 obj.defaultValue = defaultValue;
809 obj.enumeratedValues = enumeratedValues;
810
811 element.objectValue = obj;
812 }
813
814 /**
815 * Allows an <code>Object</code> reference of a given class type
816 * to be stored in nodes implementing the named element. The
817 * value of the <code>Object</code> must be within the range given
818 * by <code>minValue</code> and <code>maxValue</code>.
819 * Furthermore, the class type must implement the
820 * <code>Comparable</code> interface.
821 *
822 * <p> If an <code>Object</code> reference was previously allowed,
823 * the previous settings are overwritten.
824 *
825 * @param elementName the name of the element.
826 * @param classType a <code>Class</code> variable indicating the
827 * legal class type for the object value.
828 * @param defaultValue the default value for the
829 * @param minValue the smallest (inclusive or exclusive depending
830 * on the value of <code>minInclusive</code>) legal value for the
831 * object value, as a <code>String</code>.
832 * @param maxValue the largest (inclusive or exclusive depending
833 * on the value of <code>minInclusive</code>) legal value for the
834 * object value, as a <code>String</code>.
835 * @param minInclusive <code>true</code> if <code>minValue</code>
836 * is inclusive.
837 * @param maxInclusive <code>true</code> if <code>maxValue</code>
838 * is inclusive.
839 *
840 * @exception IllegalArgumentException if <code>elementName</code>
841 * is <code>null</code>, or is not a legal element name for this
842 * format.
843 */
844 protected <T extends Object & Comparable<? super T>> void
845 addObjectValue(String elementName,
846 Class<T> classType,
847 T defaultValue,
848 Comparable<? super T> minValue,
849 Comparable<? super T> maxValue,
850 boolean minInclusive,
851 boolean maxInclusive)
852 {
853 Element element = getElement(elementName);
854 ObjectValue obj = new ObjectValue();
855 obj.valueType = VALUE_RANGE;
856 if (minInclusive) {
857 obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
858 }
859 if (maxInclusive) {
860 obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
861 }
862 obj.classType = classType;
863 obj.defaultValue = defaultValue;
864 obj.minValue = minValue;
865 obj.maxValue = maxValue;
866
867 element.objectValue = obj;
868 }
869
870 /**
871 * Allows an <code>Object</code> reference of a given class type
872 * to be stored in nodes implementing the named element. The
873 * value of the <code>Object</code> must an array of objects of
874 * class type given by <code>classType</code>, with at least
875 * <code>arrayMinLength</code> and at most
876 * <code>arrayMaxLength</code> elements.
877 *
878 * <p> If an <code>Object</code> reference was previously allowed,
879 * the previous settings are overwritten.
880 *
881 * @param elementName the name of the element.
882 * @param classType a <code>Class</code> variable indicating the
883 * legal class type for the object value.
884 * @param arrayMinLength the smallest legal length for the array.
885 * @param arrayMaxLength the largest legal length for the array.
886 *
887 * @exception IllegalArgumentException if <code>elementName</code> is
888 * not a legal element name for this format.
889 */
890 protected void addObjectValue(String elementName,
891 Class<?> classType,
892 int arrayMinLength,
893 int arrayMaxLength) {
894 Element element = getElement(elementName);
895 ObjectValue obj = new ObjectValue();
896 obj.valueType = VALUE_LIST;
897 obj.classType = classType;
898 obj.arrayMinLength = arrayMinLength;
899 obj.arrayMaxLength = arrayMaxLength;
900
901 element.objectValue = obj;
902 }
903
904 /**
905 * Disallows an <code>Object</code> reference from being stored in
906 * nodes implementing the named element.
907 *
908 * @param elementName the name of the element.
909 *
910 * @exception IllegalArgumentException if <code>elementName</code> is
911 * not a legal element name for this format.
912 */
913 protected void removeObjectValue(String elementName) {
914 Element element = getElement(elementName);
915 element.objectValue = null;
916 }
917
918 // Utility method
919
920 // Methods from IIOMetadataFormat
921
922 // Root
923
924 public String getRootName() {
925 return rootName;
926 }
927
928 // Multiplicity
929
930 public abstract boolean canNodeAppear(String elementName,
931 ImageTypeSpecifier imageType);
932
933 public int getElementMinChildren(String elementName) {
934 Element element = getElement(elementName);
935 if (element.childPolicy != CHILD_POLICY_REPEAT) {
936 throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
937 }
938 return element.minChildren;
939 }
940
941 public int getElementMaxChildren(String elementName) {
942 Element element = getElement(elementName);
943 if (element.childPolicy != CHILD_POLICY_REPEAT) {
944 throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
945 }
946 return element.maxChildren;
947 }
948
949 private String getResource(String key, Locale locale) {
950 if (locale == null) {
951 locale = Locale.getDefault();
952 }
953
954 /**
955 * If an applet supplies an implementation of IIOMetadataFormat and
956 * resource bundles, then the resource bundle will need to be
957 * accessed via the applet class loader. So first try the context
958 * class loader to locate the resource bundle.
959 * If that throws MissingResourceException, then try the
960 * system class loader.
961 */
962 ClassLoader loader = (ClassLoader)
963 java.security.AccessController.doPrivileged(
964 new java.security.PrivilegedAction() {
965 public Object run() {
966 return Thread.currentThread().getContextClassLoader();
967 }
968 });
969
970 ResourceBundle bundle = null;
971 try {
972 bundle = ResourceBundle.getBundle(resourceBaseName,
973 locale, loader);
974 } catch (MissingResourceException mre) {
975 try {
976 bundle = ResourceBundle.getBundle(resourceBaseName, locale);
977 } catch (MissingResourceException mre1) {
978 return null;
979 }
980 }
981
982 try {
983 return bundle.getString(key);
984 } catch (MissingResourceException e) {
985 return null;
986 }
987 }
988
989 /**
990 * Returns a <code>String</code> containing a description of the
991 * named element, or <code>null</code>. The desciption will be
992 * localized for the supplied <code>Locale</code> if possible.
993 *
994 * <p> The default implementation will first locate a
995 * <code>ResourceBundle</code> using the current resource base
996 * name set by <code>setResourceBaseName</code> and the supplied
997 * <code>Locale</code>, using the fallback mechanism described in
998 * the comments for <code>ResourceBundle.getBundle</code>. If a
999 * <code>ResourceBundle</code> is found, the element name will be
1000 * used as a key to its <code>getString</code> method, and the
1001 * result returned. If no <code>ResourceBundle</code> is found,
1002 * or no such key is present, <code>null</code> will be returned.
1003 *
1004 * <p> If <code>locale</code> is <code>null</code>, the current
1005 * default <code>Locale</code> returned by <code>Locale.getLocale</code>
1006 * will be used.
1007 *
1008 * @param elementName the name of the element.
1009 * @param locale the <code>Locale</code> for which localization
1010 * will be attempted.
1011 *
1012 * @return the element description.
1013 *
1014 * @exception IllegalArgumentException if <code>elementName</code>
1015 * is <code>null</code>, or is not a legal element name for this format.
1016 *
1017 * @see #setResourceBaseName
1018 */
1019 public String getElementDescription(String elementName,
1020 Locale locale) {
1021 Element element = getElement(elementName);
1022 return getResource(elementName, locale);
1023 }
1024
1025 // Children
1026
1027 public int getChildPolicy(String elementName) {
1028 Element element = getElement(elementName);
1029 return element.childPolicy;
1030 }
1031
1032 public String[] getChildNames(String elementName) {
1033 Element element = getElement(elementName);
1034 if (element.childPolicy == CHILD_POLICY_EMPTY) {
1035 return null;
1036 }
1037 return (String[])element.childList.toArray(new String[0]);
1038 }
1039
1040 // Attributes
1041
1042 public String[] getAttributeNames(String elementName) {
1043 Element element = getElement(elementName);
1044 List names = element.attrList;
1045
1046 String[] result = new String[names.size()];
1047 return (String[])names.toArray(result);
1048 }
1049
1050 public int getAttributeValueType(String elementName, String attrName) {
1051 Attribute attr = getAttribute(elementName, attrName);
1052 return attr.valueType;
1053 }
1054
1055 public int getAttributeDataType(String elementName, String attrName) {
1056 Attribute attr = getAttribute(elementName, attrName);
1057 return attr.dataType;
1058 }
1059
1060 public boolean isAttributeRequired(String elementName, String attrName) {
1061 Attribute attr = getAttribute(elementName, attrName);
1062 return attr.required;
1063 }
1064
1065 public String getAttributeDefaultValue(String elementName,
1066 String attrName) {
1067 Attribute attr = getAttribute(elementName, attrName);
1068 return attr.defaultValue;
1069 }
1070
1071 public String[] getAttributeEnumerations(String elementName,
1072 String attrName) {
1073 Attribute attr = getAttribute(elementName, attrName);
1074 if (attr.valueType != VALUE_ENUMERATION) {
1075 throw new IllegalArgumentException
1076 ("Attribute not an enumeration!");
1077 }
1078
1079 List values = attr.enumeratedValues;
1080 Iterator iter = values.iterator();
1081 String[] result = new String[values.size()];
1082 return (String[])values.toArray(result);
1083 }
1084
1085 public String getAttributeMinValue(String elementName, String attrName) {
1086 Attribute attr = getAttribute(elementName, attrName);
1087 if (attr.valueType != VALUE_RANGE &&
1088 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1089 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1090 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1091 throw new IllegalArgumentException("Attribute not a range!");
1092 }
1093
1094 return attr.minValue;
1095 }
1096
1097 public String getAttributeMaxValue(String elementName, String attrName) {
1098 Attribute attr = getAttribute(elementName, attrName);
1099 if (attr.valueType != VALUE_RANGE &&
1100 attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1101 attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1102 attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1103 throw new IllegalArgumentException("Attribute not a range!");
1104 }
1105
1106 return attr.maxValue;
1107 }
1108
1109 public int getAttributeListMinLength(String elementName, String attrName) {
1110 Attribute attr = getAttribute(elementName, attrName);
1111 if (attr.valueType != VALUE_LIST) {
1112 throw new IllegalArgumentException("Attribute not a list!");
1113 }
1114
1115 return attr.listMinLength;
1116 }
1117
1118 public int getAttributeListMaxLength(String elementName, String attrName) {
1119 Attribute attr = getAttribute(elementName, attrName);
1120 if (attr.valueType != VALUE_LIST) {
1121 throw new IllegalArgumentException("Attribute not a list!");
1122 }
1123
1124 return attr.listMaxLength;
1125 }
1126
1127 /**
1128 * Returns a <code>String</code> containing a description of the
1129 * named attribute, or <code>null</code>. The desciption will be
1130 * localized for the supplied <code>Locale</code> if possible.
1131 *
1132 * <p> The default implementation will first locate a
1133 * <code>ResourceBundle</code> using the current resource base
1134 * name set by <code>setResourceBaseName</code> and the supplied
1135 * <code>Locale</code>, using the fallback mechanism described in
1136 * the comments for <code>ResourceBundle.getBundle</code>. If a
1137 * <code>ResourceBundle</code> is found, the element name followed
1138 * by a "/" character followed by the attribute name
1139 * (<code>elementName + "/" + attrName</code>) will be used as a
1140 * key to its <code>getString</code> method, and the result
1141 * returned. If no <code>ResourceBundle</code> is found, or no
1142 * such key is present, <code>null</code> will be returned.
1143 *
1144 * <p> If <code>locale</code> is <code>null</code>, the current
1145 * default <code>Locale</code> returned by <code>Locale.getLocale</code>
1146 * will be used.
1147 *
1148 * @param elementName the name of the element.
1149 * @param attrName the name of the attribute.
1150 * @param locale the <code>Locale</code> for which localization
1151 * will be attempted, or <code>null</code>.
1152 *
1153 * @return the attribute description.
1154 *
1155 * @exception IllegalArgumentException if <code>elementName</code>
1156 * is <code>null</code>, or is not a legal element name for this format.
1157 * @exception IllegalArgumentException if <code>attrName</code> is
1158 * <code>null</code> or is not a legal attribute name for this
1159 * element.
1160 *
1161 * @see #setResourceBaseName
1162 */
1163 public String getAttributeDescription(String elementName,
1164 String attrName,
1165 Locale locale) {
1166 Element element = getElement(elementName);
1167 if (attrName == null) {
1168 throw new IllegalArgumentException("attrName == null!");
1169 }
1170 Attribute attr = (Attribute)element.attrMap.get(attrName);
1171 if (attr == null) {
1172 throw new IllegalArgumentException("No such attribute!");
1173 }
1174
1175 String key = elementName + "/" + attrName;
1176 return getResource(key, locale);
1177 }
1178
1179 private ObjectValue getObjectValue(String elementName) {
1180 Element element = getElement(elementName);
1181 ObjectValue objv = (ObjectValue)element.objectValue;
1182 if (objv == null) {
1183 throw new IllegalArgumentException("No object within element " +
1184 elementName + "!");
1185 }
1186 return objv;
1187 }
1188
1189 public int getObjectValueType(String elementName) {
1190 Element element = getElement(elementName);
1191 ObjectValue objv = (ObjectValue)element.objectValue;
1192 if (objv == null) {
1193 return VALUE_NONE;
1194 }
1195 return objv.valueType;
1196 }
1197
1198 public Class<?> getObjectClass(String elementName) {
1199 ObjectValue objv = getObjectValue(elementName);
1200 return objv.classType;
1201 }
1202
1203 public Object getObjectDefaultValue(String elementName) {
1204 ObjectValue objv = getObjectValue(elementName);
1205 return objv.defaultValue;
1206 }
1207
1208 public Object[] getObjectEnumerations(String elementName) {
1209 ObjectValue objv = getObjectValue(elementName);
1210 if (objv.valueType != VALUE_ENUMERATION) {
1211 throw new IllegalArgumentException("Not an enumeration!");
1212 }
1213 List vlist = objv.enumeratedValues;
1214 Object[] values = new Object[vlist.size()];
1215 return vlist.toArray(values);
1216 }
1217
1218 public Comparable<?> getObjectMinValue(String elementName) {
1219 ObjectValue objv = getObjectValue(elementName);
1220 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1221 throw new IllegalArgumentException("Not a range!");
1222 }
1223 return objv.minValue;
1224 }
1225
1226 public Comparable<?> getObjectMaxValue(String elementName) {
1227 ObjectValue objv = getObjectValue(elementName);
1228 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1229 throw new IllegalArgumentException("Not a range!");
1230 }
1231 return objv.maxValue;
1232 }
1233
1234 public int getObjectArrayMinLength(String elementName) {
1235 ObjectValue objv = getObjectValue(elementName);
1236 if (objv.valueType != VALUE_LIST) {
1237 throw new IllegalArgumentException("Not a list!");
1238 }
1239 return objv.arrayMinLength;
1240 }
1241
1242 public int getObjectArrayMaxLength(String elementName) {
1243 ObjectValue objv = getObjectValue(elementName);
1244 if (objv.valueType != VALUE_LIST) {
1245 throw new IllegalArgumentException("Not a list!");
1246 }
1247 return objv.arrayMaxLength;
1248 }
1249
1250 // Standard format descriptor
1251
1252 private synchronized static void createStandardFormat() {
1253 if (standardFormat == null) {
1254 standardFormat = new StandardMetadataFormat();
1255 }
1256 }
1257
1258 /**
1259 * Returns an <code>IIOMetadataFormat</code> object describing the
1260 * standard, plug-in neutral <code>javax.imageio_1.0</code>
1261 * metadata document format described in the comment of the
1262 * <code>javax.imageio.metadata</code> package.
1263 *
1264 * @return a predefined <code>IIOMetadataFormat</code> instance.
1265 */
1266 public static IIOMetadataFormat getStandardFormatInstance() {
1267 createStandardFormat();
1268 return standardFormat;
1269 }
1270}