1. /*
  2. * @(#)IIOMetadataFormatImpl.java 1.25 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.imageio.metadata;
  8. import java.util.ArrayList;
  9. import java.util.Collection;
  10. import java.util.HashMap;
  11. import java.util.Iterator;
  12. import java.util.List;
  13. import java.util.Locale;
  14. import java.util.Map;
  15. import java.util.MissingResourceException;
  16. import java.util.ResourceBundle;
  17. import javax.imageio.ImageTypeSpecifier;
  18. import com.sun.imageio.plugins.common.StandardMetadataFormat;
  19. /**
  20. * A concrete class providing a reusable implementation of the
  21. * <code>IIOMetadataFormat</code> interface. In addition, a static
  22. * instance representing the standard, plug-in neutral
  23. * <code>javax_imageio_1.0</code> format is provided by the
  24. * <code>getStandardFormatInstance</code> method.
  25. *
  26. * <p> In order to supply localized descriptions of elements and
  27. * attributes, a <code>ResourceBundle</code> with a base name of
  28. * <code>this.getClass().getName() + "Resources"</code> should be
  29. * supplied via the usual mechanism used by
  30. * <code>ResourceBundle.getBundle</code>. Briefly, the subclasser
  31. * supplies one or more additional classes according to a naming
  32. * convention (by default, the fully-qualified name of the subclass
  33. * extending <code>IIMetadataFormatImpl</code>, plus the string
  34. * "Resources", plus the country, language, and variant codes
  35. * separated by underscores). At run time, calls to
  36. * <code>getElementDescription</code> or
  37. * <code>getAttributeDescription</code> will attempt to load such
  38. * classes dynamically according to the supplied locale, and will use
  39. * either the element name, or the element name followed by a '/'
  40. * character followed by the attribute name as a key. This key will
  41. * be supplied to the <code>ResourceBundle</code>'s
  42. * <code>getString</code> method, and the resulting localized
  43. * description of the node or attribute is returned.
  44. *
  45. * <p> The subclass may supply a different base name for the resource
  46. * bundles using the <code>setResourceBaseName</code> method.
  47. *
  48. * <p> A subclass may choose its own localization mechanism, if so
  49. * desired, by overriding the supplied implementations of
  50. * <code>getElementDescription</code> and
  51. * <code>getAttributeDescription</code>.
  52. *
  53. * @see ResourceBundle#getBundle(String,Locale)
  54. *
  55. * @version 0.5
  56. */
  57. public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
  58. /**
  59. * A <code>String</code> constant containing the standard format
  60. * name, <code>"javax_imageio_1.0"</code>.
  61. */
  62. public static final String standardMetadataFormatName =
  63. "javax_imageio_1.0";
  64. private static IIOMetadataFormat standardFormat = null;
  65. private String resourceBaseName = this.getClass().getName() + "Resources";
  66. private String rootName;
  67. // Element name (String) -> Element
  68. private HashMap elementMap = new HashMap();
  69. class Element {
  70. String elementName;
  71. int childPolicy;
  72. int minChildren = 0;
  73. int maxChildren = 0;
  74. // Child names (Strings)
  75. List childList = new ArrayList();
  76. // Parent names (Strings)
  77. List parentList = new ArrayList();
  78. // List of attribute names in the order they were added
  79. List attrList = new ArrayList();
  80. // Attr name (String) -> Attribute
  81. Map attrMap = new HashMap();
  82. ObjectValue objectValue;
  83. }
  84. class Attribute {
  85. String attrName;
  86. int valueType = VALUE_ARBITRARY;
  87. int dataType;
  88. boolean required;
  89. String defaultValue = null;
  90. // enumeration
  91. List enumeratedValues;
  92. // range
  93. String minValue;
  94. String maxValue;
  95. // list
  96. int listMinLength;
  97. int listMaxLength;
  98. }
  99. class ObjectValue {
  100. int valueType = VALUE_NONE;
  101. Class classType = null;
  102. Object defaultValue = null;
  103. // Meaningful only if valueType == VALUE_ENUMERATION
  104. List enumeratedValues = null;
  105. // Meaningful only if valueType == VALUE_RANGE
  106. Comparable minValue = null;
  107. Comparable maxValue = null;
  108. // Meaningful only if valueType == VALUE_LIST
  109. int arrayMinLength = 0;
  110. int arrayMaxLength = 0;
  111. }
  112. /**
  113. * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
  114. * with a given root element name and child policy (other than
  115. * <code>CHILD_POLICY_REPEAT</code>). Additional elements, and
  116. * their attributes and <code>Object</code> reference information
  117. * may be added using the various <code>add</code> methods.
  118. *
  119. * @param rootName the name of the root element.
  120. * @param childPolicy one of the <code>CHILD_POLICY_*</code> constants,
  121. * other than <code>CHILD_POLICY_REPEAT</code>.
  122. *
  123. * @exception IllegalArgumentException if <code>rootName</code> is
  124. * <code>null</code>.
  125. * @exception IllegalArgumentException if <code>childPolicy</code> is
  126. * not one of the predefined constants.
  127. */
  128. public IIOMetadataFormatImpl(String rootName,
  129. int childPolicy) {
  130. if (rootName == null) {
  131. throw new IllegalArgumentException("rootName == null!");
  132. }
  133. if (childPolicy < CHILD_POLICY_EMPTY ||
  134. childPolicy > CHILD_POLICY_MAX ||
  135. childPolicy == CHILD_POLICY_REPEAT) {
  136. throw new IllegalArgumentException("Invalid value for childPolicy!");
  137. }
  138. this.rootName = rootName;
  139. Element root = new Element();
  140. root.elementName = rootName;
  141. root.childPolicy = childPolicy;
  142. elementMap.put(rootName, root);
  143. }
  144. /**
  145. * Constructs a blank <code>IIOMetadataFormatImpl</code> instance,
  146. * with a given root element name and a child policy of
  147. * <code>CHILD_POLICY_REPEAT</code>. Additional elements, and
  148. * their attributes and <code>Object</code> reference information
  149. * may be added using the various <code>add</code> methods.
  150. *
  151. * @param rootName the name of the root element.
  152. * @param minChildren the minimum number of children of the node.
  153. * @param maxChildren the maximum number of children of the node.
  154. *
  155. * @exception IllegalArgumentException if <code>rootName</code> is
  156. * <code>null</code>.
  157. * @exception IllegalArgumentException if <code>minChildren</code>
  158. * is negative or larger than <code>maxChildren</code>.
  159. */
  160. public IIOMetadataFormatImpl(String rootName,
  161. int minChildren,
  162. int maxChildren) {
  163. if (rootName == null) {
  164. throw new IllegalArgumentException("rootName == null!");
  165. }
  166. if (minChildren < 0) {
  167. throw new IllegalArgumentException("minChildren < 0!");
  168. }
  169. if (minChildren > maxChildren) {
  170. throw new IllegalArgumentException("minChildren > maxChildren!");
  171. }
  172. Element root = new Element();
  173. root.elementName = rootName;
  174. root.childPolicy = CHILD_POLICY_REPEAT;
  175. root.minChildren = minChildren;
  176. root.maxChildren = maxChildren;
  177. this.rootName = rootName;
  178. elementMap.put(rootName, root);
  179. }
  180. /**
  181. * Sets a new base name for locating <code>ResourceBundle</code>s
  182. * containing descriptions of elements and attributes for this
  183. * format.
  184. *
  185. * <p> Prior to the first time this method is called, the base
  186. * name will be equal to <code>this.getClass().getName() +
  187. * "Resources"</code>.
  188. *
  189. * @param resourceBaseName a <code>String</code> containg the new
  190. * base name.
  191. *
  192. * @exception IllegalArgumentException if
  193. * <code>resourceBaseName</code> is <code>null</code>.
  194. *
  195. * @see #getResourceBaseName
  196. */
  197. protected void setResourceBaseName(String resourceBaseName) {
  198. if (resourceBaseName == null) {
  199. throw new IllegalArgumentException("resourceBaseName == null!");
  200. }
  201. this.resourceBaseName = resourceBaseName;
  202. }
  203. /**
  204. * Returns the currently set base name for locating
  205. * <code>ResourceBundle</code>s.
  206. *
  207. * @return a <code>String</code> containing the base name.
  208. *
  209. * @see #setResourceBaseName
  210. */
  211. protected String getResourceBaseName() {
  212. return resourceBaseName;
  213. }
  214. /**
  215. * Utility method for locating an element.
  216. *
  217. * @param mustAppear if <code>true</code>, throw an
  218. * <code>IllegalArgumentException</code> if no such node exists;
  219. * if <code>false</code>, just return null.
  220. */
  221. private Element getElement(String elementName, boolean mustAppear) {
  222. if (mustAppear && (elementName == null)) {
  223. throw new IllegalArgumentException("element name is null!");
  224. }
  225. Element element = (Element)elementMap.get(elementName);
  226. if (mustAppear && (element == null)) {
  227. throw new IllegalArgumentException("No such element: " +
  228. elementName);
  229. }
  230. return element;
  231. }
  232. private Element getElement(String elementName) {
  233. return getElement(elementName, true);
  234. }
  235. // Utility method for locating an attribute
  236. private Attribute getAttribute(String elementName, String attrName) {
  237. Element element = getElement(elementName);
  238. Attribute attr = (Attribute)element.attrMap.get(attrName);
  239. if (attr == null) {
  240. throw new IllegalArgumentException("No such attribute \"" +
  241. attrName + "\"!");
  242. }
  243. return attr;
  244. }
  245. // Setup
  246. /**
  247. * Adds a new element type to this metadata document format with a
  248. * child policy other than <code>CHILD_POLICY_REPEAT</code>.
  249. *
  250. * @param elementName the name of the new element.
  251. * @param parentName the name of the element that will be the
  252. * parent of the new element.
  253. * @param childPolicy one of the <code>CHILD_POLICY_*</code>
  254. * constants, other than <code>CHILD_POLICY_REPEAT</code>,
  255. * indicating the child policy of the new element.
  256. *
  257. * @exception IllegalArgumentException if <code>parentName</code>
  258. * is <code>null</code>, or is not a legal element name for this
  259. * format.
  260. * @exception IllegalArgumentException if <code>childPolicy</code>
  261. * is not one of the predefined constants.
  262. */
  263. protected void addElement(String elementName,
  264. String parentName,
  265. int childPolicy) {
  266. Element parent = getElement(parentName);
  267. if (childPolicy < CHILD_POLICY_EMPTY ||
  268. childPolicy > CHILD_POLICY_MAX ||
  269. childPolicy == CHILD_POLICY_REPEAT) {
  270. throw new IllegalArgumentException
  271. ("Invalid value for childPolicy!");
  272. }
  273. Element element = new Element();
  274. element.elementName = elementName;
  275. element.childPolicy = childPolicy;
  276. parent.childList.add(elementName);
  277. element.parentList.add(parentName);
  278. elementMap.put(elementName, element);
  279. }
  280. /**
  281. * Adds a new element type to this metadata document format with a
  282. * child policy of <code>CHILD_POLICY_REPEAT</code>.
  283. *
  284. * @param elementName the name of the new element.
  285. * @param parentName the name of the element that will be the
  286. * parent of the new element.
  287. * @param minChildren the minimum number of children of the node.
  288. * @param maxChildren the maximum number of children of the node.
  289. *
  290. * @exception IllegalArgumentException if <code>parentName</code>
  291. * is <code>null</code>, or is not a legal element name for this
  292. * format.
  293. * @exception IllegalArgumentException if <code>minChildren</code>
  294. * is negative or larger than <code>maxChildren</code>.
  295. */
  296. protected void addElement(String elementName,
  297. String parentName,
  298. int minChildren,
  299. int maxChildren) {
  300. Element parent = getElement(parentName);
  301. if (minChildren < 0) {
  302. throw new IllegalArgumentException("minChildren < 0!");
  303. }
  304. if (minChildren > maxChildren) {
  305. throw new IllegalArgumentException("minChildren > maxChildren!");
  306. }
  307. Element element = new Element();
  308. element.elementName = elementName;
  309. element.childPolicy = CHILD_POLICY_REPEAT;
  310. element.minChildren = minChildren;
  311. element.maxChildren = maxChildren;
  312. parent.childList.add(elementName);
  313. element.parentList.add(parentName);
  314. elementMap.put(elementName, element);
  315. }
  316. /**
  317. * Adds an existing element to the list of legal children for a
  318. * given parent node type.
  319. *
  320. * @param parentName the name of the element that will be the
  321. * new parent of the element.
  322. * @param elementName the name of the element to be addded as a
  323. * child.
  324. *
  325. * @exception IllegalArgumentException if <code>elementName</code>
  326. * is <code>null</code>, or is not a legal element name for this
  327. * format.
  328. * @exception IllegalArgumentException if <code>parentName</code>
  329. * is <code>null</code>, or is not a legal element name for this
  330. * format.
  331. */
  332. protected void addChildElement(String elementName, String parentName) {
  333. Element parent = getElement(parentName);
  334. Element element = getElement(elementName);
  335. parent.childList.add(elementName);
  336. element.parentList.add(parentName);
  337. }
  338. /**
  339. * Removes an element from the format. If no element with the
  340. * given name was present, nothing happens and no exception is
  341. * thrown.
  342. *
  343. * @param elementName the name of the element to be removed.
  344. */
  345. protected void removeElement(String elementName) {
  346. Element element = getElement(elementName, false);
  347. if (element != null) {
  348. Iterator iter = element.parentList.iterator();
  349. while (iter.hasNext()) {
  350. String parentName = (String)iter.next();
  351. Element parent = getElement(parentName, false);
  352. if (parent != null) {
  353. parent.childList.remove(elementName);
  354. }
  355. }
  356. elementMap.remove(elementName);
  357. }
  358. }
  359. /**
  360. * Adds a new attribute to a previously defined element that may
  361. * be set to an arbitrary value.
  362. *
  363. * @param elementName the name of the element.
  364. * @param attrName the name of the attribute being added.
  365. * @param dataType the data type (string format) of the attribute,
  366. * one of the <code>DATATYPE_*</code> constants.
  367. * @param required <code>true</code> if the attribute must be present.
  368. * @param defaultValue the default value for the attribute, or
  369. * <code>null</code>.
  370. *
  371. * @exception IllegalArgumentException if <code>elementName</code>
  372. * is <code>null</code>, or is not a legal element name for this
  373. * format.
  374. * @exception IllegalArgumentException if <code>attrName</code> is
  375. * <code>null</code>.
  376. * @exception IllegalArgumentException if <code>dataType</code> is
  377. * not one of the predefined constants.
  378. */
  379. protected void addAttribute(String elementName,
  380. String attrName,
  381. int dataType,
  382. boolean required,
  383. String defaultValue) {
  384. Element element = getElement(elementName);
  385. if (attrName == null) {
  386. throw new IllegalArgumentException("attrName == null!");
  387. }
  388. if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
  389. throw new IllegalArgumentException("Invalid value for dataType!");
  390. }
  391. Attribute attr = new Attribute();
  392. attr.attrName = attrName;
  393. attr.valueType = VALUE_ARBITRARY;
  394. attr.dataType = dataType;
  395. attr.required = required;
  396. attr.defaultValue = defaultValue;
  397. element.attrList.add(attrName);
  398. element.attrMap.put(attrName, attr);
  399. }
  400. /**
  401. * Adds a new attribute to a previously defined element that will
  402. * be defined by a set of enumerated values.
  403. *
  404. * @param elementName the name of the element.
  405. * @param attrName the name of the attribute being added.
  406. * @param dataType the data type (string format) of the attribute,
  407. * one of the <code>DATATYPE_*</code> constants.
  408. * @param required <code>true</code> if the attribute must be present.
  409. * @param defaultValue the default value for the attribute, or
  410. * <code>null</code>.
  411. * @param enumeratedValues a <code>List</code> of
  412. * <code>String</code>s containing the legal values for the
  413. * attribute.
  414. *
  415. * @exception IllegalArgumentException if <code>elementName</code>
  416. * is <code>null</code>, or is not a legal element name for this
  417. * format.
  418. * @exception IllegalArgumentException if <code>attrName</code> is
  419. * <code>null</code>.
  420. * @exception IllegalArgumentException if <code>dataType</code> is
  421. * not one of the predefined constants.
  422. * @exception IllegalArgumentException if
  423. * <code>enumeratedValues</code> is <code>null</code>.
  424. * @exception IllegalArgumentException if
  425. * <code>enumeratedValues</code> does not contain at least one
  426. * entry.
  427. * @exception IllegalArgumentException if
  428. * <code>enumeratedValues</code> contains an element that is not a
  429. * <code>String</code> or is <code>null</code>.
  430. */
  431. protected void addAttribute(String elementName,
  432. String attrName,
  433. int dataType,
  434. boolean required,
  435. String defaultValue,
  436. List enumeratedValues) {
  437. Element element = getElement(elementName);
  438. if (attrName == null) {
  439. throw new IllegalArgumentException("attrName == null!");
  440. }
  441. if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
  442. throw new IllegalArgumentException("Invalid value for dataType!");
  443. }
  444. if (enumeratedValues == null) {
  445. throw new IllegalArgumentException("enumeratedValues == null!");
  446. }
  447. if (enumeratedValues.size() == 0) {
  448. throw new IllegalArgumentException("enumeratedValues is empty!");
  449. }
  450. Iterator iter = enumeratedValues.iterator();
  451. while (iter.hasNext()) {
  452. Object o = iter.next();
  453. if (o == null) {
  454. throw new IllegalArgumentException
  455. ("enumeratedValues contains a null!");
  456. }
  457. if (!(o instanceof String)) {
  458. throw new IllegalArgumentException
  459. ("enumeratedValues contains a non-String value!");
  460. }
  461. }
  462. Attribute attr = new Attribute();
  463. attr.attrName = attrName;
  464. attr.valueType = VALUE_ENUMERATION;
  465. attr.dataType = dataType;
  466. attr.required = required;
  467. attr.defaultValue = defaultValue;
  468. attr.enumeratedValues = enumeratedValues;
  469. element.attrList.add(attrName);
  470. element.attrMap.put(attrName, attr);
  471. }
  472. /**
  473. * Adds a new attribute to a previously defined element that will
  474. * be defined by a range of values.
  475. *
  476. * @param elementName the name of the element.
  477. * @param attrName the name of the attribute being added.
  478. * @param dataType the data type (string format) of the attribute,
  479. * one of the <code>DATATYPE_*</code> constants.
  480. * @param required <code>true</code> if the attribute must be present.
  481. * @param defaultValue the default value for the attribute, or
  482. * <code>null</code>.
  483. * @param minValue the smallest (inclusive or exclusive depending
  484. * on the value of <code>minInclusive</code>) legal value for the
  485. * attribute, as a <code>String</code>.
  486. * @param maxValue the largest (inclusive or exclusive depending
  487. * on the value of <code>minInclusive</code>) legal value for the
  488. * attribute, as a <code>String</code>.
  489. * @param minInclusive <code>true</code> if <code>minValue</code>
  490. * is inclusive.
  491. * @param maxInclusive <code>true</code> if <code>maxValue</code>
  492. * is inclusive.
  493. *
  494. * @exception IllegalArgumentException if <code>elementName</code>
  495. * is <code>null</code>, or is not a legal element name for this
  496. * format.
  497. * @exception IllegalArgumentException if <code>attrName</code> is
  498. * <code>null</code>.
  499. * @exception IllegalArgumentException if <code>dataType</code> is
  500. * not one of the predefined constants.
  501. */
  502. protected void addAttribute(String elementName,
  503. String attrName,
  504. int dataType,
  505. boolean required,
  506. String defaultValue,
  507. String minValue,
  508. String maxValue,
  509. boolean minInclusive,
  510. boolean maxInclusive) {
  511. Element element = getElement(elementName);
  512. if (attrName == null) {
  513. throw new IllegalArgumentException("attrName == null!");
  514. }
  515. if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
  516. throw new IllegalArgumentException("Invalid value for dataType!");
  517. }
  518. Attribute attr = new Attribute();
  519. attr.attrName = attrName;
  520. attr.valueType = VALUE_RANGE;
  521. if (minInclusive) {
  522. attr.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
  523. }
  524. if (maxInclusive) {
  525. attr.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
  526. }
  527. attr.dataType = dataType;
  528. attr.required = required;
  529. attr.defaultValue = defaultValue;
  530. attr.minValue = minValue;
  531. attr.maxValue = maxValue;
  532. element.attrList.add(attrName);
  533. element.attrMap.put(attrName, attr);
  534. }
  535. /**
  536. * Adds a new attribute to a previously defined element that will
  537. * be defined by a list of values.
  538. *
  539. * @param elementName the name of the element.
  540. * @param attrName the name of the attribute being added.
  541. * @param dataType the data type (string format) of the attribute,
  542. * one of the <code>DATATYPE_*</code> constants.
  543. * @param required <code>true</code> if the attribute must be present.
  544. * @param listMinLength the smallest legal number of list items.
  545. * @param listMaxLength the largest legal number of list items.
  546. *
  547. * @exception IllegalArgumentException if <code>elementName</code>
  548. * is <code>null</code>, or is not a legal element name for this
  549. * format.
  550. * @exception IllegalArgumentException if <code>attrName</code> is
  551. * <code>null</code>.
  552. * @exception IllegalArgumentException if <code>dataType</code> is
  553. * not one of the predefined constants.
  554. * @exception IllegalArgumentException if
  555. * <code>listMinLength</code> is negative or larger than
  556. * <code>listMaxLength</code>.
  557. */
  558. protected void addAttribute(String elementName,
  559. String attrName,
  560. int dataType,
  561. boolean required,
  562. int listMinLength,
  563. int listMaxLength) {
  564. Element element = getElement(elementName);
  565. if (attrName == null) {
  566. throw new IllegalArgumentException("attrName == null!");
  567. }
  568. if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
  569. throw new IllegalArgumentException("Invalid value for dataType!");
  570. }
  571. if (listMinLength < 0 || listMinLength > listMaxLength) {
  572. throw new IllegalArgumentException("Invalid list bounds!");
  573. }
  574. Attribute attr = new Attribute();
  575. attr.attrName = attrName;
  576. attr.valueType = VALUE_LIST;
  577. attr.dataType = dataType;
  578. attr.required = required;
  579. attr.listMinLength = listMinLength;
  580. attr.listMaxLength = listMaxLength;
  581. element.attrList.add(attrName);
  582. element.attrMap.put(attrName, attr);
  583. }
  584. /**
  585. * Adds a new attribute to a previously defined element that will
  586. * be defined by the enumerated values <code>TRUE</code> and
  587. * <code>FALSE</code>, with a datatype of
  588. * <code>DATATYPE_BOOLEAN</code>.
  589. *
  590. * @param elementName the name of the element.
  591. * @param attrName the name of the attribute being added.
  592. * @param hasDefaultValue <code>true</code> if a default value
  593. * should be present.
  594. * @param defaultValue the default value for the attribute as a
  595. * <code>boolean</code>, ignored if <code>hasDefaultValue</code>
  596. * is <code>false</code>.
  597. *
  598. * @exception IllegalArgumentException if <code>elementName</code>
  599. * is <code>null</code>, or is not a legal element name for this
  600. * format.
  601. * @exception IllegalArgumentException if <code>attrName</code> is
  602. * <code>null</code>.
  603. */
  604. protected void addBooleanAttribute(String elementName,
  605. String attrName,
  606. boolean hasDefaultValue,
  607. boolean defaultValue) {
  608. List values = new ArrayList();
  609. values.add("TRUE");
  610. values.add("FALSE");
  611. String dval = null;
  612. if (hasDefaultValue) {
  613. dval = defaultValue ? "TRUE" : "FALSE";
  614. }
  615. addAttribute(elementName,
  616. attrName,
  617. DATATYPE_BOOLEAN,
  618. true,
  619. dval,
  620. values);
  621. }
  622. /**
  623. * Removes an attribute from a previously defined element. If no
  624. * attribute with the given name was present in the given element,
  625. * nothing happens and no exception is thrown.
  626. *
  627. * @param elementName the name of the element.
  628. * @param attrName the name of the attribute being removed.
  629. *
  630. * @exception IllegalArgumentException if <code>elementName</code>
  631. * is <code>null</code>, or is not a legal element name for this format.
  632. */
  633. protected void removeAttribute(String elementName, String attrName) {
  634. Element element = getElement(elementName);
  635. element.attrList.remove(attrName);
  636. element.attrMap.remove(attrName);
  637. }
  638. /**
  639. * Allows an <code>Object</code> reference of a given class type
  640. * to be stored in nodes implementing the named element. The
  641. * value of the <code>Object</code> is unconstrained other than by
  642. * its class type.
  643. *
  644. * <p> If an <code>Object</code> reference was previously allowed,
  645. * the previous settings are overwritten.
  646. *
  647. * @param elementName the name of the element.
  648. * @param classType a <code>Class</code> variable indicating the
  649. * legal class type for the object value.
  650. * @param required <code>true</code> if an object value must be present.
  651. * @param defaultValue the default value for the
  652. * <code>Object</code> reference, or <code>null</code>.
  653. *
  654. * @exception IllegalArgumentException if <code>elementName</code>
  655. * is <code>null</code>, or is not a legal element name for this format.
  656. */
  657. protected void addObjectValue(String elementName,
  658. Class classType,
  659. boolean required,
  660. Object defaultValue) {
  661. Element element = getElement(elementName);
  662. ObjectValue obj = new ObjectValue();
  663. obj.valueType = VALUE_ARBITRARY;
  664. obj.classType = classType;
  665. obj.defaultValue = defaultValue;
  666. element.objectValue = obj;
  667. }
  668. /**
  669. * Allows an <code>Object</code> reference of a given class type
  670. * to be stored in nodes implementing the named element. The
  671. * value of the <code>Object</code> must be one of the values
  672. * given by <code>enumeratedValues</code>.
  673. *
  674. * <p> If an <code>Object</code> reference was previously allowed,
  675. * the previous settings are overwritten.
  676. *
  677. * @param elementName the name of the element.
  678. * @param classType a <code>Class</code> variable indicating the
  679. * legal class type for the object value.
  680. * @param required <code>true</code> if an object value must be present.
  681. * @param defaultValue the default value for the
  682. * <code>Object</code> reference, or <code>null</code>.
  683. * @param enumeratedValues a <code>List</code> of
  684. * <code>Object</code>s containing the legal values for the
  685. * object reference.
  686. *
  687. * @exception IllegalArgumentException if <code>elementName</code>
  688. * is <code>null</code>, or is not a legal element name for this format.
  689. * @exception IllegalArgumentException if
  690. * <code>enumeratedValues</code> is <code>null</code>.
  691. * @exception IllegalArgumentException if
  692. * <code>enumeratedValues</code> does not contain at least one
  693. * entry.
  694. * @exception IllegalArgumentException if
  695. * <code>enumeratedValues</code> contains an element that is not
  696. * an instance of the class type denoted by <code>classType</code>
  697. * or is <code>null</code>.
  698. */
  699. protected void addObjectValue(String elementName,
  700. Class classType,
  701. boolean required,
  702. Object defaultValue,
  703. List enumeratedValues) {
  704. Element element = getElement(elementName);
  705. if (enumeratedValues == null) {
  706. throw new IllegalArgumentException("enumeratedValues == null!");
  707. }
  708. if (enumeratedValues.size() == 0) {
  709. throw new IllegalArgumentException("enumeratedValues is empty!");
  710. }
  711. Iterator iter = enumeratedValues.iterator();
  712. while (iter.hasNext()) {
  713. Object o = iter.next();
  714. if (o == null) {
  715. throw new IllegalArgumentException("enumeratedValues contains a null!");
  716. }
  717. if (!classType.isInstance(o)) {
  718. throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
  719. }
  720. }
  721. ObjectValue obj = new ObjectValue();
  722. obj.valueType = VALUE_ENUMERATION;
  723. obj.classType = classType;
  724. obj.defaultValue = defaultValue;
  725. obj.enumeratedValues = enumeratedValues;
  726. element.objectValue = obj;
  727. }
  728. /**
  729. * Allows an <code>Object</code> reference of a given class type
  730. * to be stored in nodes implementing the named element. The
  731. * value of the <code>Object</code> must be within the range given
  732. * by <code>minValue</code> and <code>maxValue</code>.
  733. * Furthermore, the class type must implement the
  734. * <code>Comparable</code> interface.
  735. *
  736. * <p> If an <code>Object</code> reference was previously allowed,
  737. * the previous settings are overwritten.
  738. *
  739. * @param elementName the name of the element.
  740. * @param classType a <code>Class</code> variable indicating the
  741. * legal class type for the object value.
  742. * @param defaultValue the default value for the
  743. * @param minValue the smallest (inclusive or exclusive depending
  744. * on the value of <code>minInclusive</code>) legal value for the
  745. * object value, as a <code>String</code>.
  746. * @param maxValue the largest (inclusive or exclusive depending
  747. * on the value of <code>minInclusive</code>) legal value for the
  748. * object value, as a <code>String</code>.
  749. * @param minInclusive <code>true</code> if <code>minValue</code>
  750. * is inclusive.
  751. * @param maxInclusive <code>true</code> if <code>maxValue</code>
  752. * is inclusive.
  753. *
  754. * @exception IllegalArgumentException if <code>elementName</code>
  755. * is <code>null</code>, or is not a legal element name for this
  756. * format.
  757. */
  758. protected void addObjectValue(String elementName,
  759. Class classType,
  760. Object defaultValue,
  761. Comparable minValue,
  762. Comparable maxValue,
  763. boolean minInclusive,
  764. boolean maxInclusive) {
  765. Element element = getElement(elementName);
  766. ObjectValue obj = new ObjectValue();
  767. obj.valueType = VALUE_RANGE;
  768. if (minInclusive) {
  769. obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
  770. }
  771. if (maxInclusive) {
  772. obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
  773. }
  774. obj.classType = classType;
  775. obj.defaultValue = defaultValue;
  776. obj.minValue = minValue;
  777. obj.maxValue = maxValue;
  778. element.objectValue = obj;
  779. }
  780. /**
  781. * Allows an <code>Object</code> reference of a given class type
  782. * to be stored in nodes implementing the named element. The
  783. * value of the <code>Object</code> must an array of objects of
  784. * class type given by <code>classType</code>, with at least
  785. * <code>arrayMinLength</code> and at most
  786. * <code>arrayMaxLength</code> elements.
  787. *
  788. * <p> If an <code>Object</code> reference was previously allowed,
  789. * the previous settings are overwritten.
  790. *
  791. * @param elementName the name of the element.
  792. * @param classType a <code>Class</code> variable indicating the
  793. * legal class type for the object value.
  794. * @param arrayMinLength the smallest legal length for the array.
  795. * @param arrayMaxLength the largest legal length for the array.
  796. *
  797. * @exception IllegalArgumentException if <code>elementName</code> is
  798. * not a legal element name for this format.
  799. */
  800. protected void addObjectValue(String elementName,
  801. Class classType,
  802. int arrayMinLength,
  803. int arrayMaxLength) {
  804. Element element = getElement(elementName);
  805. ObjectValue obj = new ObjectValue();
  806. obj.valueType = VALUE_LIST;
  807. obj.classType = classType;
  808. obj.arrayMinLength = arrayMinLength;
  809. obj.arrayMaxLength = arrayMaxLength;
  810. element.objectValue = obj;
  811. }
  812. /**
  813. * Disallows an <code>Object</code> reference from being stored in
  814. * nodes implementing the named element.
  815. *
  816. * @param elementName the name of the element.
  817. *
  818. * @exception IllegalArgumentException if <code>elementName</code> is
  819. * not a legal element name for this format.
  820. */
  821. protected void removeObjectValue(String elementName) {
  822. Element element = getElement(elementName);
  823. element.objectValue = null;
  824. }
  825. // Utility method
  826. // Methods from IIOMetadataFormat
  827. // Root
  828. public String getRootName() {
  829. return rootName;
  830. }
  831. // Multiplicity
  832. public abstract boolean canNodeAppear(String elementName,
  833. ImageTypeSpecifier imageType);
  834. public int getElementMinChildren(String elementName) {
  835. Element element = getElement(elementName);
  836. if (element.childPolicy != CHILD_POLICY_REPEAT) {
  837. throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
  838. }
  839. return element.minChildren;
  840. }
  841. public int getElementMaxChildren(String elementName) {
  842. Element element = getElement(elementName);
  843. if (element.childPolicy != CHILD_POLICY_REPEAT) {
  844. throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
  845. }
  846. return element.maxChildren;
  847. }
  848. private String getResource(String key, Locale locale) {
  849. if (locale == null) {
  850. locale = Locale.getDefault();
  851. }
  852. try {
  853. ResourceBundle bundle =
  854. ResourceBundle.getBundle(resourceBaseName, locale);
  855. return bundle.getString(key);
  856. } catch (MissingResourceException e) {
  857. return null;
  858. }
  859. }
  860. /**
  861. * Returns a <code>String</code> containing a description of the
  862. * named element, or <code>null</code>. The desciption will be
  863. * localized for the supplied <code>Locale</code> if possible.
  864. *
  865. * <p> The default implementation will first locate a
  866. * <code>ResourceBundle</code> using the current resource base
  867. * name set by <code>setResourceBaseName</code> and the supplied
  868. * <code>Locale</code>, using the fallback mechanism described in
  869. * the comments for <code>ResourceBundle.getBundle</code>. If a
  870. * <code>ResourceBundle</code> is found, the element name will be
  871. * used as a key to its <code>getString</code> method, and the
  872. * result returned. If no <code>ResourceBundle</code> is found,
  873. * or no such key is present, <code>null</code> will be returned.
  874. *
  875. * <p> If <code>locale</code> is <code>null</code>, the current
  876. * default <code>Locale</code> returned by <code>Locale.getLocale</code>
  877. * will be used.
  878. *
  879. * @param elementName the name of the element.
  880. * @param locale the <code>Locale</code> for which localization
  881. * will be attempted.
  882. *
  883. * @return the element description.
  884. *
  885. * @exception IllegalArgumentException if <code>elementName</code>
  886. * is <code>null</code>, or is not a legal element name for this format.
  887. *
  888. * @see #setResourceBaseName
  889. */
  890. public String getElementDescription(String elementName,
  891. Locale locale) {
  892. Element element = getElement(elementName);
  893. return getResource(elementName, locale);
  894. }
  895. // Children
  896. public int getChildPolicy(String elementName) {
  897. Element element = getElement(elementName);
  898. return element.childPolicy;
  899. }
  900. public String[] getChildNames(String elementName) {
  901. Element element = getElement(elementName);
  902. if (element.childPolicy == CHILD_POLICY_EMPTY) {
  903. return null;
  904. }
  905. return (String[])element.childList.toArray(new String[0]);
  906. }
  907. // Attributes
  908. public String[] getAttributeNames(String elementName) {
  909. Element element = getElement(elementName);
  910. List names = element.attrList;
  911. String[] result = new String[names.size()];
  912. return (String[])names.toArray(result);
  913. }
  914. public int getAttributeValueType(String elementName, String attrName) {
  915. Attribute attr = getAttribute(elementName, attrName);
  916. return attr.valueType;
  917. }
  918. public int getAttributeDataType(String elementName, String attrName) {
  919. Attribute attr = getAttribute(elementName, attrName);
  920. return attr.dataType;
  921. }
  922. public boolean isAttributeRequired(String elementName, String attrName) {
  923. Attribute attr = getAttribute(elementName, attrName);
  924. return attr.required;
  925. }
  926. public String getAttributeDefaultValue(String elementName,
  927. String attrName) {
  928. Attribute attr = getAttribute(elementName, attrName);
  929. return attr.defaultValue;
  930. }
  931. public String[] getAttributeEnumerations(String elementName,
  932. String attrName) {
  933. Attribute attr = getAttribute(elementName, attrName);
  934. if (attr.valueType != VALUE_ENUMERATION) {
  935. throw new IllegalArgumentException
  936. ("Attribute not an enumeration!");
  937. }
  938. List values = attr.enumeratedValues;
  939. Iterator iter = values.iterator();
  940. String[] result = new String[values.size()];
  941. return (String[])values.toArray(result);
  942. }
  943. public String getAttributeMinValue(String elementName, String attrName) {
  944. Attribute attr = getAttribute(elementName, attrName);
  945. if (attr.valueType != VALUE_RANGE &&
  946. attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
  947. attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
  948. attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
  949. throw new IllegalArgumentException("Attribute not a range!");
  950. }
  951. return attr.minValue;
  952. }
  953. public String getAttributeMaxValue(String elementName, String attrName) {
  954. Attribute attr = getAttribute(elementName, attrName);
  955. if (attr.valueType != VALUE_RANGE &&
  956. attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
  957. attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
  958. attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
  959. throw new IllegalArgumentException("Attribute not a range!");
  960. }
  961. return attr.maxValue;
  962. }
  963. public int getAttributeListMinLength(String elementName, String attrName) {
  964. Attribute attr = getAttribute(elementName, attrName);
  965. if (attr.valueType != VALUE_LIST) {
  966. throw new IllegalArgumentException("Attribute not a list!");
  967. }
  968. return attr.listMinLength;
  969. }
  970. public int getAttributeListMaxLength(String elementName, String attrName) {
  971. Attribute attr = getAttribute(elementName, attrName);
  972. if (attr.valueType != VALUE_LIST) {
  973. throw new IllegalArgumentException("Attribute not a list!");
  974. }
  975. return attr.listMaxLength;
  976. }
  977. /**
  978. * Returns a <code>String</code> containing a description of the
  979. * named attribute, or <code>null</code>. The desciption will be
  980. * localized for the supplied <code>Locale</code> if possible.
  981. *
  982. * <p> The default implementation will first locate a
  983. * <code>ResourceBundle</code> using the current resource base
  984. * name set by <code>setResourceBaseName</code> and the supplied
  985. * <code>Locale</code>, using the fallback mechanism described in
  986. * the comments for <code>ResourceBundle.getBundle</code>. If a
  987. * <code>ResourceBundle</code> is found, the element name followed
  988. * by a "/" character followed by the attribite name
  989. * (<code>elementName + "/" + attrName</code>) will be used as a
  990. * key to its <code>getString</code> method, and the result
  991. * returned. If no <code>ResourceBundle</code> is found, or no
  992. * such key is present, <code>null</code> will be returned.
  993. *
  994. * <p> If <code>locale</code> is <code>null</code>, the current
  995. * default <code>Locale</code> returned by <code>Locale.getLocale</code>
  996. * will be used.
  997. *
  998. * @param elementName the name of the element.
  999. * @param attrName the name of the attribute.
  1000. * @param locale the <code>Locale</code> for which localization
  1001. * will be attempted, or <code>null</code>.
  1002. *
  1003. * @return the attribute description.
  1004. *
  1005. * @exception IllegalArgumentException if <code>elementName</code>
  1006. * is <code>null</code>, or is not a legal element name for this format.
  1007. * @exception IllegalArgumentException if <code>attrName</code> is
  1008. * <code>null</code> or is not a legal attribute name for this
  1009. * element.
  1010. *
  1011. * @see #setResourceBaseName
  1012. */
  1013. public String getAttributeDescription(String elementName,
  1014. String attrName,
  1015. Locale locale) {
  1016. Element element = getElement(elementName);
  1017. if (attrName == null) {
  1018. throw new IllegalArgumentException("attrName == null!");
  1019. }
  1020. Attribute attr = (Attribute)element.attrMap.get(attrName);
  1021. if (attr == null) {
  1022. throw new IllegalArgumentException("No such attribute!");
  1023. }
  1024. String key = elementName + "/" + attrName;
  1025. return getResource(key, locale);
  1026. }
  1027. private ObjectValue getObjectValue(String elementName) {
  1028. Element element = getElement(elementName);
  1029. ObjectValue objv = (ObjectValue)element.objectValue;
  1030. if (objv == null) {
  1031. throw new IllegalArgumentException("No object within element " +
  1032. elementName + "!");
  1033. }
  1034. return objv;
  1035. }
  1036. public int getObjectValueType(String elementName) {
  1037. Element element = getElement(elementName);
  1038. ObjectValue objv = (ObjectValue)element.objectValue;
  1039. if (objv == null) {
  1040. return VALUE_NONE;
  1041. }
  1042. return objv.valueType;
  1043. }
  1044. public Class getObjectClass(String elementName) {
  1045. ObjectValue objv = getObjectValue(elementName);
  1046. return objv.classType;
  1047. }
  1048. public Object getObjectDefaultValue(String elementName) {
  1049. ObjectValue objv = getObjectValue(elementName);
  1050. return objv.defaultValue;
  1051. }
  1052. public Object[] getObjectEnumerations(String elementName) {
  1053. ObjectValue objv = getObjectValue(elementName);
  1054. if (objv.valueType != VALUE_ENUMERATION) {
  1055. throw new IllegalArgumentException("Not an enumeration!");
  1056. }
  1057. List vlist = objv.enumeratedValues;
  1058. Object[] values = new Object[vlist.size()];
  1059. return vlist.toArray(values);
  1060. }
  1061. public Comparable getObjectMinValue(String elementName) {
  1062. ObjectValue objv = getObjectValue(elementName);
  1063. if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
  1064. throw new IllegalArgumentException("Not a range!");
  1065. }
  1066. return objv.minValue;
  1067. }
  1068. public Comparable getObjectMaxValue(String elementName) {
  1069. ObjectValue objv = getObjectValue(elementName);
  1070. if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
  1071. throw new IllegalArgumentException("Not a range!");
  1072. }
  1073. return objv.maxValue;
  1074. }
  1075. public int getObjectArrayMinLength(String elementName) {
  1076. ObjectValue objv = getObjectValue(elementName);
  1077. if (objv.valueType != VALUE_LIST) {
  1078. throw new IllegalArgumentException("Not a list!");
  1079. }
  1080. return objv.arrayMinLength;
  1081. }
  1082. public int getObjectArrayMaxLength(String elementName) {
  1083. ObjectValue objv = getObjectValue(elementName);
  1084. if (objv.valueType != VALUE_LIST) {
  1085. throw new IllegalArgumentException("Not a list!");
  1086. }
  1087. return objv.arrayMaxLength;
  1088. }
  1089. // Standard format descriptor
  1090. private synchronized static void createStandardFormat() {
  1091. if (standardFormat == null) {
  1092. standardFormat = new StandardMetadataFormat();
  1093. }
  1094. }
  1095. /**
  1096. * Returns an <code>IIOMetadataFormat</code> object describing the
  1097. * standard, plug-in neutral <code>javax.imageio_1.0</code>
  1098. * metadata document format described in the comment of the
  1099. * <code>javax.imageio.metadata</code> package.
  1100. *
  1101. * @return a predefined <code>IIOMetadataFormat</code> instance.
  1102. */
  1103. public static IIOMetadataFormat getStandardFormatInstance() {
  1104. createStandardFormat();
  1105. return standardFormat;
  1106. }
  1107. }