IIOMetadataFormatImpl.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package javax.imageio.metadata;
19
20import javax.imageio.ImageTypeSpecifier;
21import java.util.*;
22import java.security.AccessController;
23import java.security.PrivilegedAction;
24
25/**
26 * The IIOMetadataFormatImpl class provides an implementation of the
27 * IIOMetadataFormat interface.
28 *
29 * @since Android 1.0
30 */
31public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
32
33    /**
34     * The Constant standardMetadataFormatName.
35     */
36    @SuppressWarnings( {
37        "ConstantDeclaredInAbstractClass"
38    })
39    public static final String standardMetadataFormatName = "javax_imageio_1.0";
40
41    /**
42     * The standard format.
43     */
44    @SuppressWarnings( {
45        "StaticNonFinalField"
46    })
47    private static IIOMetadataFormatImpl standardFormat;
48
49    /**
50     * The root name.
51     */
52    private String rootName;
53
54    /**
55     * The element hash.
56     */
57    private HashMap<String, Element> elementHash = new HashMap<String, Element>();
58
59    /**
60     * The resource base name.
61     */
62    private String resourceBaseName = getClass().getName() + "Resources";
63
64    /**
65     * Instantiates an IIOMetadataFormatImpl with the specified root name and
66     * child policy (not CHILD_POLICY_REPEAT).
67     *
68     * @param rootName
69     *            the name of root element.
70     * @param childPolicy
71     *            the child policy defined by one of the CHILD_POLICY_*
72     *            constants (except CHILD_POLICY_REPEAT).
73     */
74    public IIOMetadataFormatImpl(String rootName, int childPolicy) {
75        if (rootName == null) {
76            throw new IllegalArgumentException("rootName is null");
77        }
78        if (childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX
79                || childPolicy == CHILD_POLICY_REPEAT) {
80            throw new IllegalArgumentException("childPolicy is not one of the predefined constants");
81        }
82
83        this.rootName = rootName;
84        Element root = new Element();
85        root.name = rootName;
86        root.childPolicy = childPolicy;
87        elementHash.put(rootName, root);
88    }
89
90    /**
91     * Instantiates an IIOMetadataFormatImpl with the specified root name and
92     * CHILD_POLICY_REPEAT child policy.
93     *
94     * @param rootName
95     *            the name of root element.
96     * @param minChildren
97     *            the minimum number of children.
98     * @param maxChildren
99     *            the maximum number of children
100     */
101    public IIOMetadataFormatImpl(String rootName, int minChildren, int maxChildren) {
102        if (rootName == null) {
103            throw new IllegalArgumentException("rootName is null");
104        }
105        if (minChildren < 0) {
106            throw new IllegalArgumentException("minChildren < 0!");
107        }
108        if (minChildren > maxChildren) {
109            throw new IllegalArgumentException("minChildren > maxChildren!");
110        }
111
112        this.rootName = rootName;
113        Element root = new Element();
114        root.name = rootName;
115        root.minChildren = minChildren;
116        root.maxChildren = maxChildren;
117        root.childPolicy = CHILD_POLICY_REPEAT;
118        elementHash.put(rootName, root);
119    }
120
121    @SuppressWarnings( {
122        "AbstractMethodOverridesAbstractMethod"
123    })
124    public abstract boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType);
125
126    /**
127     * Adds a new attribute to an existing element.
128     *
129     * @param elementName
130     *            the name of the element to which the new attribute will be
131     *            added.
132     * @param attrName
133     *            the attribute name.
134     * @param dataType
135     *            the data type of the new attribute.
136     * @param required
137     *            the flag which indicates whether this attribute must be
138     *            present.
139     * @param listMinLength
140     *            the minimum legal number of list items.
141     * @param listMaxLength
142     *            the the maximum legal number of list items.
143     */
144    protected void addAttribute(String elementName, String attrName, int dataType,
145            boolean required, int listMinLength, int listMaxLength) {
146        if (attrName == null) {
147            throw new IllegalArgumentException("attrName == null!");
148        }
149        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
150            throw new IllegalArgumentException("Invalid value for dataType!");
151        }
152        if (listMinLength < 0 || listMinLength > listMaxLength) {
153            throw new IllegalArgumentException("Invalid list bounds!");
154        }
155
156        Element element = findElement(elementName);
157        Attlist attr = new Attlist();
158        attr.name = attrName;
159        attr.dataType = dataType;
160        attr.required = required;
161        attr.listMinLength = listMinLength;
162        attr.listMaxLength = listMaxLength;
163        attr.valueType = VALUE_LIST;
164
165        element.attributes.put(attrName, attr);
166    }
167
168    /**
169     * Adds a new attribute to an existing element.
170     *
171     * @param elementName
172     *            the name of the element to which the new attribute will be
173     *            added.
174     * @param attrName
175     *            the attribute name.
176     * @param dataType
177     *            the data type of the new attribute.
178     * @param required
179     *            the flag which indicates whether this attribute must be
180     *            present.
181     * @param defaultValue
182     *            the default value of the attribute.
183     */
184    protected void addAttribute(String elementName, String attrName, int dataType,
185            boolean required, String defaultValue) {
186        if (attrName == null) {
187            throw new IllegalArgumentException("attrName == null!");
188        }
189        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
190            throw new IllegalArgumentException("Invalid value for dataType!");
191        }
192
193        Element element = findElement(elementName);
194        Attlist attr = new Attlist();
195        attr.name = attrName;
196        attr.dataType = dataType;
197        attr.required = required;
198        attr.defaultValue = defaultValue;
199        attr.valueType = VALUE_ARBITRARY;
200
201        element.attributes.put(attrName, attr);
202    }
203
204    /**
205     * Adds a new attribute to an existing element.
206     *
207     * @param elementName
208     *            the name of the element to which the new attribute will be
209     *            added.
210     * @param attrName
211     *            the attribute name.
212     * @param dataType
213     *            the data type of the new attribute.
214     * @param required
215     *            the flag which indicates whether this attribute must be
216     *            present.
217     * @param defaultValue
218     *            the default value of the attribute.
219     * @param enumeratedValues
220     *            the legal values for the attribute as a list of strings.
221     */
222    protected void addAttribute(String elementName, String attrName, int dataType,
223            boolean required, String defaultValue, List<String> enumeratedValues) {
224        if (attrName == null) {
225            throw new IllegalArgumentException("attrName == null!");
226        }
227        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
228            throw new IllegalArgumentException("Invalid value for dataType!");
229        }
230        if (enumeratedValues == null || enumeratedValues.isEmpty()) {
231            throw new IllegalArgumentException("enumeratedValues is empty or null");
232        }
233
234        try {
235            for (String enumeratedValue : enumeratedValues) {
236                if (enumeratedValue == null) {
237                    throw new IllegalArgumentException("enumeratedValues contains a null!");
238                }
239            }
240        } catch (ClassCastException e) {
241            throw new IllegalArgumentException("enumeratedValues contains a non-String value!");
242        }
243
244        Element element = findElement(elementName);
245        Attlist attr = new Attlist();
246        attr.name = attrName;
247        attr.dataType = dataType;
248        attr.required = required;
249        attr.defaultValue = defaultValue;
250        attr.enumeratedValues = enumeratedValues;
251        attr.valueType = VALUE_ENUMERATION;
252
253        element.attributes.put(attrName, attr);
254    }
255
256    /**
257     * Adds a new attribute to an existing element.
258     *
259     * @param elementName
260     *            the name of the element to which the new attribute will be
261     *            added.
262     * @param attrName
263     *            the attribute name.
264     * @param dataType
265     *            the data type of the new attribute.
266     * @param required
267     *            the flag which indicates whether this attribute must be
268     *            present.
269     * @param defaultValue
270     *            the default value of attribute.
271     * @param minValue
272     *            the minimum legal value of an attribute.
273     * @param maxValue
274     *            the maximum legal value of an attribute.
275     * @param minInclusive
276     *            the flag which indicates whether the minValue is inclusive.
277     * @param maxInclusive
278     *            the flag which indicates whether the maxValue is inclusive.
279     */
280    protected void addAttribute(String elementName, String attrName, int dataType,
281            boolean required, String defaultValue, String minValue, String maxValue,
282            boolean minInclusive, boolean maxInclusive) {
283        if (attrName == null) {
284            throw new IllegalArgumentException("attrName == null!");
285        }
286        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
287            throw new IllegalArgumentException("Invalid value for dataType!");
288        }
289
290        Element element = findElement(elementName);
291        Attlist attr = new Attlist();
292        attr.name = attrName;
293        attr.dataType = dataType;
294        attr.required = required;
295        attr.defaultValue = defaultValue;
296        attr.minValue = minValue;
297        attr.maxValue = maxValue;
298        attr.minInclusive = minInclusive;
299        attr.maxInclusive = maxInclusive;
300
301        attr.valueType = VALUE_RANGE;
302        attr.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0;
303        attr.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0;
304
305        element.attributes.put(attrName, attr);
306    }
307
308    /**
309     * Adds a new attribute with boolean data type to an existing element.
310     *
311     * @param elementName
312     *            the name of the element to which the new attribute will be
313     *            added.
314     * @param attrName
315     *            the attribute name.
316     * @param hasDefaultValue
317     *            the flag which indicates whether this attribute must have a
318     *            default value.
319     * @param defaultValue
320     *            the default value.
321     */
322    protected void addBooleanAttribute(String elementName, String attrName,
323            boolean hasDefaultValue, boolean defaultValue) {
324        String defaultVal = hasDefaultValue ? (defaultValue ? "TRUE" : "FALSE") : null;
325        ArrayList<String> values = new ArrayList<String>(2);
326        values.add("TRUE");
327        values.add("FALSE");
328
329        addAttribute(elementName, attrName, DATATYPE_BOOLEAN, true, defaultVal, values);
330    }
331
332    /**
333     * Adds an existing element to the list of child elements of the specified
334     * parent element.
335     *
336     * @param elementName
337     *            the name of the element to be added.
338     * @param parentName
339     *            the parent element name.
340     */
341    protected void addChildElement(String elementName, String parentName) {
342        Element parent = findElement(parentName);
343        Element element = findElement(elementName);
344        parent.children.add(element.name);
345    }
346
347    /**
348     * Adds a new element type to this IIOMetadataFormat with a child policy (if
349     * policy is not CHILD_POLICY_REPEAT).
350     *
351     * @param elementName
352     *            the name of the element to be added.
353     * @param parentName
354     *            the parent element name.
355     * @param childPolicy
356     *            one of the CHILD_POLICY_* constants defined by
357     *            IIOMetadataFormat.
358     */
359    protected void addElement(String elementName, String parentName, int childPolicy) {
360        if (childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX
361                || childPolicy == CHILD_POLICY_REPEAT) {
362            throw new IllegalArgumentException("childPolicy is not one of the predefined constants");
363        }
364
365        Element parent = findElement(parentName);
366        Element element = new Element();
367        element.name = elementName;
368        element.childPolicy = childPolicy;
369        elementHash.put(elementName, element);
370        parent.children.add(elementName);
371    }
372
373    /**
374     * Adds a new element type to this IIOMetadataFormat with
375     * CHILD_POLICY_REPEAT and the specified minimum and maximum number of child
376     * elements.
377     *
378     * @param elementName
379     *            the element name to be added.
380     * @param parentName
381     *            the parent element name.
382     * @param minChildren
383     *            the minimum number of child elements.
384     * @param maxChildren
385     *            the maximum number of child elements.
386     */
387    protected void addElement(String elementName, String parentName, int minChildren,
388            int maxChildren) {
389        if (minChildren < 0) {
390            throw new IllegalArgumentException("minChildren < 0!");
391        }
392        if (minChildren > maxChildren) {
393            throw new IllegalArgumentException("minChildren > maxChildren!");
394        }
395
396        Element parent = findElement(parentName);
397        Element element = new Element();
398        element.name = elementName;
399        element.childPolicy = CHILD_POLICY_REPEAT;
400        element.minChildren = minChildren;
401        element.maxChildren = maxChildren;
402        elementHash.put(elementName, element);
403        parent.children.add(elementName);
404    }
405
406    /**
407     * Adds an Object reference with the specified class type to be stored as
408     * element's value.
409     *
410     * @param elementName
411     *            the element name.
412     * @param classType
413     *            the class indicates the legal types for the object's value.
414     * @param arrayMinLength
415     *            the minimum legal length for the array.
416     * @param arrayMaxLength
417     *            the maximum legal length for the array.
418     */
419    protected void addObjectValue(String elementName, Class<?> classType, int arrayMinLength,
420            int arrayMaxLength) {
421        Element element = findElement(elementName);
422
423        ObjectValue objVal = new ObjectValue();
424        objVal.classType = classType;
425        objVal.arrayMaxLength = arrayMaxLength;
426        objVal.arrayMinLength = arrayMinLength;
427        objVal.valueType = VALUE_LIST;
428
429        element.objectValue = objVal;
430    }
431
432    /**
433     * Adds an Object reference with the specified class type to be stored as an
434     * element's value.
435     *
436     * @param elementName
437     *            the element name.
438     * @param classType
439     *            the class indicates the legal types for the object's value.
440     * @param required
441     *            a flag indicated that this object value must be present.
442     * @param defaultValue
443     *            the default value, or null.
444     */
445    protected <T> void addObjectValue(String elementName, Class<T> classType, boolean required,
446            T defaultValue) {
447        // note: reqired is an unused parameter
448        Element element = findElement(elementName);
449
450        ObjectValue<T> objVal = new ObjectValue<T>();
451        objVal.classType = classType;
452        objVal.defaultValue = defaultValue;
453        objVal.valueType = VALUE_ARBITRARY;
454
455        element.objectValue = objVal;
456    }
457
458    /**
459     * Adds an Object reference with the specified class type to be stored as
460     * the element's value.
461     *
462     * @param elementName
463     *            the element name.
464     * @param classType
465     *            the class indicates the legal types for the object value.
466     * @param required
467     *            a flag indicated that this object value must be present.
468     * @param defaultValue
469     *            the default value, or null.
470     * @param enumeratedValues
471     *            the list of legal values for the object.
472     */
473    protected <T> void addObjectValue(String elementName, Class<T> classType, boolean required,
474            T defaultValue, List<? extends T> enumeratedValues) {
475        // note: reqired is an unused parameter
476        if (enumeratedValues == null || enumeratedValues.isEmpty()) {
477            throw new IllegalArgumentException("enumeratedValues is empty or null");
478        }
479
480        try {
481            for (T enumeratedValue : enumeratedValues) {
482                if (enumeratedValue == null) {
483                    throw new IllegalArgumentException("enumeratedValues contains a null!");
484                }
485            }
486        } catch (ClassCastException e) {
487            throw new IllegalArgumentException(
488                    "enumeratedValues contains a value not of class classType!");
489        }
490
491        Element element = findElement(elementName);
492
493        ObjectValue<T> objVal = new ObjectValue<T>();
494        objVal.classType = classType;
495        objVal.defaultValue = defaultValue;
496        objVal.enumeratedValues = enumeratedValues;
497        objVal.valueType = VALUE_ENUMERATION;
498
499        element.objectValue = objVal;
500    }
501
502    /**
503     * Adds an Object reference with the specified class type to be stored as
504     * the element's value.
505     *
506     * @param elementName
507     *            the element name.
508     * @param classType
509     *            the class indicates the legal types for the object value.
510     * @param defaultValue
511     *            the default value, or null.
512     * @param minValue
513     *            the minimum legal value for the object value.
514     * @param maxValue
515     *            the maximum legal value for the object value.
516     * @param minInclusive
517     *            the flag which indicates whether the minValue is inclusive.
518     * @param maxInclusive
519     *            the flag which indicates whether the maxValue is inclusive.
520     */
521    protected <T extends Object & Comparable<? super T>> void addObjectValue(String elementName,
522            Class<T> classType, T defaultValue, Comparable<? super T> minValue,
523            Comparable<? super T> maxValue, boolean minInclusive, boolean maxInclusive) {
524        Element element = findElement(elementName);
525
526        ObjectValue<T> objVal = new ObjectValue<T>();
527        objVal.classType = classType;
528        objVal.defaultValue = defaultValue;
529        objVal.minValue = minValue;
530        objVal.maxValue = maxValue;
531        objVal.minInclusive = minInclusive;
532        objVal.maxInclusive = maxInclusive;
533
534        objVal.valueType = VALUE_RANGE;
535        objVal.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0;
536        objVal.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0;
537
538        element.objectValue = objVal;
539    }
540
541    public int getAttributeDataType(String elementName, String attrName) {
542        Attlist attr = findAttribute(elementName, attrName);
543        return attr.dataType;
544    }
545
546    public String getAttributeDefaultValue(String elementName, String attrName) {
547        Attlist attr = findAttribute(elementName, attrName);
548        return attr.defaultValue;
549    }
550
551    public String getAttributeDescription(String elementName, String attrName, Locale locale) {
552        findAttribute(elementName, attrName);
553        return getResourceString(elementName + "/" + attrName, locale);
554    }
555
556    public String[] getAttributeEnumerations(String elementName, String attrName) {
557        Attlist attr = findAttribute(elementName, attrName);
558        if (attr.valueType != VALUE_ENUMERATION) {
559            throw new IllegalArgumentException("Attribute is not an enumeration!");
560        }
561
562        return attr.enumeratedValues.toArray(new String[attr.enumeratedValues.size()]);
563    }
564
565    public int getAttributeListMaxLength(String elementName, String attrName) {
566        Attlist attr = findAttribute(elementName, attrName);
567        if (attr.valueType != VALUE_LIST) {
568            throw new IllegalArgumentException("Attribute is not a list!");
569        }
570        return attr.listMaxLength;
571    }
572
573    public int getAttributeListMinLength(String elementName, String attrName) {
574        Attlist attr = findAttribute(elementName, attrName);
575        if (attr.valueType != VALUE_LIST) {
576            throw new IllegalArgumentException("Attribute is not a list!");
577        }
578        return attr.listMinLength;
579    }
580
581    public String getAttributeMaxValue(String elementName, String attrName) {
582        Attlist attr = findAttribute(elementName, attrName);
583        if ((attr.valueType & VALUE_RANGE) == 0) {
584            throw new IllegalArgumentException("Attribute is not a range!");
585        }
586        return attr.maxValue;
587    }
588
589    public String getAttributeMinValue(String elementName, String attrName) {
590        Attlist attr = findAttribute(elementName, attrName);
591        if ((attr.valueType & VALUE_RANGE) == 0) {
592            throw new IllegalArgumentException("Attribute is not a range!");
593        }
594        return attr.minValue;
595    }
596
597    public String[] getAttributeNames(String elementName) {
598        Element element = findElement(elementName);
599        return element.attributes.keySet().toArray(new String[element.attributes.size()]);
600    }
601
602    public int getAttributeValueType(String elementName, String attrName) {
603        Attlist attr = findAttribute(elementName, attrName);
604        return attr.valueType;
605    }
606
607    public String[] getChildNames(String elementName) {
608        Element element = findElement(elementName);
609        if (element.childPolicy == CHILD_POLICY_EMPTY) { // Element cannot have
610            // children
611            return null;
612        }
613        return element.children.toArray(new String[element.children.size()]);
614    }
615
616    public int getChildPolicy(String elementName) {
617        Element element = findElement(elementName);
618        return element.childPolicy;
619    }
620
621    public String getElementDescription(String elementName, Locale locale) {
622        findElement(elementName); // Check if there is such element
623        return getResourceString(elementName, locale);
624    }
625
626    public int getElementMaxChildren(String elementName) {
627        Element element = findElement(elementName);
628        if (element.childPolicy != CHILD_POLICY_REPEAT) {
629            throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!");
630        }
631        return element.maxChildren;
632    }
633
634    public int getElementMinChildren(String elementName) {
635        Element element = findElement(elementName);
636        if (element.childPolicy != CHILD_POLICY_REPEAT) {
637            throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!");
638        }
639        return element.minChildren;
640    }
641
642    public int getObjectArrayMaxLength(String elementName) {
643        Element element = findElement(elementName);
644        ObjectValue v = element.objectValue;
645        if (v == null || v.valueType != VALUE_LIST) {
646            throw new IllegalArgumentException("Not a list!");
647        }
648        return v.arrayMaxLength;
649    }
650
651    public int getObjectArrayMinLength(String elementName) {
652        Element element = findElement(elementName);
653        ObjectValue v = element.objectValue;
654        if (v == null || v.valueType != VALUE_LIST) {
655            throw new IllegalArgumentException("Not a list!");
656        }
657        return v.arrayMinLength;
658    }
659
660    public Class<?> getObjectClass(String elementName) {
661        ObjectValue v = findObjectValue(elementName);
662        return v.classType;
663    }
664
665    public Object getObjectDefaultValue(String elementName) {
666        ObjectValue v = findObjectValue(elementName);
667        return v.defaultValue;
668    }
669
670    public Object[] getObjectEnumerations(String elementName) {
671        Element element = findElement(elementName);
672        ObjectValue v = element.objectValue;
673        if (v == null || v.valueType != VALUE_ENUMERATION) {
674            throw new IllegalArgumentException("Not an enumeration!");
675        }
676        return v.enumeratedValues.toArray();
677    }
678
679    public Comparable<?> getObjectMaxValue(String elementName) {
680        Element element = findElement(elementName);
681        ObjectValue v = element.objectValue;
682        if (v == null || (v.valueType & VALUE_RANGE) == 0) {
683            throw new IllegalArgumentException("Not a range!");
684        }
685        return v.maxValue;
686    }
687
688    public Comparable<?> getObjectMinValue(String elementName) {
689        Element element = findElement(elementName);
690        ObjectValue v = element.objectValue;
691        if (v == null || (v.valueType & VALUE_RANGE) == 0) {
692            throw new IllegalArgumentException("Not a range!");
693        }
694        return v.minValue;
695    }
696
697    public int getObjectValueType(String elementName) {
698        Element element = findElement(elementName);
699        if (element.objectValue == null) {
700            return VALUE_NONE;
701        }
702        return element.objectValue.valueType;
703    }
704
705    /**
706     * Gets the resource base name for locating ResourceBundles.
707     *
708     * @return the current resource base name.
709     */
710    protected String getResourceBaseName() {
711        return resourceBaseName;
712    }
713
714    public String getRootName() {
715        return rootName;
716    }
717
718    /**
719     * Gets the standard format instance.
720     *
721     * @return the IIOMetadataFormat instance.
722     */
723    public static IIOMetadataFormat getStandardFormatInstance() {
724        if (standardFormat == null) {
725            standardFormat = new IIOStandardMetadataFormat();
726        }
727
728        return standardFormat;
729    }
730
731    public boolean isAttributeRequired(String elementName, String attrName) {
732        return findAttribute(elementName, attrName).required;
733    }
734
735    /**
736     * Removes the specified attribute from the specified element.
737     *
738     * @param elementName
739     *            the specified element name.
740     * @param attrName
741     *            the specified attribute name.
742     */
743    protected void removeAttribute(String elementName, String attrName) {
744        Element element = findElement(elementName);
745        element.attributes.remove(attrName);
746    }
747
748    /**
749     * Removes the specified element from this format.
750     *
751     * @param elementName
752     *            the specified element name.
753     */
754    protected void removeElement(String elementName) {
755        Element element;
756        if ((element = elementHash.get(elementName)) != null) {
757            elementHash.remove(elementName);
758            for (Element e : elementHash.values()) {
759                e.children.remove(element.name);
760            }
761        }
762    }
763
764    /**
765     * Removes the object value from the specified element.
766     *
767     * @param elementName
768     *            the element name.
769     */
770    protected void removeObjectValue(String elementName) {
771        Element element = findElement(elementName);
772        element.objectValue = null;
773    }
774
775    /**
776     * Sets a new base name for ResourceBundles containing descriptions of
777     * elements and attributes for this format.
778     *
779     * @param resourceBaseName
780     *            the new resource base name.
781     */
782    protected void setResourceBaseName(String resourceBaseName) {
783        if (resourceBaseName == null) {
784            throw new IllegalArgumentException("resourceBaseName == null!");
785        }
786        this.resourceBaseName = resourceBaseName;
787    }
788
789    /**
790     * The Class Element.
791     */
792    @SuppressWarnings( {
793        "ClassWithoutConstructor"
794    })
795    private class Element {
796
797        /**
798         * The name.
799         */
800        String name;
801
802        /**
803         * The children.
804         */
805        ArrayList<String> children = new ArrayList<String>();
806
807        /**
808         * The attributes.
809         */
810        HashMap<String, Attlist> attributes = new HashMap<String, Attlist>();
811
812        /**
813         * The min children.
814         */
815        int minChildren;
816
817        /**
818         * The max children.
819         */
820        int maxChildren;
821
822        /**
823         * The child policy.
824         */
825        int childPolicy;
826
827        /**
828         * The object value.
829         */
830        ObjectValue objectValue;
831    }
832
833    /**
834     * The Class Attlist.
835     */
836    @SuppressWarnings( {
837        "ClassWithoutConstructor"
838    })
839    private class Attlist {
840
841        /**
842         * The name.
843         */
844        String name;
845
846        /**
847         * The data type.
848         */
849        int dataType;
850
851        /**
852         * The required.
853         */
854        boolean required;
855
856        /**
857         * The list min length.
858         */
859        int listMinLength;
860
861        /**
862         * The list max length.
863         */
864        int listMaxLength;
865
866        /**
867         * The default value.
868         */
869        String defaultValue;
870
871        /**
872         * The enumerated values.
873         */
874        List<String> enumeratedValues;
875
876        /**
877         * The min value.
878         */
879        String minValue;
880
881        /**
882         * The max value.
883         */
884        String maxValue;
885
886        /**
887         * The min inclusive.
888         */
889        boolean minInclusive;
890
891        /**
892         * The max inclusive.
893         */
894        boolean maxInclusive;
895
896        /**
897         * The value type.
898         */
899        int valueType;
900    }
901
902    /**
903     * The Class ObjectValue.
904     */
905    @SuppressWarnings( {
906        "ClassWithoutConstructor"
907    })
908    private class ObjectValue<T> {
909
910        /**
911         * The class type.
912         */
913        Class<T> classType;
914
915        /**
916         * The array min length.
917         */
918        int arrayMinLength;
919
920        /**
921         * The array max length.
922         */
923        int arrayMaxLength;
924
925        /**
926         * The default value.
927         */
928        T defaultValue;
929
930        /**
931         * The enumerated values.
932         */
933        List<? extends T> enumeratedValues;
934
935        /**
936         * The min value.
937         */
938        Comparable<? super T> minValue;
939
940        /**
941         * The max value.
942         */
943        Comparable<? super T> maxValue;
944
945        /**
946         * The min inclusive.
947         */
948        boolean minInclusive;
949
950        /**
951         * The max inclusive.
952         */
953        boolean maxInclusive;
954
955        /**
956         * The value type.
957         */
958        int valueType;
959    }
960
961    /**
962     * Find element.
963     *
964     * @param name
965     *            the name.
966     * @return the element.
967     */
968    private Element findElement(String name) {
969        Element element;
970        if ((element = elementHash.get(name)) == null) {
971            throw new IllegalArgumentException("element name is null or no such element: " + name);
972        }
973
974        return element;
975    }
976
977    /**
978     * Find attribute.
979     *
980     * @param elementName
981     *            the element name.
982     * @param attributeName
983     *            the attribute name.
984     * @return the attlist.
985     */
986    private Attlist findAttribute(String elementName, String attributeName) {
987        Element element = findElement(elementName);
988        Attlist attribute;
989        if ((attribute = element.attributes.get(attributeName)) == null) {
990            throw new IllegalArgumentException("attribute name is null or no such attribute: "
991                    + attributeName);
992        }
993
994        return attribute;
995    }
996
997    /**
998     * Find object value.
999     *
1000     * @param elementName
1001     *            the element name.
1002     * @return the object value.
1003     */
1004    private ObjectValue findObjectValue(String elementName) {
1005        Element element = findElement(elementName);
1006        ObjectValue v = element.objectValue;
1007        if (v == null) {
1008            throw new IllegalArgumentException("No object within element");
1009        }
1010        return v;
1011    }
1012
1013    /**
1014     * Gets the resource string.
1015     *
1016     * @param key
1017     *            the key.
1018     * @param locale
1019     *            the locale.
1020     * @return the resource string.
1021     */
1022    private String getResourceString(String key, Locale locale) {
1023        if (locale == null) {
1024            locale = Locale.getDefault();
1025        }
1026
1027        // Get the context class loader and try to locate the bundle with it
1028        // first
1029        ClassLoader contextClassloader = AccessController
1030                .doPrivileged(new PrivilegedAction<ClassLoader>() {
1031                    public ClassLoader run() {
1032                        return Thread.currentThread().getContextClassLoader();
1033                    }
1034                });
1035
1036        // Now try to get the resource bundle
1037        ResourceBundle rb;
1038        try {
1039            rb = ResourceBundle.getBundle(resourceBaseName, locale, contextClassloader);
1040        } catch (MissingResourceException e) {
1041            try {
1042                rb = ResourceBundle.getBundle(resourceBaseName, locale);
1043            } catch (MissingResourceException e1) {
1044                return null;
1045            }
1046        }
1047
1048        try {
1049            return rb.getString(key);
1050        } catch (MissingResourceException e) {
1051            return null;
1052        } catch (ClassCastException e) {
1053            return null; // Not a string resource
1054        }
1055    }
1056}
1057