0001 /*
0002 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.imageio.metadata;
0027
0028 import java.util.ArrayList;
0029 import java.util.Collection;
0030 import java.util.HashMap;
0031 import java.util.Iterator;
0032 import java.util.List;
0033 import java.util.Locale;
0034 import java.util.Map;
0035 import java.util.MissingResourceException;
0036 import java.util.ResourceBundle;
0037 import javax.imageio.ImageTypeSpecifier;
0038 import com.sun.imageio.plugins.common.StandardMetadataFormat;
0039
0040 /**
0041 * A concrete class providing a reusable implementation of the
0042 * <code>IIOMetadataFormat</code> interface. In addition, a static
0043 * instance representing the standard, plug-in neutral
0044 * <code>javax_imageio_1.0</code> format is provided by the
0045 * <code>getStandardFormatInstance</code> method.
0046 *
0047 * <p> In order to supply localized descriptions of elements and
0048 * attributes, a <code>ResourceBundle</code> with a base name of
0049 * <code>this.getClass().getName() + "Resources"</code> should be
0050 * supplied via the usual mechanism used by
0051 * <code>ResourceBundle.getBundle</code>. Briefly, the subclasser
0052 * supplies one or more additional classes according to a naming
0053 * convention (by default, the fully-qualified name of the subclass
0054 * extending <code>IIMetadataFormatImpl</code>, plus the string
0055 * "Resources", plus the country, language, and variant codes
0056 * separated by underscores). At run time, calls to
0057 * <code>getElementDescription</code> or
0058 * <code>getAttributeDescription</code> will attempt to load such
0059 * classes dynamically according to the supplied locale, and will use
0060 * either the element name, or the element name followed by a '/'
0061 * character followed by the attribute name as a key. This key will
0062 * be supplied to the <code>ResourceBundle</code>'s
0063 * <code>getString</code> method, and the resulting localized
0064 * description of the node or attribute is returned.
0065 *
0066 * <p> The subclass may supply a different base name for the resource
0067 * bundles using the <code>setResourceBaseName</code> method.
0068 *
0069 * <p> A subclass may choose its own localization mechanism, if so
0070 * desired, by overriding the supplied implementations of
0071 * <code>getElementDescription</code> and
0072 * <code>getAttributeDescription</code>.
0073 *
0074 * @see ResourceBundle#getBundle(String,Locale)
0075 *
0076 * @version 0.5
0077 */
0078 public abstract class IIOMetadataFormatImpl implements
0079 IIOMetadataFormat {
0080
0081 /**
0082 * A <code>String</code> constant containing the standard format
0083 * name, <code>"javax_imageio_1.0"</code>.
0084 */
0085 public static final String standardMetadataFormatName = "javax_imageio_1.0";
0086
0087 private static IIOMetadataFormat standardFormat = null;
0088
0089 private String resourceBaseName = this .getClass().getName()
0090 + "Resources";
0091
0092 private String rootName;
0093
0094 // Element name (String) -> Element
0095 private HashMap elementMap = new HashMap();
0096
0097 class Element {
0098 String elementName;
0099
0100 int childPolicy;
0101 int minChildren = 0;
0102 int maxChildren = 0;
0103
0104 // Child names (Strings)
0105 List childList = new ArrayList();
0106
0107 // Parent names (Strings)
0108 List parentList = new ArrayList();
0109
0110 // List of attribute names in the order they were added
0111 List attrList = new ArrayList();
0112 // Attr name (String) -> Attribute
0113 Map attrMap = new HashMap();
0114
0115 ObjectValue objectValue;
0116 }
0117
0118 class Attribute {
0119 String attrName;
0120
0121 int valueType = VALUE_ARBITRARY;
0122 int dataType;
0123 boolean required;
0124 String defaultValue = null;
0125
0126 // enumeration
0127 List enumeratedValues;
0128
0129 // range
0130 String minValue;
0131 String maxValue;
0132
0133 // list
0134 int listMinLength;
0135 int listMaxLength;
0136 }
0137
0138 class ObjectValue {
0139 int valueType = VALUE_NONE;
0140 Class classType = null;
0141 Object defaultValue = null;
0142
0143 // Meaningful only if valueType == VALUE_ENUMERATION
0144 List enumeratedValues = null;
0145
0146 // Meaningful only if valueType == VALUE_RANGE
0147 Comparable minValue = null;
0148 Comparable maxValue = null;
0149
0150 // Meaningful only if valueType == VALUE_LIST
0151 int arrayMinLength = 0;
0152 int arrayMaxLength = 0;
0153 }
0154
0155 /**
0156 * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
0157 * with a given root element name and child policy (other than
0158 * <code>CHILD_POLICY_REPEAT</code>). Additional elements, and
0159 * their attributes and <code>Object</code> reference information
0160 * may be added using the various <code>add</code> methods.
0161 *
0162 * @param rootName the name of the root element.
0163 * @param childPolicy one of the <code>CHILD_POLICY_*</code> constants,
0164 * other than <code>CHILD_POLICY_REPEAT</code>.
0165 *
0166 * @exception IllegalArgumentException if <code>rootName</code> is
0167 * <code>null</code>.
0168 * @exception IllegalArgumentException if <code>childPolicy</code> is
0169 * not one of the predefined constants.
0170 */
0171 public IIOMetadataFormatImpl(String rootName, int childPolicy) {
0172 if (rootName == null) {
0173 throw new IllegalArgumentException("rootName == null!");
0174 }
0175 if (childPolicy < CHILD_POLICY_EMPTY
0176 || childPolicy > CHILD_POLICY_MAX
0177 || childPolicy == CHILD_POLICY_REPEAT) {
0178 throw new IllegalArgumentException(
0179 "Invalid value for childPolicy!");
0180 }
0181
0182 this .rootName = rootName;
0183
0184 Element root = new Element();
0185 root.elementName = rootName;
0186 root.childPolicy = childPolicy;
0187
0188 elementMap.put(rootName, root);
0189 }
0190
0191 /**
0192 * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
0193 * with a given root element name and a child policy of
0194 * <code>CHILD_POLICY_REPEAT</code>. Additional elements, and
0195 * their attributes and <code>Object</code> reference information
0196 * may be added using the various <code>add</code> methods.
0197 *
0198 * @param rootName the name of the root element.
0199 * @param minChildren the minimum number of children of the node.
0200 * @param maxChildren the maximum number of children of the node.
0201 *
0202 * @exception IllegalArgumentException if <code>rootName</code> is
0203 * <code>null</code>.
0204 * @exception IllegalArgumentException if <code>minChildren</code>
0205 * is negative or larger than <code>maxChildren</code>.
0206 */
0207 public IIOMetadataFormatImpl(String rootName, int minChildren,
0208 int maxChildren) {
0209 if (rootName == null) {
0210 throw new IllegalArgumentException("rootName == null!");
0211 }
0212 if (minChildren < 0) {
0213 throw new IllegalArgumentException("minChildren < 0!");
0214 }
0215 if (minChildren > maxChildren) {
0216 throw new IllegalArgumentException(
0217 "minChildren > maxChildren!");
0218 }
0219
0220 Element root = new Element();
0221 root.elementName = rootName;
0222 root.childPolicy = CHILD_POLICY_REPEAT;
0223 root.minChildren = minChildren;
0224 root.maxChildren = maxChildren;
0225
0226 this .rootName = rootName;
0227 elementMap.put(rootName, root);
0228 }
0229
0230 /**
0231 * Sets a new base name for locating <code>ResourceBundle</code>s
0232 * containing descriptions of elements and attributes for this
0233 * format.
0234 *
0235 * <p> Prior to the first time this method is called, the base
0236 * name will be equal to <code>this.getClass().getName() +
0237 * "Resources"</code>.
0238 *
0239 * @param resourceBaseName a <code>String</code> containg the new
0240 * base name.
0241 *
0242 * @exception IllegalArgumentException if
0243 * <code>resourceBaseName</code> is <code>null</code>.
0244 *
0245 * @see #getResourceBaseName
0246 */
0247 protected void setResourceBaseName(String resourceBaseName) {
0248 if (resourceBaseName == null) {
0249 throw new IllegalArgumentException(
0250 "resourceBaseName == null!");
0251 }
0252 this .resourceBaseName = resourceBaseName;
0253 }
0254
0255 /**
0256 * Returns the currently set base name for locating
0257 * <code>ResourceBundle</code>s.
0258 *
0259 * @return a <code>String</code> containing the base name.
0260 *
0261 * @see #setResourceBaseName
0262 */
0263 protected String getResourceBaseName() {
0264 return resourceBaseName;
0265 }
0266
0267 /**
0268 * Utility method for locating an element.
0269 *
0270 * @param mustAppear if <code>true</code>, throw an
0271 * <code>IllegalArgumentException</code> if no such node exists;
0272 * if <code>false</code>, just return null.
0273 */
0274 private Element getElement(String elementName, boolean mustAppear) {
0275 if (mustAppear && (elementName == null)) {
0276 throw new IllegalArgumentException("element name is null!");
0277 }
0278 Element element = (Element) elementMap.get(elementName);
0279 if (mustAppear && (element == null)) {
0280 throw new IllegalArgumentException("No such element: "
0281 + elementName);
0282 }
0283 return element;
0284 }
0285
0286 private Element getElement(String elementName) {
0287 return getElement(elementName, true);
0288 }
0289
0290 // Utility method for locating an attribute
0291 private Attribute getAttribute(String elementName, String attrName) {
0292 Element element = getElement(elementName);
0293 Attribute attr = (Attribute) element.attrMap.get(attrName);
0294 if (attr == null) {
0295 throw new IllegalArgumentException("No such attribute \""
0296 + attrName + "\"!");
0297 }
0298 return attr;
0299 }
0300
0301 // Setup
0302
0303 /**
0304 * Adds a new element type to this metadata document format with a
0305 * child policy other than <code>CHILD_POLICY_REPEAT</code>.
0306 *
0307 * @param elementName the name of the new element.
0308 * @param parentName the name of the element that will be the
0309 * parent of the new element.
0310 * @param childPolicy one of the <code>CHILD_POLICY_*</code>
0311 * constants, other than <code>CHILD_POLICY_REPEAT</code>,
0312 * indicating the child policy of the new element.
0313 *
0314 * @exception IllegalArgumentException if <code>parentName</code>
0315 * is <code>null</code>, or is not a legal element name for this
0316 * format.
0317 * @exception IllegalArgumentException if <code>childPolicy</code>
0318 * is not one of the predefined constants.
0319 */
0320 protected void addElement(String elementName, String parentName,
0321 int childPolicy) {
0322 Element parent = getElement(parentName);
0323 if (childPolicy < CHILD_POLICY_EMPTY
0324 || childPolicy > CHILD_POLICY_MAX
0325 || childPolicy == CHILD_POLICY_REPEAT) {
0326 throw new IllegalArgumentException(
0327 "Invalid value for childPolicy!");
0328 }
0329
0330 Element element = new Element();
0331 element.elementName = elementName;
0332 element.childPolicy = childPolicy;
0333
0334 parent.childList.add(elementName);
0335 element.parentList.add(parentName);
0336
0337 elementMap.put(elementName, element);
0338 }
0339
0340 /**
0341 * Adds a new element type to this metadata document format with a
0342 * child policy of <code>CHILD_POLICY_REPEAT</code>.
0343 *
0344 * @param elementName the name of the new element.
0345 * @param parentName the name of the element that will be the
0346 * parent of the new element.
0347 * @param minChildren the minimum number of children of the node.
0348 * @param maxChildren the maximum number of children of the node.
0349 *
0350 * @exception IllegalArgumentException if <code>parentName</code>
0351 * is <code>null</code>, or is not a legal element name for this
0352 * format.
0353 * @exception IllegalArgumentException if <code>minChildren</code>
0354 * is negative or larger than <code>maxChildren</code>.
0355 */
0356 protected void addElement(String elementName, String parentName,
0357 int minChildren, int maxChildren) {
0358 Element parent = getElement(parentName);
0359 if (minChildren < 0) {
0360 throw new IllegalArgumentException("minChildren < 0!");
0361 }
0362 if (minChildren > maxChildren) {
0363 throw new IllegalArgumentException(
0364 "minChildren > maxChildren!");
0365 }
0366
0367 Element element = new Element();
0368 element.elementName = elementName;
0369 element.childPolicy = CHILD_POLICY_REPEAT;
0370 element.minChildren = minChildren;
0371 element.maxChildren = maxChildren;
0372
0373 parent.childList.add(elementName);
0374 element.parentList.add(parentName);
0375
0376 elementMap.put(elementName, element);
0377 }
0378
0379 /**
0380 * Adds an existing element to the list of legal children for a
0381 * given parent node type.
0382 *
0383 * @param parentName the name of the element that will be the
0384 * new parent of the element.
0385 * @param elementName the name of the element to be addded as a
0386 * child.
0387 *
0388 * @exception IllegalArgumentException if <code>elementName</code>
0389 * is <code>null</code>, or is not a legal element name for this
0390 * format.
0391 * @exception IllegalArgumentException if <code>parentName</code>
0392 * is <code>null</code>, or is not a legal element name for this
0393 * format.
0394 */
0395 protected void addChildElement(String elementName, String parentName) {
0396 Element parent = getElement(parentName);
0397 Element element = getElement(elementName);
0398 parent.childList.add(elementName);
0399 element.parentList.add(parentName);
0400 }
0401
0402 /**
0403 * Removes an element from the format. If no element with the
0404 * given name was present, nothing happens and no exception is
0405 * thrown.
0406 *
0407 * @param elementName the name of the element to be removed.
0408 */
0409 protected void removeElement(String elementName) {
0410 Element element = getElement(elementName, false);
0411 if (element != null) {
0412 Iterator iter = element.parentList.iterator();
0413 while (iter.hasNext()) {
0414 String parentName = (String) iter.next();
0415 Element parent = getElement(parentName, false);
0416 if (parent != null) {
0417 parent.childList.remove(elementName);
0418 }
0419 }
0420 elementMap.remove(elementName);
0421 }
0422 }
0423
0424 /**
0425 * Adds a new attribute to a previously defined element that may
0426 * be set to an arbitrary value.
0427 *
0428 * @param elementName the name of the element.
0429 * @param attrName the name of the attribute being added.
0430 * @param dataType the data type (string format) of the attribute,
0431 * one of the <code>DATATYPE_*</code> constants.
0432 * @param required <code>true</code> if the attribute must be present.
0433 * @param defaultValue the default value for the attribute, or
0434 * <code>null</code>.
0435 *
0436 * @exception IllegalArgumentException if <code>elementName</code>
0437 * is <code>null</code>, or is not a legal element name for this
0438 * format.
0439 * @exception IllegalArgumentException if <code>attrName</code> is
0440 * <code>null</code>.
0441 * @exception IllegalArgumentException if <code>dataType</code> is
0442 * not one of the predefined constants.
0443 */
0444 protected void addAttribute(String elementName, String attrName,
0445 int dataType, boolean required, String defaultValue) {
0446 Element element = getElement(elementName);
0447 if (attrName == null) {
0448 throw new IllegalArgumentException("attrName == null!");
0449 }
0450 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
0451 throw new IllegalArgumentException(
0452 "Invalid value for dataType!");
0453 }
0454
0455 Attribute attr = new Attribute();
0456 attr.attrName = attrName;
0457 attr.valueType = VALUE_ARBITRARY;
0458 attr.dataType = dataType;
0459 attr.required = required;
0460 attr.defaultValue = defaultValue;
0461
0462 element.attrList.add(attrName);
0463 element.attrMap.put(attrName, attr);
0464 }
0465
0466 /**
0467 * Adds a new attribute to a previously defined element that will
0468 * be defined by a set of enumerated values.
0469 *
0470 * @param elementName the name of the element.
0471 * @param attrName the name of the attribute being added.
0472 * @param dataType the data type (string format) of the attribute,
0473 * one of the <code>DATATYPE_*</code> constants.
0474 * @param required <code>true</code> if the attribute must be present.
0475 * @param defaultValue the default value for the attribute, or
0476 * <code>null</code>.
0477 * @param enumeratedValues a <code>List</code> of
0478 * <code>String</code>s containing the legal values for the
0479 * attribute.
0480 *
0481 * @exception IllegalArgumentException if <code>elementName</code>
0482 * is <code>null</code>, or is not a legal element name for this
0483 * format.
0484 * @exception IllegalArgumentException if <code>attrName</code> is
0485 * <code>null</code>.
0486 * @exception IllegalArgumentException if <code>dataType</code> is
0487 * not one of the predefined constants.
0488 * @exception IllegalArgumentException if
0489 * <code>enumeratedValues</code> is <code>null</code>.
0490 * @exception IllegalArgumentException if
0491 * <code>enumeratedValues</code> does not contain at least one
0492 * entry.
0493 * @exception IllegalArgumentException if
0494 * <code>enumeratedValues</code> contains an element that is not a
0495 * <code>String</code> or is <code>null</code>.
0496 */
0497 protected void addAttribute(String elementName, String attrName,
0498 int dataType, boolean required, String defaultValue,
0499 List<String> enumeratedValues) {
0500 Element element = getElement(elementName);
0501 if (attrName == null) {
0502 throw new IllegalArgumentException("attrName == null!");
0503 }
0504 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
0505 throw new IllegalArgumentException(
0506 "Invalid value for dataType!");
0507 }
0508 if (enumeratedValues == null) {
0509 throw new IllegalArgumentException(
0510 "enumeratedValues == null!");
0511 }
0512 if (enumeratedValues.size() == 0) {
0513 throw new IllegalArgumentException(
0514 "enumeratedValues is empty!");
0515 }
0516 Iterator iter = enumeratedValues.iterator();
0517 while (iter.hasNext()) {
0518 Object o = iter.next();
0519 if (o == null) {
0520 throw new IllegalArgumentException(
0521 "enumeratedValues contains a null!");
0522 }
0523 if (!(o instanceof String)) {
0524 throw new IllegalArgumentException(
0525 "enumeratedValues contains a non-String value!");
0526 }
0527 }
0528
0529 Attribute attr = new Attribute();
0530 attr.attrName = attrName;
0531 attr.valueType = VALUE_ENUMERATION;
0532 attr.dataType = dataType;
0533 attr.required = required;
0534 attr.defaultValue = defaultValue;
0535 attr.enumeratedValues = enumeratedValues;
0536
0537 element.attrList.add(attrName);
0538 element.attrMap.put(attrName, attr);
0539 }
0540
0541 /**
0542 * Adds a new attribute to a previously defined element that will
0543 * be defined by a range of values.
0544 *
0545 * @param elementName the name of the element.
0546 * @param attrName the name of the attribute being added.
0547 * @param dataType the data type (string format) of the attribute,
0548 * one of the <code>DATATYPE_*</code> constants.
0549 * @param required <code>true</code> if the attribute must be present.
0550 * @param defaultValue the default value for the attribute, or
0551 * <code>null</code>.
0552 * @param minValue the smallest (inclusive or exclusive depending
0553 * on the value of <code>minInclusive</code>) legal value for the
0554 * attribute, as a <code>String</code>.
0555 * @param maxValue the largest (inclusive or exclusive depending
0556 * on the value of <code>minInclusive</code>) legal value for the
0557 * attribute, as a <code>String</code>.
0558 * @param minInclusive <code>true</code> if <code>minValue</code>
0559 * is inclusive.
0560 * @param maxInclusive <code>true</code> if <code>maxValue</code>
0561 * is inclusive.
0562 *
0563 * @exception IllegalArgumentException if <code>elementName</code>
0564 * is <code>null</code>, or is not a legal element name for this
0565 * format.
0566 * @exception IllegalArgumentException if <code>attrName</code> is
0567 * <code>null</code>.
0568 * @exception IllegalArgumentException if <code>dataType</code> is
0569 * not one of the predefined constants.
0570 */
0571 protected void addAttribute(String elementName, String attrName,
0572 int dataType, boolean required, String defaultValue,
0573 String minValue, String maxValue, boolean minInclusive,
0574 boolean maxInclusive) {
0575 Element element = getElement(elementName);
0576 if (attrName == null) {
0577 throw new IllegalArgumentException("attrName == null!");
0578 }
0579 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
0580 throw new IllegalArgumentException(
0581 "Invalid value for dataType!");
0582 }
0583
0584 Attribute attr = new Attribute();
0585 attr.attrName = attrName;
0586 attr.valueType = VALUE_RANGE;
0587 if (minInclusive) {
0588 attr.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
0589 }
0590 if (maxInclusive) {
0591 attr.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
0592 }
0593 attr.dataType = dataType;
0594 attr.required = required;
0595 attr.defaultValue = defaultValue;
0596 attr.minValue = minValue;
0597 attr.maxValue = maxValue;
0598
0599 element.attrList.add(attrName);
0600 element.attrMap.put(attrName, attr);
0601 }
0602
0603 /**
0604 * Adds a new attribute to a previously defined element that will
0605 * be defined by a list of values.
0606 *
0607 * @param elementName the name of the element.
0608 * @param attrName the name of the attribute being added.
0609 * @param dataType the data type (string format) of the attribute,
0610 * one of the <code>DATATYPE_*</code> constants.
0611 * @param required <code>true</code> if the attribute must be present.
0612 * @param listMinLength the smallest legal number of list items.
0613 * @param listMaxLength the largest legal number of list items.
0614 *
0615 * @exception IllegalArgumentException if <code>elementName</code>
0616 * is <code>null</code>, or is not a legal element name for this
0617 * format.
0618 * @exception IllegalArgumentException if <code>attrName</code> is
0619 * <code>null</code>.
0620 * @exception IllegalArgumentException if <code>dataType</code> is
0621 * not one of the predefined constants.
0622 * @exception IllegalArgumentException if
0623 * <code>listMinLength</code> is negative or larger than
0624 * <code>listMaxLength</code>.
0625 */
0626 protected void addAttribute(String elementName, String attrName,
0627 int dataType, boolean required, int listMinLength,
0628 int listMaxLength) {
0629 Element element = getElement(elementName);
0630 if (attrName == null) {
0631 throw new IllegalArgumentException("attrName == null!");
0632 }
0633 if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
0634 throw new IllegalArgumentException(
0635 "Invalid value for dataType!");
0636 }
0637 if (listMinLength < 0 || listMinLength > listMaxLength) {
0638 throw new IllegalArgumentException("Invalid list bounds!");
0639 }
0640
0641 Attribute attr = new Attribute();
0642 attr.attrName = attrName;
0643 attr.valueType = VALUE_LIST;
0644 attr.dataType = dataType;
0645 attr.required = required;
0646 attr.listMinLength = listMinLength;
0647 attr.listMaxLength = listMaxLength;
0648
0649 element.attrList.add(attrName);
0650 element.attrMap.put(attrName, attr);
0651 }
0652
0653 /**
0654 * Adds a new attribute to a previously defined element that will
0655 * be defined by the enumerated values <code>TRUE</code> and
0656 * <code>FALSE</code>, with a datatype of
0657 * <code>DATATYPE_BOOLEAN</code>.
0658 *
0659 * @param elementName the name of the element.
0660 * @param attrName the name of the attribute being added.
0661 * @param hasDefaultValue <code>true</code> if a default value
0662 * should be present.
0663 * @param defaultValue the default value for the attribute as a
0664 * <code>boolean</code>, ignored if <code>hasDefaultValue</code>
0665 * is <code>false</code>.
0666 *
0667 * @exception IllegalArgumentException if <code>elementName</code>
0668 * is <code>null</code>, or is not a legal element name for this
0669 * format.
0670 * @exception IllegalArgumentException if <code>attrName</code> is
0671 * <code>null</code>.
0672 */
0673 protected void addBooleanAttribute(String elementName,
0674 String attrName, boolean hasDefaultValue,
0675 boolean defaultValue) {
0676 List values = new ArrayList();
0677 values.add("TRUE");
0678 values.add("FALSE");
0679
0680 String dval = null;
0681 if (hasDefaultValue) {
0682 dval = defaultValue ? "TRUE" : "FALSE";
0683 }
0684 addAttribute(elementName, attrName, DATATYPE_BOOLEAN, true,
0685 dval, values);
0686 }
0687
0688 /**
0689 * Removes an attribute from a previously defined element. If no
0690 * attribute with the given name was present in the given element,
0691 * nothing happens and no exception is thrown.
0692 *
0693 * @param elementName the name of the element.
0694 * @param attrName the name of the attribute being removed.
0695 *
0696 * @exception IllegalArgumentException if <code>elementName</code>
0697 * is <code>null</code>, or is not a legal element name for this format.
0698 */
0699 protected void removeAttribute(String elementName, String attrName) {
0700 Element element = getElement(elementName);
0701 element.attrList.remove(attrName);
0702 element.attrMap.remove(attrName);
0703 }
0704
0705 /**
0706 * Allows an <code>Object</code> reference of a given class type
0707 * to be stored in nodes implementing the named element. The
0708 * value of the <code>Object</code> is unconstrained other than by
0709 * its class type.
0710 *
0711 * <p> If an <code>Object</code> reference was previously allowed,
0712 * the previous settings are overwritten.
0713 *
0714 * @param elementName the name of the element.
0715 * @param classType a <code>Class</code> variable indicating the
0716 * legal class type for the object value.
0717 * @param required <code>true</code> if an object value must be present.
0718 * @param defaultValue the default value for the
0719 * <code>Object</code> reference, or <code>null</code>.
0720 *
0721 * @exception IllegalArgumentException if <code>elementName</code>
0722 * is <code>null</code>, or is not a legal element name for this format.
0723 */
0724 protected <T> void addObjectValue(String elementName,
0725 Class<T> classType, boolean required, T defaultValue) {
0726 Element element = getElement(elementName);
0727 ObjectValue obj = new ObjectValue();
0728 obj.valueType = VALUE_ARBITRARY;
0729 obj.classType = classType;
0730 obj.defaultValue = defaultValue;
0731
0732 element.objectValue = obj;
0733 }
0734
0735 /**
0736 * Allows an <code>Object</code> reference of a given class type
0737 * to be stored in nodes implementing the named element. The
0738 * value of the <code>Object</code> must be one of the values
0739 * given by <code>enumeratedValues</code>.
0740 *
0741 * <p> If an <code>Object</code> reference was previously allowed,
0742 * the previous settings are overwritten.
0743 *
0744 * @param elementName the name of the element.
0745 * @param classType a <code>Class</code> variable indicating the
0746 * legal class type for the object value.
0747 * @param required <code>true</code> if an object value must be present.
0748 * @param defaultValue the default value for the
0749 * <code>Object</code> reference, or <code>null</code>.
0750 * @param enumeratedValues a <code>List</code> of
0751 * <code>Object</code>s containing the legal values for the
0752 * object reference.
0753 *
0754 * @exception IllegalArgumentException if <code>elementName</code>
0755 * is <code>null</code>, or is not a legal element name for this format.
0756 * @exception IllegalArgumentException if
0757 * <code>enumeratedValues</code> is <code>null</code>.
0758 * @exception IllegalArgumentException if
0759 * <code>enumeratedValues</code> does not contain at least one
0760 * entry.
0761 * @exception IllegalArgumentException if
0762 * <code>enumeratedValues</code> contains an element that is not
0763 * an instance of the class type denoted by <code>classType</code>
0764 * or is <code>null</code>.
0765 */
0766 protected <T> void addObjectValue(String elementName,
0767 Class<T> classType, boolean required, T defaultValue,
0768 List<? extends T> enumeratedValues) {
0769 Element element = getElement(elementName);
0770 if (enumeratedValues == null) {
0771 throw new IllegalArgumentException(
0772 "enumeratedValues == null!");
0773 }
0774 if (enumeratedValues.size() == 0) {
0775 throw new IllegalArgumentException(
0776 "enumeratedValues is empty!");
0777 }
0778 Iterator iter = enumeratedValues.iterator();
0779 while (iter.hasNext()) {
0780 Object o = iter.next();
0781 if (o == null) {
0782 throw new IllegalArgumentException(
0783 "enumeratedValues contains a null!");
0784 }
0785 if (!classType.isInstance(o)) {
0786 throw new IllegalArgumentException(
0787 "enumeratedValues contains a value not of class classType!");
0788 }
0789 }
0790
0791 ObjectValue obj = new ObjectValue();
0792 obj.valueType = VALUE_ENUMERATION;
0793 obj.classType = classType;
0794 obj.defaultValue = defaultValue;
0795 obj.enumeratedValues = enumeratedValues;
0796
0797 element.objectValue = obj;
0798 }
0799
0800 /**
0801 * Allows an <code>Object</code> reference of a given class type
0802 * to be stored in nodes implementing the named element. The
0803 * value of the <code>Object</code> must be within the range given
0804 * by <code>minValue</code> and <code>maxValue</code>.
0805 * Furthermore, the class type must implement the
0806 * <code>Comparable</code> interface.
0807 *
0808 * <p> If an <code>Object</code> reference was previously allowed,
0809 * the previous settings are overwritten.
0810 *
0811 * @param elementName the name of the element.
0812 * @param classType a <code>Class</code> variable indicating the
0813 * legal class type for the object value.
0814 * @param defaultValue the default value for the
0815 * @param minValue the smallest (inclusive or exclusive depending
0816 * on the value of <code>minInclusive</code>) legal value for the
0817 * object value, as a <code>String</code>.
0818 * @param maxValue the largest (inclusive or exclusive depending
0819 * on the value of <code>minInclusive</code>) legal value for the
0820 * object value, as a <code>String</code>.
0821 * @param minInclusive <code>true</code> if <code>minValue</code>
0822 * is inclusive.
0823 * @param maxInclusive <code>true</code> if <code>maxValue</code>
0824 * is inclusive.
0825 *
0826 * @exception IllegalArgumentException if <code>elementName</code>
0827 * is <code>null</code>, or is not a legal element name for this
0828 * format.
0829 */
0830 protected <T extends Object & Comparable<? super T>> void addObjectValue(
0831 String elementName, Class<T> classType, T defaultValue,
0832 Comparable<? super T> minValue,
0833 Comparable<? super T> maxValue, boolean minInclusive,
0834 boolean maxInclusive) {
0835 Element element = getElement(elementName);
0836 ObjectValue obj = new ObjectValue();
0837 obj.valueType = VALUE_RANGE;
0838 if (minInclusive) {
0839 obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
0840 }
0841 if (maxInclusive) {
0842 obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
0843 }
0844 obj.classType = classType;
0845 obj.defaultValue = defaultValue;
0846 obj.minValue = minValue;
0847 obj.maxValue = maxValue;
0848
0849 element.objectValue = obj;
0850 }
0851
0852 /**
0853 * Allows an <code>Object</code> reference of a given class type
0854 * to be stored in nodes implementing the named element. The
0855 * value of the <code>Object</code> must an array of objects of
0856 * class type given by <code>classType</code>, with at least
0857 * <code>arrayMinLength</code> and at most
0858 * <code>arrayMaxLength</code> elements.
0859 *
0860 * <p> If an <code>Object</code> reference was previously allowed,
0861 * the previous settings are overwritten.
0862 *
0863 * @param elementName the name of the element.
0864 * @param classType a <code>Class</code> variable indicating the
0865 * legal class type for the object value.
0866 * @param arrayMinLength the smallest legal length for the array.
0867 * @param arrayMaxLength the largest legal length for the array.
0868 *
0869 * @exception IllegalArgumentException if <code>elementName</code> is
0870 * not a legal element name for this format.
0871 */
0872 protected void addObjectValue(String elementName,
0873 Class<?> classType, int arrayMinLength, int arrayMaxLength) {
0874 Element element = getElement(elementName);
0875 ObjectValue obj = new ObjectValue();
0876 obj.valueType = VALUE_LIST;
0877 obj.classType = classType;
0878 obj.arrayMinLength = arrayMinLength;
0879 obj.arrayMaxLength = arrayMaxLength;
0880
0881 element.objectValue = obj;
0882 }
0883
0884 /**
0885 * Disallows an <code>Object</code> reference from being stored in
0886 * nodes implementing the named element.
0887 *
0888 * @param elementName the name of the element.
0889 *
0890 * @exception IllegalArgumentException if <code>elementName</code> is
0891 * not a legal element name for this format.
0892 */
0893 protected void removeObjectValue(String elementName) {
0894 Element element = getElement(elementName);
0895 element.objectValue = null;
0896 }
0897
0898 // Utility method
0899
0900 // Methods from IIOMetadataFormat
0901
0902 // Root
0903
0904 public String getRootName() {
0905 return rootName;
0906 }
0907
0908 // Multiplicity
0909
0910 public abstract boolean canNodeAppear(String elementName,
0911 ImageTypeSpecifier imageType);
0912
0913 public int getElementMinChildren(String elementName) {
0914 Element element = getElement(elementName);
0915 if (element.childPolicy != CHILD_POLICY_REPEAT) {
0916 throw new IllegalArgumentException(
0917 "Child policy not CHILD_POLICY_REPEAT!");
0918 }
0919 return element.minChildren;
0920 }
0921
0922 public int getElementMaxChildren(String elementName) {
0923 Element element = getElement(elementName);
0924 if (element.childPolicy != CHILD_POLICY_REPEAT) {
0925 throw new IllegalArgumentException(
0926 "Child policy not CHILD_POLICY_REPEAT!");
0927 }
0928 return element.maxChildren;
0929 }
0930
0931 private String getResource(String key, Locale locale) {
0932 if (locale == null) {
0933 locale = Locale.getDefault();
0934 }
0935
0936 /**
0937 * If an applet supplies an implementation of IIOMetadataFormat and
0938 * resource bundles, then the resource bundle will need to be
0939 * accessed via the applet class loader. So first try the context
0940 * class loader to locate the resource bundle.
0941 * If that throws MissingResourceException, then try the
0942 * system class loader.
0943 */
0944 ClassLoader loader = (ClassLoader) java.security.AccessController
0945 .doPrivileged(new java.security.PrivilegedAction() {
0946 public Object run() {
0947 return Thread.currentThread()
0948 .getContextClassLoader();
0949 }
0950 });
0951
0952 ResourceBundle bundle = null;
0953 try {
0954 bundle = ResourceBundle.getBundle(resourceBaseName, locale,
0955 loader);
0956 } catch (MissingResourceException mre) {
0957 try {
0958 bundle = ResourceBundle.getBundle(resourceBaseName,
0959 locale);
0960 } catch (MissingResourceException mre1) {
0961 return null;
0962 }
0963 }
0964
0965 try {
0966 return bundle.getString(key);
0967 } catch (MissingResourceException e) {
0968 return null;
0969 }
0970 }
0971
0972 /**
0973 * Returns a <code>String</code> containing a description of the
0974 * named element, or <code>null</code>. The desciption will be
0975 * localized for the supplied <code>Locale</code> if possible.
0976 *
0977 * <p> The default implementation will first locate a
0978 * <code>ResourceBundle</code> using the current resource base
0979 * name set by <code>setResourceBaseName</code> and the supplied
0980 * <code>Locale</code>, using the fallback mechanism described in
0981 * the comments for <code>ResourceBundle.getBundle</code>. If a
0982 * <code>ResourceBundle</code> is found, the element name will be
0983 * used as a key to its <code>getString</code> method, and the
0984 * result returned. If no <code>ResourceBundle</code> is found,
0985 * or no such key is present, <code>null</code> will be returned.
0986 *
0987 * <p> If <code>locale</code> is <code>null</code>, the current
0988 * default <code>Locale</code> returned by <code>Locale.getLocale</code>
0989 * will be used.
0990 *
0991 * @param elementName the name of the element.
0992 * @param locale the <code>Locale</code> for which localization
0993 * will be attempted.
0994 *
0995 * @return the element description.
0996 *
0997 * @exception IllegalArgumentException if <code>elementName</code>
0998 * is <code>null</code>, or is not a legal element name for this format.
0999 *
1000 * @see #setResourceBaseName
1001 */
1002 public String getElementDescription(String elementName,
1003 Locale locale) {
1004 Element element = getElement(elementName);
1005 return getResource(elementName, locale);
1006 }
1007
1008 // Children
1009
1010 public int getChildPolicy(String elementName) {
1011 Element element = getElement(elementName);
1012 return element.childPolicy;
1013 }
1014
1015 public String[] getChildNames(String elementName) {
1016 Element element = getElement(elementName);
1017 if (element.childPolicy == CHILD_POLICY_EMPTY) {
1018 return null;
1019 }
1020 return (String[]) element.childList.toArray(new String[0]);
1021 }
1022
1023 // Attributes
1024
1025 public String[] getAttributeNames(String elementName) {
1026 Element element = getElement(elementName);
1027 List names = element.attrList;
1028
1029 String[] result = new String[names.size()];
1030 return (String[]) names.toArray(result);
1031 }
1032
1033 public int getAttributeValueType(String elementName, String attrName) {
1034 Attribute attr = getAttribute(elementName, attrName);
1035 return attr.valueType;
1036 }
1037
1038 public int getAttributeDataType(String elementName, String attrName) {
1039 Attribute attr = getAttribute(elementName, attrName);
1040 return attr.dataType;
1041 }
1042
1043 public boolean isAttributeRequired(String elementName,
1044 String attrName) {
1045 Attribute attr = getAttribute(elementName, attrName);
1046 return attr.required;
1047 }
1048
1049 public String getAttributeDefaultValue(String elementName,
1050 String attrName) {
1051 Attribute attr = getAttribute(elementName, attrName);
1052 return attr.defaultValue;
1053 }
1054
1055 public String[] getAttributeEnumerations(String elementName,
1056 String attrName) {
1057 Attribute attr = getAttribute(elementName, attrName);
1058 if (attr.valueType != VALUE_ENUMERATION) {
1059 throw new IllegalArgumentException(
1060 "Attribute not an enumeration!");
1061 }
1062
1063 List values = attr.enumeratedValues;
1064 Iterator iter = values.iterator();
1065 String[] result = new String[values.size()];
1066 return (String[]) values.toArray(result);
1067 }
1068
1069 public String getAttributeMinValue(String elementName,
1070 String attrName) {
1071 Attribute attr = getAttribute(elementName, attrName);
1072 if (attr.valueType != VALUE_RANGE
1073 && attr.valueType != VALUE_RANGE_MIN_INCLUSIVE
1074 && attr.valueType != VALUE_RANGE_MAX_INCLUSIVE
1075 && attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1076 throw new IllegalArgumentException("Attribute not a range!");
1077 }
1078
1079 return attr.minValue;
1080 }
1081
1082 public String getAttributeMaxValue(String elementName,
1083 String attrName) {
1084 Attribute attr = getAttribute(elementName, attrName);
1085 if (attr.valueType != VALUE_RANGE
1086 && attr.valueType != VALUE_RANGE_MIN_INCLUSIVE
1087 && attr.valueType != VALUE_RANGE_MAX_INCLUSIVE
1088 && attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1089 throw new IllegalArgumentException("Attribute not a range!");
1090 }
1091
1092 return attr.maxValue;
1093 }
1094
1095 public int getAttributeListMinLength(String elementName,
1096 String attrName) {
1097 Attribute attr = getAttribute(elementName, attrName);
1098 if (attr.valueType != VALUE_LIST) {
1099 throw new IllegalArgumentException("Attribute not a list!");
1100 }
1101
1102 return attr.listMinLength;
1103 }
1104
1105 public int getAttributeListMaxLength(String elementName,
1106 String attrName) {
1107 Attribute attr = getAttribute(elementName, attrName);
1108 if (attr.valueType != VALUE_LIST) {
1109 throw new IllegalArgumentException("Attribute not a list!");
1110 }
1111
1112 return attr.listMaxLength;
1113 }
1114
1115 /**
1116 * Returns a <code>String</code> containing a description of the
1117 * named attribute, or <code>null</code>. The desciption will be
1118 * localized for the supplied <code>Locale</code> if possible.
1119 *
1120 * <p> The default implementation will first locate a
1121 * <code>ResourceBundle</code> using the current resource base
1122 * name set by <code>setResourceBaseName</code> and the supplied
1123 * <code>Locale</code>, using the fallback mechanism described in
1124 * the comments for <code>ResourceBundle.getBundle</code>. If a
1125 * <code>ResourceBundle</code> is found, the element name followed
1126 * by a "/" character followed by the attribute name
1127 * (<code>elementName + "/" + attrName</code>) will be used as a
1128 * key to its <code>getString</code> method, and the result
1129 * returned. If no <code>ResourceBundle</code> is found, or no
1130 * such key is present, <code>null</code> will be returned.
1131 *
1132 * <p> If <code>locale</code> is <code>null</code>, the current
1133 * default <code>Locale</code> returned by <code>Locale.getLocale</code>
1134 * will be used.
1135 *
1136 * @param elementName the name of the element.
1137 * @param attrName the name of the attribute.
1138 * @param locale the <code>Locale</code> for which localization
1139 * will be attempted, or <code>null</code>.
1140 *
1141 * @return the attribute description.
1142 *
1143 * @exception IllegalArgumentException if <code>elementName</code>
1144 * is <code>null</code>, or is not a legal element name for this format.
1145 * @exception IllegalArgumentException if <code>attrName</code> is
1146 * <code>null</code> or is not a legal attribute name for this
1147 * element.
1148 *
1149 * @see #setResourceBaseName
1150 */
1151 public String getAttributeDescription(String elementName,
1152 String attrName, Locale locale) {
1153 Element element = getElement(elementName);
1154 if (attrName == null) {
1155 throw new IllegalArgumentException("attrName == null!");
1156 }
1157 Attribute attr = (Attribute) element.attrMap.get(attrName);
1158 if (attr == null) {
1159 throw new IllegalArgumentException("No such attribute!");
1160 }
1161
1162 String key = elementName + "/" + attrName;
1163 return getResource(key, locale);
1164 }
1165
1166 private ObjectValue getObjectValue(String elementName) {
1167 Element element = getElement(elementName);
1168 ObjectValue objv = (ObjectValue) element.objectValue;
1169 if (objv == null) {
1170 throw new IllegalArgumentException(
1171 "No object within element " + elementName + "!");
1172 }
1173 return objv;
1174 }
1175
1176 public int getObjectValueType(String elementName) {
1177 Element element = getElement(elementName);
1178 ObjectValue objv = (ObjectValue) element.objectValue;
1179 if (objv == null) {
1180 return VALUE_NONE;
1181 }
1182 return objv.valueType;
1183 }
1184
1185 public Class<?> getObjectClass(String elementName) {
1186 ObjectValue objv = getObjectValue(elementName);
1187 return objv.classType;
1188 }
1189
1190 public Object getObjectDefaultValue(String elementName) {
1191 ObjectValue objv = getObjectValue(elementName);
1192 return objv.defaultValue;
1193 }
1194
1195 public Object[] getObjectEnumerations(String elementName) {
1196 ObjectValue objv = getObjectValue(elementName);
1197 if (objv.valueType != VALUE_ENUMERATION) {
1198 throw new IllegalArgumentException("Not an enumeration!");
1199 }
1200 List vlist = objv.enumeratedValues;
1201 Object[] values = new Object[vlist.size()];
1202 return vlist.toArray(values);
1203 }
1204
1205 public Comparable<?> getObjectMinValue(String elementName) {
1206 ObjectValue objv = getObjectValue(elementName);
1207 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1208 throw new IllegalArgumentException("Not a range!");
1209 }
1210 return objv.minValue;
1211 }
1212
1213 public Comparable<?> getObjectMaxValue(String elementName) {
1214 ObjectValue objv = getObjectValue(elementName);
1215 if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1216 throw new IllegalArgumentException("Not a range!");
1217 }
1218 return objv.maxValue;
1219 }
1220
1221 public int getObjectArrayMinLength(String elementName) {
1222 ObjectValue objv = getObjectValue(elementName);
1223 if (objv.valueType != VALUE_LIST) {
1224 throw new IllegalArgumentException("Not a list!");
1225 }
1226 return objv.arrayMinLength;
1227 }
1228
1229 public int getObjectArrayMaxLength(String elementName) {
1230 ObjectValue objv = getObjectValue(elementName);
1231 if (objv.valueType != VALUE_LIST) {
1232 throw new IllegalArgumentException("Not a list!");
1233 }
1234 return objv.arrayMaxLength;
1235 }
1236
1237 // Standard format descriptor
1238
1239 private synchronized static void createStandardFormat() {
1240 if (standardFormat == null) {
1241 standardFormat = new StandardMetadataFormat();
1242 }
1243 }
1244
1245 /**
1246 * Returns an <code>IIOMetadataFormat</code> object describing the
1247 * standard, plug-in neutral <code>javax.imageio_1.0</code>
1248 * metadata document format described in the comment of the
1249 * <code>javax.imageio.metadata</code> package.
1250 *
1251 * @return a predefined <code>IIOMetadataFormat</code> instance.
1252 */
1253 public static IIOMetadataFormat getStandardFormatInstance() {
1254 createStandardFormat();
1255 return standardFormat;
1256 }
1257 }
|