Path: blob/master/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java
41153 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.imageio.metadata;2627import java.util.ArrayList;28import java.util.Collection;29import java.util.HashMap;30import java.util.Iterator;31import java.util.List;32import java.util.Locale;33import java.util.Map;34import java.util.MissingResourceException;35import java.util.ResourceBundle;36import javax.imageio.ImageTypeSpecifier;37import com.sun.imageio.plugins.common.StandardMetadataFormat;3839/**40* A concrete class providing a reusable implementation of the41* {@code IIOMetadataFormat} interface. In addition, a static42* instance representing the standard, plug-in neutral43* {@code javax_imageio_1.0} format is provided by the44* {@code getStandardFormatInstance} method.45*46* <p> In order to supply localized descriptions of elements and47* attributes, a {@code ResourceBundle} with a base name of48* {@code this.getClass().getName() + "Resources"} should be49* supplied via the usual mechanism used by50* {@code ResourceBundle.getBundle}. Briefly, the subclasser51* supplies one or more additional classes according to a naming52* convention (by default, the fully-qualified name of the subclass53* extending {@code IIMetadataFormatImpl}, plus the string54* "Resources", plus the country, language, and variant codes55* separated by underscores). At run time, calls to56* {@code getElementDescription} or57* {@code getAttributeDescription} will attempt to load such58* classes dynamically according to the supplied locale, and will use59* either the element name, or the element name followed by a '/'60* character followed by the attribute name as a key. This key will61* be supplied to the {@code ResourceBundle}'s62* {@code getString} method, and the resulting localized63* description of the node or attribute is returned.64*65* <p> The subclass may supply a different base name for the resource66* bundles using the {@code setResourceBaseName} method.67*68* <p> A subclass may choose its own localization mechanism, if so69* desired, by overriding the supplied implementations of70* {@code getElementDescription} and71* {@code getAttributeDescription}.72*73* @see ResourceBundle#getBundle(String,Locale)74*75*/76public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {7778/**79* A {@code String} constant containing the standard format80* name, {@code "javax_imageio_1.0"}.81*/82public static final String standardMetadataFormatName =83"javax_imageio_1.0";8485private static IIOMetadataFormat standardFormat = null;8687private String resourceBaseName = this.getClass().getName() + "Resources";8889private String rootName;9091// Element name (String) -> Element92private HashMap<String, Element> elementMap = new HashMap<>();9394class Element {95String elementName;9697int childPolicy;98int minChildren = 0;99int maxChildren = 0;100101// Child names (Strings)102List<String> childList = new ArrayList<>();103104// Parent names (Strings)105List<String> parentList = new ArrayList<>();106107// List of attribute names in the order they were added108List<String> attrList = new ArrayList<>();109// Attr name (String) -> Attribute110Map<String, Attribute> attrMap = new HashMap<>();111112ObjectValue<?> objectValue;113}114115class Attribute {116String attrName;117118int valueType = VALUE_ARBITRARY;119int dataType;120boolean required;121String defaultValue = null;122123// enumeration124List<String> enumeratedValues;125126// range127String minValue;128String maxValue;129130// list131int listMinLength;132int listMaxLength;133}134135class ObjectValue<T> {136int valueType = VALUE_NONE;137// ? extends T So that ObjectValue<Object> can take Class<?>138Class<? extends T> classType = null;139T defaultValue = null;140141// Meaningful only if valueType == VALUE_ENUMERATION142List<? extends T> enumeratedValues = null;143144// Meaningful only if valueType == VALUE_RANGE145Comparable<? super T> minValue = null;146Comparable<? super T> maxValue = null;147148// Meaningful only if valueType == VALUE_LIST149int arrayMinLength = 0;150int arrayMaxLength = 0;151}152153/**154* Constructs a blank {@code IIOMetadataFormatImpl} instance,155* with a given root element name and child policy (other than156* {@code CHILD_POLICY_REPEAT}). Additional elements, and157* their attributes and {@code Object} reference information158* may be added using the various {@code add} methods.159*160* @param rootName the name of the root element.161* @param childPolicy one of the {@code CHILD_POLICY_*} constants,162* other than {@code CHILD_POLICY_REPEAT}.163*164* @exception IllegalArgumentException if {@code rootName} is165* {@code null}.166* @exception IllegalArgumentException if {@code childPolicy} is167* not one of the predefined constants.168*/169public IIOMetadataFormatImpl(String rootName,170int childPolicy) {171if (rootName == null) {172throw new IllegalArgumentException("rootName == null!");173}174if (childPolicy < CHILD_POLICY_EMPTY ||175childPolicy > CHILD_POLICY_MAX ||176childPolicy == CHILD_POLICY_REPEAT) {177throw new IllegalArgumentException("Invalid value for childPolicy!");178}179180this.rootName = rootName;181182Element root = new Element();183root.elementName = rootName;184root.childPolicy = childPolicy;185186elementMap.put(rootName, root);187}188189/**190* Constructs a blank {@code IIOMetadataFormatImpl} instance,191* with a given root element name and a child policy of192* {@code CHILD_POLICY_REPEAT}. Additional elements, and193* their attributes and {@code Object} reference information194* may be added using the various {@code add} 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} is201* {@code null}.202* @exception IllegalArgumentException if {@code minChildren}203* is negative or larger than {@code maxChildren}.204*/205public IIOMetadataFormatImpl(String rootName,206int minChildren,207int maxChildren) {208if (rootName == null) {209throw new IllegalArgumentException("rootName == null!");210}211if (minChildren < 0) {212throw new IllegalArgumentException("minChildren < 0!");213}214if (minChildren > maxChildren) {215throw new IllegalArgumentException("minChildren > maxChildren!");216}217218Element root = new Element();219root.elementName = rootName;220root.childPolicy = CHILD_POLICY_REPEAT;221root.minChildren = minChildren;222root.maxChildren = maxChildren;223224this.rootName = rootName;225elementMap.put(rootName, root);226}227228/**229* Sets a new base name for locating {@code ResourceBundle}s230* containing descriptions of elements and attributes for this231* format.232*233* <p> Prior to the first time this method is called, the base234* name will be equal to235* {@code this.getClass().getName() + "Resources"}.236*237* @param resourceBaseName a {@code String} containing the new238* base name.239*240* @exception IllegalArgumentException if241* {@code resourceBaseName} is {@code null}.242*243* @see #getResourceBaseName244*/245protected void setResourceBaseName(String resourceBaseName) {246if (resourceBaseName == null) {247throw new IllegalArgumentException("resourceBaseName == null!");248}249this.resourceBaseName = resourceBaseName;250}251252/**253* Returns the currently set base name for locating254* {@code ResourceBundle}s.255*256* @return a {@code String} containing the base name.257*258* @see #setResourceBaseName259*/260protected String getResourceBaseName() {261return resourceBaseName;262}263264/**265* Utility method for locating an element.266*267* @param mustAppear if {@code true}, throw an268* {@code IllegalArgumentException} if no such node exists;269* if {@code false}, just return null.270*/271private Element getElement(String elementName, boolean mustAppear) {272if (mustAppear && (elementName == null)) {273throw new IllegalArgumentException("element name is null!");274}275Element element = elementMap.get(elementName);276if (mustAppear && (element == null)) {277throw new IllegalArgumentException("No such element: " +278elementName);279}280return element;281}282283private Element getElement(String elementName) {284return getElement(elementName, true);285}286287// Utility method for locating an attribute288private Attribute getAttribute(String elementName, String attrName) {289Element element = getElement(elementName);290Attribute attr = element.attrMap.get(attrName);291if (attr == null) {292throw new IllegalArgumentException("No such attribute \"" +293attrName + "\"!");294}295return attr;296}297298// Setup299300/**301* Adds a new element type to this metadata document format with a302* child policy other than {@code CHILD_POLICY_REPEAT}.303*304* @param elementName the name of the new element.305* @param parentName the name of the element that will be the306* parent of the new element.307* @param childPolicy one of the {@code CHILD_POLICY_*}308* constants, other than {@code CHILD_POLICY_REPEAT},309* indicating the child policy of the new element.310*311* @exception IllegalArgumentException if {@code parentName}312* is {@code null}, or is not a legal element name for this313* format.314* @exception IllegalArgumentException if {@code childPolicy}315* is not one of the predefined constants.316*/317protected void addElement(String elementName,318String parentName,319int childPolicy) {320Element parent = getElement(parentName);321if (childPolicy < CHILD_POLICY_EMPTY ||322childPolicy > CHILD_POLICY_MAX ||323childPolicy == CHILD_POLICY_REPEAT) {324throw new IllegalArgumentException325("Invalid value for childPolicy!");326}327328Element element = new Element();329element.elementName = elementName;330element.childPolicy = childPolicy;331332parent.childList.add(elementName);333element.parentList.add(parentName);334335elementMap.put(elementName, element);336}337338/**339* Adds a new element type to this metadata document format with a340* child policy of {@code CHILD_POLICY_REPEAT}.341*342* @param elementName the name of the new element.343* @param parentName the name of the element that will be the344* 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}349* is {@code null}, or is not a legal element name for this350* format.351* @exception IllegalArgumentException if {@code minChildren}352* is negative or larger than {@code maxChildren}.353*/354protected void addElement(String elementName,355String parentName,356int minChildren,357int maxChildren) {358Element parent = getElement(parentName);359if (minChildren < 0) {360throw new IllegalArgumentException("minChildren < 0!");361}362if (minChildren > maxChildren) {363throw new IllegalArgumentException("minChildren > maxChildren!");364}365366Element element = new Element();367element.elementName = elementName;368element.childPolicy = CHILD_POLICY_REPEAT;369element.minChildren = minChildren;370element.maxChildren = maxChildren;371372parent.childList.add(elementName);373element.parentList.add(parentName);374375elementMap.put(elementName, element);376}377378/**379* Adds an existing element to the list of legal children for a380* given parent node type.381*382* @param parentName the name of the element that will be the383* new parent of the element.384* @param elementName the name of the element to be added as a385* child.386*387* @exception IllegalArgumentException if {@code elementName}388* is {@code null}, or is not a legal element name for this389* format.390* @exception IllegalArgumentException if {@code parentName}391* is {@code null}, or is not a legal element name for this392* format.393*/394protected void addChildElement(String elementName, String parentName) {395Element parent = getElement(parentName);396Element element = getElement(elementName);397parent.childList.add(elementName);398element.parentList.add(parentName);399}400401/**402* Removes an element from the format. If no element with the403* given name was present, nothing happens and no exception is404* thrown.405*406* @param elementName the name of the element to be removed.407*/408protected void removeElement(String elementName) {409Element element = getElement(elementName, false);410if (element != null) {411for (String parentName : element.parentList) {412Element parent = getElement(parentName, false);413if (parent != null) {414parent.childList.remove(elementName);415}416}417elementMap.remove(elementName);418}419}420421/**422* Adds a new attribute to a previously defined element that may423* be set to an arbitrary value.424*425* @param elementName the name of the element.426* @param attrName the name of the attribute being added.427* @param dataType the data type (string format) of the attribute,428* one of the {@code DATATYPE_*} constants.429* @param required {@code true} if the attribute must be present.430* @param defaultValue the default value for the attribute, or431* {@code null}.432*433* @exception IllegalArgumentException if {@code elementName}434* is {@code null}, or is not a legal element name for this435* format.436* @exception IllegalArgumentException if {@code attrName} is437* {@code null}.438* @exception IllegalArgumentException if {@code dataType} is439* not one of the predefined constants.440*/441protected void addAttribute(String elementName,442String attrName,443int dataType,444boolean required,445String defaultValue) {446Element element = getElement(elementName);447if (attrName == null) {448throw new IllegalArgumentException("attrName == null!");449}450if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {451throw new IllegalArgumentException("Invalid value for dataType!");452}453454Attribute attr = new Attribute();455attr.attrName = attrName;456attr.valueType = VALUE_ARBITRARY;457attr.dataType = dataType;458attr.required = required;459attr.defaultValue = defaultValue;460461element.attrList.add(attrName);462element.attrMap.put(attrName, attr);463}464465/**466* Adds a new attribute to a previously defined element that will467* be defined by a set of enumerated values.468*469* @param elementName the name of the element.470* @param attrName the name of the attribute being added.471* @param dataType the data type (string format) of the attribute,472* one of the {@code DATATYPE_*} constants.473* @param required {@code true} if the attribute must be present.474* @param defaultValue the default value for the attribute, or475* {@code null}.476* @param enumeratedValues a {@code List} of477* {@code String}s containing the legal values for the478* attribute.479*480* @exception IllegalArgumentException if {@code elementName}481* is {@code null}, or is not a legal element name for this482* format.483* @exception IllegalArgumentException if {@code attrName} is484* {@code null}.485* @exception IllegalArgumentException if {@code dataType} is486* not one of the predefined constants.487* @exception IllegalArgumentException if488* {@code enumeratedValues} is {@code null}.489* @exception IllegalArgumentException if490* {@code enumeratedValues} does not contain at least one491* entry.492* @exception IllegalArgumentException if493* {@code enumeratedValues} contains an element that is not a494* {@code String} or is {@code null}.495*/496protected void addAttribute(String elementName,497String attrName,498int dataType,499boolean required,500String defaultValue,501List<String> enumeratedValues) {502Element element = getElement(elementName);503if (attrName == null) {504throw new IllegalArgumentException("attrName == null!");505}506if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {507throw new IllegalArgumentException("Invalid value for dataType!");508}509if (enumeratedValues == null) {510throw new IllegalArgumentException("enumeratedValues == null!");511}512if (enumeratedValues.size() == 0) {513throw new IllegalArgumentException("enumeratedValues is empty!");514}515for (Object o : enumeratedValues) {516if (o == null) {517throw new IllegalArgumentException518("enumeratedValues contains a null!");519}520if (!(o instanceof String)) {521throw new IllegalArgumentException522("enumeratedValues contains a non-String value!");523}524}525526Attribute attr = new Attribute();527attr.attrName = attrName;528attr.valueType = VALUE_ENUMERATION;529attr.dataType = dataType;530attr.required = required;531attr.defaultValue = defaultValue;532attr.enumeratedValues = enumeratedValues;533534element.attrList.add(attrName);535element.attrMap.put(attrName, attr);536}537538/**539* Adds a new attribute to a previously defined element that will540* be defined by a range of values.541*542* @param elementName the name of the element.543* @param attrName the name of the attribute being added.544* @param dataType the data type (string format) of the attribute,545* one of the {@code DATATYPE_*} constants.546* @param required {@code true} if the attribute must be present.547* @param defaultValue the default value for the attribute, or548* {@code null}.549* @param minValue the smallest (inclusive or exclusive depending550* on the value of {@code minInclusive}) legal value for the551* attribute, as a {@code String}.552* @param maxValue the largest (inclusive or exclusive depending553* on the value of {@code minInclusive}) legal value for the554* attribute, as a {@code String}.555* @param minInclusive {@code true} if {@code minValue}556* is inclusive.557* @param maxInclusive {@code true} if {@code maxValue}558* is inclusive.559*560* @exception IllegalArgumentException if {@code elementName}561* is {@code null}, or is not a legal element name for this562* format.563* @exception IllegalArgumentException if {@code attrName} is564* {@code null}.565* @exception IllegalArgumentException if {@code dataType} is566* not one of the predefined constants.567*/568protected void addAttribute(String elementName,569String attrName,570int dataType,571boolean required,572String defaultValue,573String minValue,574String maxValue,575boolean minInclusive,576boolean maxInclusive) {577Element element = getElement(elementName);578if (attrName == null) {579throw new IllegalArgumentException("attrName == null!");580}581if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {582throw new IllegalArgumentException("Invalid value for dataType!");583}584585Attribute attr = new Attribute();586attr.attrName = attrName;587attr.valueType = VALUE_RANGE;588if (minInclusive) {589attr.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;590}591if (maxInclusive) {592attr.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;593}594attr.dataType = dataType;595attr.required = required;596attr.defaultValue = defaultValue;597attr.minValue = minValue;598attr.maxValue = maxValue;599600element.attrList.add(attrName);601element.attrMap.put(attrName, attr);602}603604/**605* Adds a new attribute to a previously defined element that will606* be defined by a list of values.607*608* @param elementName the name of the element.609* @param attrName the name of the attribute being added.610* @param dataType the data type (string format) of the attribute,611* one of the {@code DATATYPE_*} constants.612* @param required {@code true} if the attribute must be present.613* @param listMinLength the smallest legal number of list items.614* @param listMaxLength the largest legal number of list items.615*616* @exception IllegalArgumentException if {@code elementName}617* is {@code null}, or is not a legal element name for this618* format.619* @exception IllegalArgumentException if {@code attrName} is620* {@code null}.621* @exception IllegalArgumentException if {@code dataType} is622* not one of the predefined constants.623* @exception IllegalArgumentException if624* {@code listMinLength} is negative or larger than625* {@code listMaxLength}.626*/627protected void addAttribute(String elementName,628String attrName,629int dataType,630boolean required,631int listMinLength,632int listMaxLength) {633Element element = getElement(elementName);634if (attrName == null) {635throw new IllegalArgumentException("attrName == null!");636}637if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {638throw new IllegalArgumentException("Invalid value for dataType!");639}640if (listMinLength < 0 || listMinLength > listMaxLength) {641throw new IllegalArgumentException("Invalid list bounds!");642}643644Attribute attr = new Attribute();645attr.attrName = attrName;646attr.valueType = VALUE_LIST;647attr.dataType = dataType;648attr.required = required;649attr.listMinLength = listMinLength;650attr.listMaxLength = listMaxLength;651652element.attrList.add(attrName);653element.attrMap.put(attrName, attr);654}655656/**657* Adds a new attribute to a previously defined element that will658* be defined by the enumerated values {@code TRUE} and659* {@code FALSE}, with a datatype of660* {@code DATATYPE_BOOLEAN}.661*662* @param elementName the name of the element.663* @param attrName the name of the attribute being added.664* @param hasDefaultValue {@code true} if a default value665* should be present.666* @param defaultValue the default value for the attribute as a667* {@code boolean}, ignored if {@code hasDefaultValue}668* is {@code false}.669*670* @exception IllegalArgumentException if {@code elementName}671* is {@code null}, or is not a legal element name for this672* format.673* @exception IllegalArgumentException if {@code attrName} is674* {@code null}.675*/676protected void addBooleanAttribute(String elementName,677String attrName,678boolean hasDefaultValue,679boolean defaultValue) {680List<String> values = new ArrayList<>();681values.add("TRUE");682values.add("FALSE");683684String dval = null;685if (hasDefaultValue) {686dval = defaultValue ? "TRUE" : "FALSE";687}688addAttribute(elementName,689attrName,690DATATYPE_BOOLEAN,691true,692dval,693values);694}695696/**697* Removes an attribute from a previously defined element. If no698* attribute with the given name was present in the given element,699* nothing happens and no exception is thrown.700*701* @param elementName the name of the element.702* @param attrName the name of the attribute being removed.703*704* @exception IllegalArgumentException if {@code elementName}705* is {@code null}, or is not a legal element name for this format.706*/707protected void removeAttribute(String elementName, String attrName) {708Element element = getElement(elementName);709element.attrList.remove(attrName);710element.attrMap.remove(attrName);711}712713/**714* Allows an {@code Object} reference of a given class type715* to be stored in nodes implementing the named element. The716* value of the {@code Object} is unconstrained other than by717* its class type.718*719* <p> If an {@code Object} reference was previously allowed,720* the previous settings are overwritten.721*722* @param elementName the name of the element.723* @param classType a {@code Class} variable indicating the724* legal class type for the object value.725* @param required {@code true} if an object value must be present.726* @param defaultValue the default value for the727* {@code Object} reference, or {@code null}.728* @param <T> the type of the object.729*730* @exception IllegalArgumentException if {@code elementName}731* is {@code null}, or is not a legal element name for this format.732*/733protected <T> void addObjectValue(String elementName,734Class<T> classType,735boolean required,736T defaultValue)737{738Element element = getElement(elementName);739ObjectValue<T> obj = new ObjectValue<>();740obj.valueType = VALUE_ARBITRARY;741obj.classType = classType;742obj.defaultValue = defaultValue;743744element.objectValue = obj;745}746747/**748* Allows an {@code Object} reference of a given class type749* to be stored in nodes implementing the named element. The750* value of the {@code Object} must be one of the values751* given by {@code enumeratedValues}.752*753* <p> If an {@code Object} reference was previously allowed,754* the previous settings are overwritten.755*756* @param elementName the name of the element.757* @param classType a {@code Class} variable indicating the758* legal class type for the object value.759* @param required {@code true} if an object value must be present.760* @param defaultValue the default value for the761* {@code Object} reference, or {@code null}.762* @param enumeratedValues a {@code List} of763* {@code Object}s containing the legal values for the764* object reference.765* @param <T> the type of the object.766*767* @exception IllegalArgumentException if {@code elementName}768* is {@code null}, or is not a legal element name for this format.769* @exception IllegalArgumentException if770* {@code enumeratedValues} is {@code null}.771* @exception IllegalArgumentException if772* {@code enumeratedValues} does not contain at least one773* entry.774* @exception IllegalArgumentException if775* {@code enumeratedValues} contains an element that is not776* an instance of the class type denoted by {@code classType}777* or is {@code null}.778*/779protected <T> void addObjectValue(String elementName,780Class<T> classType,781boolean required,782T defaultValue,783List<? extends T> enumeratedValues)784{785Element element = getElement(elementName);786if (enumeratedValues == null) {787throw new IllegalArgumentException("enumeratedValues == null!");788}789if (enumeratedValues.size() == 0) {790throw new IllegalArgumentException("enumeratedValues is empty!");791}792for (Object o : enumeratedValues) {793if (o == null) {794throw new IllegalArgumentException("enumeratedValues contains a null!");795}796if (!classType.isInstance(o)) {797throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");798}799}800801ObjectValue<T> obj = new ObjectValue<>();802obj.valueType = VALUE_ENUMERATION;803obj.classType = classType;804obj.defaultValue = defaultValue;805obj.enumeratedValues = enumeratedValues;806807element.objectValue = obj;808}809810/**811* Allows an {@code Object} reference of a given class type812* to be stored in nodes implementing the named element. The813* value of the {@code Object} must be within the range given814* by {@code minValue} and {@code maxValue}.815* Furthermore, the class type must implement the816* {@code Comparable} interface.817*818* <p> If an {@code Object} reference was previously allowed,819* the previous settings are overwritten.820*821* @param elementName the name of the element.822* @param classType a {@code Class} variable indicating the823* legal class type for the object value.824* @param defaultValue the default value for the825* @param minValue the smallest (inclusive or exclusive depending826* on the value of {@code minInclusive}) legal value for the827* object value, as a {@code String}.828* @param maxValue the largest (inclusive or exclusive depending829* on the value of {@code minInclusive}) legal value for the830* object value, as a {@code String}.831* @param minInclusive {@code true} if {@code minValue}832* is inclusive.833* @param maxInclusive {@code true} if {@code maxValue}834* is inclusive.835* @param <T> the type of the object.836*837* @exception IllegalArgumentException if {@code elementName}838* is {@code null}, or is not a legal element name for this839* format.840*/841protected <T extends Object & Comparable<? super T>> void842addObjectValue(String elementName,843Class<T> classType,844T defaultValue,845Comparable<? super T> minValue,846Comparable<? super T> maxValue,847boolean minInclusive,848boolean maxInclusive)849{850Element element = getElement(elementName);851ObjectValue<T> obj = new ObjectValue<>();852obj.valueType = VALUE_RANGE;853if (minInclusive) {854obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;855}856if (maxInclusive) {857obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;858}859obj.classType = classType;860obj.defaultValue = defaultValue;861obj.minValue = minValue;862obj.maxValue = maxValue;863864element.objectValue = obj;865}866867/**868* Allows an {@code Object} reference of a given class type869* to be stored in nodes implementing the named element. The870* value of the {@code Object} must an array of objects of871* class type given by {@code classType}, with at least872* {@code arrayMinLength} and at most873* {@code arrayMaxLength} elements.874*875* <p> If an {@code Object} reference was previously allowed,876* the previous settings are overwritten.877*878* @param elementName the name of the element.879* @param classType a {@code Class} variable indicating the880* legal class type for the object value.881* @param arrayMinLength the smallest legal length for the array.882* @param arrayMaxLength the largest legal length for the array.883*884* @exception IllegalArgumentException if {@code elementName} is885* not a legal element name for this format.886*/887protected void addObjectValue(String elementName,888Class<?> classType,889int arrayMinLength,890int arrayMaxLength) {891Element element = getElement(elementName);892ObjectValue<Object> obj = new ObjectValue<>();893obj.valueType = VALUE_LIST;894obj.classType = classType;895obj.arrayMinLength = arrayMinLength;896obj.arrayMaxLength = arrayMaxLength;897898element.objectValue = obj;899}900901/**902* Disallows an {@code Object} reference from being stored in903* nodes implementing the named element.904*905* @param elementName the name of the element.906*907* @exception IllegalArgumentException if {@code elementName} is908* not a legal element name for this format.909*/910protected void removeObjectValue(String elementName) {911Element element = getElement(elementName);912element.objectValue = null;913}914915// Utility method916917// Methods from IIOMetadataFormat918919// Root920921public String getRootName() {922return rootName;923}924925// Multiplicity926927public abstract boolean canNodeAppear(String elementName,928ImageTypeSpecifier imageType);929930public int getElementMinChildren(String elementName) {931Element element = getElement(elementName);932if (element.childPolicy != CHILD_POLICY_REPEAT) {933throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");934}935return element.minChildren;936}937938public int getElementMaxChildren(String elementName) {939Element element = getElement(elementName);940if (element.childPolicy != CHILD_POLICY_REPEAT) {941throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");942}943return element.maxChildren;944}945946private String getResource(String key, Locale locale) {947if (locale == null) {948locale = Locale.getDefault();949}950951/**952* Per the class documentation, resource bundles, including localized ones953* are intended to be delivered by the subclasser - ie supplier of the954* metadataformat. For the standard format and all standard plugins that955* is the JDK. For 3rd party plugins that they will supply their own.956* This includes plugins bundled with applets/applications.957* In all cases this means it is sufficient to search for those resource958* in the module that is providing the MetadataFormatImpl subclass.959*/960try {961ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale,962this.getClass().getModule());963return bundle.getString(key);964} catch (MissingResourceException e) {965return null;966}967}968969/**970* Returns a {@code String} containing a description of the971* named element, or {@code null}. The description will be972* localized for the supplied {@code Locale} if possible.973*974* <p> The default implementation will first locate a975* {@code ResourceBundle} using the current resource base976* name set by {@code setResourceBaseName} and the supplied977* {@code Locale}, using the fallback mechanism described in978* the comments for {@code ResourceBundle.getBundle}. If a979* {@code ResourceBundle} is found, the element name will be980* used as a key to its {@code getString} method, and the981* result returned. If no {@code ResourceBundle} is found,982* or no such key is present, {@code null} will be returned.983*984* <p> If {@code locale} is {@code null}, the current985* default {@code Locale} returned by {@code Locale.getLocale}986* will be used.987*988* @param elementName the name of the element.989* @param locale the {@code Locale} for which localization990* will be attempted.991*992* @return the element description.993*994* @exception IllegalArgumentException if {@code elementName}995* is {@code null}, or is not a legal element name for this format.996*997* @see #setResourceBaseName998*/999public String getElementDescription(String elementName,1000Locale locale) {1001Element element = getElement(elementName);1002return getResource(elementName, locale);1003}10041005// Children10061007public int getChildPolicy(String elementName) {1008Element element = getElement(elementName);1009return element.childPolicy;1010}10111012public String[] getChildNames(String elementName) {1013Element element = getElement(elementName);1014if (element.childPolicy == CHILD_POLICY_EMPTY) {1015return null;1016}1017return element.childList.toArray(new String[0]);1018}10191020// Attributes10211022public String[] getAttributeNames(String elementName) {1023Element element = getElement(elementName);1024List<String> names = element.attrList;10251026String[] result = new String[names.size()];1027return names.toArray(result);1028}10291030public int getAttributeValueType(String elementName, String attrName) {1031Attribute attr = getAttribute(elementName, attrName);1032return attr.valueType;1033}10341035public int getAttributeDataType(String elementName, String attrName) {1036Attribute attr = getAttribute(elementName, attrName);1037return attr.dataType;1038}10391040public boolean isAttributeRequired(String elementName, String attrName) {1041Attribute attr = getAttribute(elementName, attrName);1042return attr.required;1043}10441045public String getAttributeDefaultValue(String elementName,1046String attrName) {1047Attribute attr = getAttribute(elementName, attrName);1048return attr.defaultValue;1049}10501051public String[] getAttributeEnumerations(String elementName,1052String attrName) {1053Attribute attr = getAttribute(elementName, attrName);1054if (attr.valueType != VALUE_ENUMERATION) {1055throw new IllegalArgumentException1056("Attribute not an enumeration!");1057}10581059List<String> values = attr.enumeratedValues;1060String[] result = new String[values.size()];1061return values.toArray(result);1062}10631064public String getAttributeMinValue(String elementName, String attrName) {1065Attribute attr = getAttribute(elementName, attrName);1066if (attr.valueType != VALUE_RANGE &&1067attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&1068attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&1069attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {1070throw new IllegalArgumentException("Attribute not a range!");1071}10721073return attr.minValue;1074}10751076public String getAttributeMaxValue(String elementName, String attrName) {1077Attribute attr = getAttribute(elementName, attrName);1078if (attr.valueType != VALUE_RANGE &&1079attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&1080attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&1081attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {1082throw new IllegalArgumentException("Attribute not a range!");1083}10841085return attr.maxValue;1086}10871088public int getAttributeListMinLength(String elementName, String attrName) {1089Attribute attr = getAttribute(elementName, attrName);1090if (attr.valueType != VALUE_LIST) {1091throw new IllegalArgumentException("Attribute not a list!");1092}10931094return attr.listMinLength;1095}10961097public int getAttributeListMaxLength(String elementName, String attrName) {1098Attribute attr = getAttribute(elementName, attrName);1099if (attr.valueType != VALUE_LIST) {1100throw new IllegalArgumentException("Attribute not a list!");1101}11021103return attr.listMaxLength;1104}11051106/**1107* Returns a {@code String} containing a description of the1108* named attribute, or {@code null}. The description will be1109* localized for the supplied {@code Locale} if possible.1110*1111* <p> The default implementation will first locate a1112* {@code ResourceBundle} using the current resource base1113* name set by {@code setResourceBaseName} and the supplied1114* {@code Locale}, using the fallback mechanism described in1115* the comments for {@code ResourceBundle.getBundle}. If a1116* {@code ResourceBundle} is found, the element name followed1117* by a "/" character followed by the attribute name1118* ({@code elementName + "/" + attrName}) will be used as a1119* key to its {@code getString} method, and the result1120* returned. If no {@code ResourceBundle} is found, or no1121* such key is present, {@code null} will be returned.1122*1123* <p> If {@code locale} is {@code null}, the current1124* default {@code Locale} returned by {@code Locale.getLocale}1125* will be used.1126*1127* @param elementName the name of the element.1128* @param attrName the name of the attribute.1129* @param locale the {@code Locale} for which localization1130* will be attempted, or {@code null}.1131*1132* @return the attribute description.1133*1134* @exception IllegalArgumentException if {@code elementName}1135* is {@code null}, or is not a legal element name for this format.1136* @exception IllegalArgumentException if {@code attrName} is1137* {@code null} or is not a legal attribute name for this1138* element.1139*1140* @see #setResourceBaseName1141*/1142public String getAttributeDescription(String elementName,1143String attrName,1144Locale locale) {1145Element element = getElement(elementName);1146if (attrName == null) {1147throw new IllegalArgumentException("attrName == null!");1148}1149Attribute attr = element.attrMap.get(attrName);1150if (attr == null) {1151throw new IllegalArgumentException("No such attribute!");1152}11531154String key = elementName + "/" + attrName;1155return getResource(key, locale);1156}11571158private ObjectValue<?> getObjectValue(String elementName) {1159Element element = getElement(elementName);1160ObjectValue<?> objv = element.objectValue;1161if (objv == null) {1162throw new IllegalArgumentException("No object within element " +1163elementName + "!");1164}1165return objv;1166}11671168public int getObjectValueType(String elementName) {1169Element element = getElement(elementName);1170ObjectValue<?> objv = element.objectValue;1171if (objv == null) {1172return VALUE_NONE;1173}1174return objv.valueType;1175}11761177public Class<?> getObjectClass(String elementName) {1178ObjectValue<?> objv = getObjectValue(elementName);1179return objv.classType;1180}11811182public Object getObjectDefaultValue(String elementName) {1183ObjectValue<?> objv = getObjectValue(elementName);1184return objv.defaultValue;1185}11861187public Object[] getObjectEnumerations(String elementName) {1188ObjectValue<?> objv = getObjectValue(elementName);1189if (objv.valueType != VALUE_ENUMERATION) {1190throw new IllegalArgumentException("Not an enumeration!");1191}1192List<?> vlist = objv.enumeratedValues;1193Object[] values = new Object[vlist.size()];1194return vlist.toArray(values);1195}11961197public Comparable<?> getObjectMinValue(String elementName) {1198ObjectValue<?> objv = getObjectValue(elementName);1199if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {1200throw new IllegalArgumentException("Not a range!");1201}1202return objv.minValue;1203}12041205public Comparable<?> getObjectMaxValue(String elementName) {1206ObjectValue<?> objv = getObjectValue(elementName);1207if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {1208throw new IllegalArgumentException("Not a range!");1209}1210return objv.maxValue;1211}12121213public int getObjectArrayMinLength(String elementName) {1214ObjectValue<?> objv = getObjectValue(elementName);1215if (objv.valueType != VALUE_LIST) {1216throw new IllegalArgumentException("Not a list!");1217}1218return objv.arrayMinLength;1219}12201221public int getObjectArrayMaxLength(String elementName) {1222ObjectValue<?> objv = getObjectValue(elementName);1223if (objv.valueType != VALUE_LIST) {1224throw new IllegalArgumentException("Not a list!");1225}1226return objv.arrayMaxLength;1227}12281229// Standard format descriptor12301231private static synchronized void createStandardFormat() {1232if (standardFormat == null) {1233standardFormat = new StandardMetadataFormat();1234}1235}12361237/**1238* Returns an {@code IIOMetadataFormat} object describing the1239* standard, plug-in neutral {@code javax.imageio_1.0}1240* metadata document format described in the comment of the1241* {@code javax.imageio.metadata} package.1242*1243* @return a predefined {@code IIOMetadataFormat} instance.1244*/1245public static IIOMetadataFormat getStandardFormatInstance() {1246createStandardFormat();1247return standardFormat;1248}1249}125012511252