/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.imageio.metadata; import javax.imageio.ImageTypeSpecifier; import java.util.*; import java.security.AccessController; import java.security.PrivilegedAction; /** * The IIOMetadataFormatImpl class provides an implementation of the * IIOMetadataFormat interface. */ public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat { /** The Constant standardMetadataFormatName. */ @SuppressWarnings({"ConstantDeclaredInAbstractClass"}) public static final String standardMetadataFormatName = "javax_imageio_1.0"; /** The standard format. */ @SuppressWarnings({"StaticNonFinalField"}) private static IIOMetadataFormatImpl standardFormat; /** The root name. */ private String rootName; /** The element hash. */ private HashMap elementHash = new HashMap(); /** The resource base name. */ private String resourceBaseName = getClass().getName() + "Resources"; /** * Instantiates an IIOMetadataFormatImpl with the specified root * name and child policy (not CHILD_POLICY_REPEAT). * * @param rootName the name of root element. * @param childPolicy the child policy defined by one of the * CHILD_POLICY_* constants (except CHILD_POLICY_REPEAT). */ public IIOMetadataFormatImpl(String rootName, int childPolicy) { if (rootName == null) { throw new IllegalArgumentException("rootName is null"); } if ( childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX || childPolicy == CHILD_POLICY_REPEAT ) { throw new IllegalArgumentException("childPolicy is not one of the predefined constants"); } this.rootName = rootName; Element root = new Element(); root.name = rootName; root.childPolicy = childPolicy; elementHash.put(rootName, root); } /** * Instantiates an IIOMetadataFormatImpl with the specified root * name and CHILD_POLICY_REPEAT child policy. * * @param rootName the name of root element. * @param minChildren the minimum number of children. * @param maxChildren the maximum number of children */ public IIOMetadataFormatImpl(String rootName, int minChildren, int maxChildren) { if (rootName == null) { throw new IllegalArgumentException("rootName is null"); } if (minChildren < 0) { throw new IllegalArgumentException("minChildren < 0!"); } if (minChildren > maxChildren) { throw new IllegalArgumentException("minChildren > maxChildren!"); } this.rootName = rootName; Element root = new Element(); root.name = rootName; root.minChildren = minChildren; root.maxChildren = maxChildren; root.childPolicy = CHILD_POLICY_REPEAT; elementHash.put(rootName, root); } @SuppressWarnings({"AbstractMethodOverridesAbstractMethod"}) public abstract boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType); /** * Adds a new attribute to an existing element. * * @param elementName the name of the element to which the new attribute * will be added. * @param attrName the attribute name. * @param dataType the data type of the new attribute. * @param required the flag which indicates whether this attribute * must be present. * @param listMinLength the minimum legal number of list items. * @param listMaxLength the the maximum legal number of list items. */ protected void addAttribute( String elementName, String attrName, int dataType, boolean required, int listMinLength, int listMaxLength ) { if (attrName == null) { throw new IllegalArgumentException("attrName == null!"); } if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { throw new IllegalArgumentException("Invalid value for dataType!"); } if (listMinLength < 0 || listMinLength > listMaxLength) { throw new IllegalArgumentException("Invalid list bounds!"); } Element element = findElement(elementName); Attlist attr = new Attlist(); attr.name = attrName; attr.dataType = dataType; attr.required = required; attr.listMinLength = listMinLength; attr.listMaxLength = listMaxLength; attr.valueType = VALUE_LIST; element.attributes.put(attrName, attr); } /** * Adds a new attribute to an existing element. * * @param elementName the name of the element to which the new attribute * will be added. * @param attrName the attribute name. * @param dataType the data type of the new attribute. * @param required the flag which indicates whether this attribute * must be present. * @param defaultValue the default value of the attribute. */ protected void addAttribute( String elementName, String attrName, int dataType, boolean required, String defaultValue ) { if (attrName == null) { throw new IllegalArgumentException("attrName == null!"); } if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { throw new IllegalArgumentException("Invalid value for dataType!"); } Element element = findElement(elementName); Attlist attr = new Attlist(); attr.name = attrName; attr.dataType = dataType; attr.required = required; attr.defaultValue = defaultValue; attr.valueType = VALUE_ARBITRARY; element.attributes.put(attrName, attr); } /** * Adds a new attribute to an existing element. * * @param elementName the name of the element to which the new attribute * will be added. * @param attrName the attribute name. * @param dataType the data type of the new attribute. * @param required the flag which indicates whether this attribute * must be present. * @param defaultValue the default value of the attribute. * @param enumeratedValues the legal values for the attribute as * a list of strings. */ protected void addAttribute( String elementName, String attrName, int dataType, boolean required, String defaultValue, List enumeratedValues ) { if (attrName == null) { throw new IllegalArgumentException("attrName == null!"); } if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { throw new IllegalArgumentException("Invalid value for dataType!"); } if (enumeratedValues == null || enumeratedValues.isEmpty()) { throw new IllegalArgumentException("enumeratedValues is empty or null"); } try { for (String enumeratedValue : enumeratedValues) { if (enumeratedValue == null) { throw new IllegalArgumentException("enumeratedValues contains a null!"); } } } catch (ClassCastException e) { throw new IllegalArgumentException("enumeratedValues contains a non-String value!"); } Element element = findElement(elementName); Attlist attr = new Attlist(); attr.name = attrName; attr.dataType = dataType; attr.required = required; attr.defaultValue = defaultValue; attr.enumeratedValues = enumeratedValues; attr.valueType = VALUE_ENUMERATION; element.attributes.put(attrName, attr); } /** * Adds a new attribute to an existing element. * * @param elementName the name of the element to which the new attribute * will be added. * @param attrName the attribute name. * @param dataType the data type of the new attribute. * @param required the flag which indicates whether this attribute * must be present. * @param defaultValue the default value of attribute. * @param minValue the minimum legal value of an attribute. * @param maxValue the maximum legal value of an attribute. * @param minInclusive the flag which indicates * whether the minValue is inclusive. * @param maxInclusive the flag which indicates * whether the maxValue is inclusive. */ protected void addAttribute( String elementName, String attrName, int dataType, boolean required, String defaultValue, String minValue, String maxValue, boolean minInclusive, boolean maxInclusive ) { if (attrName == null) { throw new IllegalArgumentException("attrName == null!"); } if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { throw new IllegalArgumentException("Invalid value for dataType!"); } Element element = findElement(elementName); Attlist attr = new Attlist(); attr.name = attrName; attr.dataType = dataType; attr.required = required; attr.defaultValue = defaultValue; attr.minValue = minValue; attr.maxValue = maxValue; attr.minInclusive = minInclusive; attr.maxInclusive = maxInclusive; attr.valueType = VALUE_RANGE; attr.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0; attr.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0; element.attributes.put(attrName, attr); } /** * Adds a new attribute with boolean data type to an existing * element. * * @param elementName the name of the element to which the new attribute * will be added. * @param attrName the attribute name. * @param hasDefaultValue the flag which indicates whether this attribute * must have a default value. * @param defaultValue the default value. */ protected void addBooleanAttribute( String elementName, String attrName, boolean hasDefaultValue, boolean defaultValue ) { String defaultVal = hasDefaultValue ? (defaultValue ? "TRUE" : "FALSE") : null; ArrayList values = new ArrayList(2); values.add("TRUE"); values.add("FALSE"); addAttribute(elementName, attrName, DATATYPE_BOOLEAN, true, defaultVal, values); } /** * Adds an existing element to the list of child elements * of the specified parent element. * * @param elementName the name of the element to be added. * @param parentName the parent element name. */ protected void addChildElement(String elementName, String parentName) { Element parent = findElement(parentName); Element element = findElement(elementName); parent.children.add(element.name); } /** * Adds a new element type to this IIOMetadataFormat with * a child policy (if policy is not CHILD_POLICY_REPEAT). * * @param elementName the name of the element to be added. * @param parentName the parent element name. * @param childPolicy one of the CHILD_POLICY_* constants defined * by IIOMetadataFormat. */ protected void addElement(String elementName, String parentName, int childPolicy) { if ( childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX || childPolicy == CHILD_POLICY_REPEAT ) { throw new IllegalArgumentException("childPolicy is not one of the predefined constants"); } Element parent = findElement(parentName); Element element = new Element(); element.name = elementName; element.childPolicy = childPolicy; elementHash.put(elementName, element); parent.children.add(elementName); } /** * Adds a new element type to this IIOMetadataFormat with * CHILD_POLICY_REPEAT and the specified minimum and maximum * number of child elements. * * @param elementName the element name to be added. * @param parentName the parent element name. * @param minChildren the minimum number of child elements. * @param maxChildren the maximum number of child elements. */ protected void addElement( String elementName, String parentName, int minChildren, int maxChildren ) { if (minChildren < 0) { throw new IllegalArgumentException("minChildren < 0!"); } if (minChildren > maxChildren) { throw new IllegalArgumentException("minChildren > maxChildren!"); } Element parent = findElement(parentName); Element element = new Element(); element.name = elementName; element.childPolicy = CHILD_POLICY_REPEAT; element.minChildren = minChildren; element.maxChildren = maxChildren; elementHash.put(elementName, element); parent.children.add(elementName); } /** * Adds an Object reference with the specified class type to be * stored as element's value. * * @param elementName the element name. * @param classType the class indicates the legal types for * the object's value. * @param arrayMinLength the minimum legal length for the array. * @param arrayMaxLength the maximum legal length for the array. */ protected void addObjectValue( String elementName, Class classType, int arrayMinLength, int arrayMaxLength ) { Element element = findElement(elementName); ObjectValue objVal = new ObjectValue(); objVal.classType = classType; objVal.arrayMaxLength = arrayMaxLength; objVal.arrayMinLength = arrayMinLength; objVal.valueType = VALUE_LIST; element.objectValue = objVal; } /** * Adds an Object reference with the specified class type to be * stored as an element's value. * * @param elementName the element name. * @param classType the class indicates the legal types for * the object's value. * @param required a flag indicated that this object value * must be present. * @param defaultValue the default value, or null. */ protected void addObjectValue( String elementName, Class classType, boolean required, T defaultValue ) { // note: reqired is an unused parameter Element element = findElement(elementName); ObjectValue objVal = new ObjectValue(); objVal.classType = classType; objVal.defaultValue = defaultValue; objVal.valueType = VALUE_ARBITRARY; element.objectValue = objVal; } /** * Adds an Object reference with the specified class type to be * stored as the element's value. * * @param elementName the element name. * @param classType the class indicates the legal types for * the object value. * @param required a flag indicated that this object value * must be present. * @param defaultValue the default value, or null. * @param enumeratedValues the list of legal values for the object. */ protected void addObjectValue( String elementName, Class classType, boolean required, T defaultValue, List enumeratedValues ) { // note: reqired is an unused parameter if (enumeratedValues == null || enumeratedValues.isEmpty()) { throw new IllegalArgumentException("enumeratedValues is empty or null"); } try { for (T enumeratedValue : enumeratedValues) { if (enumeratedValue == null) { throw new IllegalArgumentException("enumeratedValues contains a null!"); } } } catch (ClassCastException e) { throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!"); } Element element = findElement(elementName); ObjectValue objVal = new ObjectValue(); objVal.classType = classType; objVal.defaultValue = defaultValue; objVal.enumeratedValues = enumeratedValues; objVal.valueType = VALUE_ENUMERATION; element.objectValue = objVal; } /** * Adds an Object reference with the specified class type to be * stored as the element's value. * * @param elementName the element name. * @param classType the class indicates the legal types for * the object value. * @param defaultValue the default value, or null. * @param minValue the minimum legal value for the object value. * @param maxValue the maximum legal value for the object value. * @param minInclusive the flag which indicates * whether the minValue is inclusive. * @param maxInclusive the flag which indicates * whether the maxValue is inclusive. */ protected > void addObjectValue( String elementName, Class classType, T defaultValue, Comparable minValue, Comparable maxValue, boolean minInclusive, boolean maxInclusive ) { Element element = findElement(elementName); ObjectValue objVal = new ObjectValue(); objVal.classType = classType; objVal.defaultValue = defaultValue; objVal.minValue = minValue; objVal.maxValue = maxValue; objVal.minInclusive = minInclusive; objVal.maxInclusive = maxInclusive; objVal.valueType = VALUE_RANGE; objVal.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0; objVal.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0; element.objectValue = objVal; } public int getAttributeDataType(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); return attr.dataType; } public String getAttributeDefaultValue(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); return attr.defaultValue; } public String getAttributeDescription(String elementName, String attrName, Locale locale) { findAttribute(elementName, attrName); return getResourceString(elementName + "/" + attrName, locale); } public String[] getAttributeEnumerations(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); if (attr.valueType != VALUE_ENUMERATION) { throw new IllegalArgumentException("Attribute is not an enumeration!"); } return attr.enumeratedValues.toArray(new String[attr.enumeratedValues.size()]); } public int getAttributeListMaxLength(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); if (attr.valueType != VALUE_LIST) { throw new IllegalArgumentException("Attribute is not a list!"); } return attr.listMaxLength; } public int getAttributeListMinLength(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); if (attr.valueType != VALUE_LIST) { throw new IllegalArgumentException("Attribute is not a list!"); } return attr.listMinLength; } public String getAttributeMaxValue(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); if ((attr.valueType & VALUE_RANGE) == 0) { throw new IllegalArgumentException("Attribute is not a range!"); } return attr.maxValue; } public String getAttributeMinValue(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); if ((attr.valueType & VALUE_RANGE) == 0) { throw new IllegalArgumentException("Attribute is not a range!"); } return attr.minValue; } public String[] getAttributeNames(String elementName) { Element element = findElement(elementName); return element.attributes.keySet().toArray(new String[element.attributes.size()]); } public int getAttributeValueType(String elementName, String attrName) { Attlist attr = findAttribute(elementName, attrName); return attr.valueType; } public String[] getChildNames(String elementName) { Element element = findElement(elementName); if (element.childPolicy == CHILD_POLICY_EMPTY) { // Element cannot have children return null; } return element.children.toArray(new String[element.children.size()]); } public int getChildPolicy(String elementName) { Element element = findElement(elementName); return element.childPolicy; } public String getElementDescription(String elementName, Locale locale) { findElement(elementName); // Check if there is such element return getResourceString(elementName, locale); } public int getElementMaxChildren(String elementName) { Element element = findElement(elementName); if (element.childPolicy != CHILD_POLICY_REPEAT) { throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!"); } return element.maxChildren; } public int getElementMinChildren(String elementName) { Element element = findElement(elementName); if (element.childPolicy != CHILD_POLICY_REPEAT) { throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!"); } return element.minChildren; } public int getObjectArrayMaxLength(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null || v.valueType != VALUE_LIST) { throw new IllegalArgumentException("Not a list!"); } return v.arrayMaxLength; } public int getObjectArrayMinLength(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null || v.valueType != VALUE_LIST) { throw new IllegalArgumentException("Not a list!"); } return v.arrayMinLength; } public Class getObjectClass(String elementName) { ObjectValue v = findObjectValue(elementName); return v.classType; } public Object getObjectDefaultValue(String elementName) { ObjectValue v = findObjectValue(elementName); return v.defaultValue; } public Object[] getObjectEnumerations(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null || v.valueType != VALUE_ENUMERATION) { throw new IllegalArgumentException("Not an enumeration!"); } return v.enumeratedValues.toArray(); } public Comparable getObjectMaxValue(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null || (v.valueType & VALUE_RANGE) == 0) { throw new IllegalArgumentException("Not a range!"); } return v.maxValue; } public Comparable getObjectMinValue(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null || (v.valueType & VALUE_RANGE) == 0) { throw new IllegalArgumentException("Not a range!"); } return v.minValue; } public int getObjectValueType(String elementName) { Element element = findElement(elementName); if (element.objectValue == null) { return VALUE_NONE; } return element.objectValue.valueType; } /** * Gets the resource base name for locating ResourceBundles. * * @return the current resource base name. */ protected String getResourceBaseName() { return resourceBaseName; } public String getRootName() { return rootName; } /** * Gets the standard format instance. * * @return the IIOMetadataFormat instance. */ public static IIOMetadataFormat getStandardFormatInstance() { if (standardFormat == null) { standardFormat = new IIOStandardMetadataFormat(); } return standardFormat; } public boolean isAttributeRequired(String elementName, String attrName) { return findAttribute(elementName, attrName).required; } /** * Removes the specified attribute from the specified element. * * @param elementName the specified element name. * @param attrName the specified attribute name. */ protected void removeAttribute(String elementName, String attrName) { Element element = findElement(elementName); element.attributes.remove(attrName); } /** * Removes the specified element from this format. * * @param elementName the specified element name. */ protected void removeElement(String elementName) { Element element; if ((element = elementHash.get(elementName)) != null) { elementHash.remove(elementName); for (Element e : elementHash.values()) { e.children.remove(element.name); } } } /** * Removes the object value from the specified element. * * @param elementName the element name. */ protected void removeObjectValue(String elementName) { Element element = findElement(elementName); element.objectValue = null; } /** * Sets a new base name for ResourceBundles containing * descriptions of elements and attributes for this format. * * @param resourceBaseName the new resource base name. */ protected void setResourceBaseName(String resourceBaseName) { if (resourceBaseName == null) { throw new IllegalArgumentException("resourceBaseName == null!"); } this.resourceBaseName = resourceBaseName; } /** * The Class Element. */ @SuppressWarnings({"ClassWithoutConstructor"}) private class Element { /** The name. */ String name; /** The children. */ ArrayList children = new ArrayList(); /** The attributes. */ HashMap attributes = new HashMap(); /** The min children. */ int minChildren; /** The max children. */ int maxChildren; /** The child policy. */ int childPolicy; /** The object value. */ ObjectValue objectValue; } /** * The Class Attlist. */ @SuppressWarnings({"ClassWithoutConstructor"}) private class Attlist { /** The name. */ String name; /** The data type. */ int dataType; /** The required. */ boolean required; /** The list min length. */ int listMinLength; /** The list max length. */ int listMaxLength; /** The default value. */ String defaultValue; /** The enumerated values. */ List enumeratedValues; /** The min value. */ String minValue; /** The max value. */ String maxValue; /** The min inclusive. */ boolean minInclusive; /** The max inclusive. */ boolean maxInclusive; /** The value type. */ int valueType; } /** * The Class ObjectValue. */ @SuppressWarnings({"ClassWithoutConstructor"}) private class ObjectValue { /** The class type. */ Class classType; /** The array min length. */ int arrayMinLength; /** The array max length. */ int arrayMaxLength; /** The default value. */ T defaultValue; /** The enumerated values. */ List enumeratedValues; /** The min value. */ Comparable minValue; /** The max value. */ Comparable maxValue; /** The min inclusive. */ boolean minInclusive; /** The max inclusive. */ boolean maxInclusive; /** The value type. */ int valueType; } /** * Find element. * * @param name the name * * @return the element */ private Element findElement(String name) { Element element; if ((element = elementHash.get(name)) == null) { throw new IllegalArgumentException("element name is null or no such element: " + name); } return element; } /** * Find attribute. * * @param elementName the element name * @param attributeName the attribute name * * @return the attlist */ private Attlist findAttribute(String elementName, String attributeName) { Element element = findElement(elementName); Attlist attribute; if ((attribute = element.attributes.get(attributeName)) == null) { throw new IllegalArgumentException("attribute name is null or no such attribute: " + attributeName); } return attribute; } /** * Find object value. * * @param elementName the element name * * @return the object value */ private ObjectValue findObjectValue(String elementName) { Element element = findElement(elementName); ObjectValue v = element.objectValue; if (v == null) { throw new IllegalArgumentException("No object within element"); } return v; } /** * Gets the resource string. * * @param key the key * @param locale the locale * * @return the resource string */ private String getResourceString(String key, Locale locale) { if (locale == null) { locale = Locale.getDefault(); } // Get the context class loader and try to locate the bundle with it first ClassLoader contextClassloader = AccessController.doPrivileged( new PrivilegedAction() { public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); // Now try to get the resource bundle ResourceBundle rb; try { rb = ResourceBundle.getBundle(resourceBaseName, locale, contextClassloader); } catch (MissingResourceException e) { try { rb = ResourceBundle.getBundle(resourceBaseName, locale); } catch (MissingResourceException e1) { return null; } } try { return rb.getString(key); } catch (MissingResourceException e) { return null; } catch (ClassCastException e) { return null; // Not a string resource } } }