1. /*
  2. * @(#)IIOMetadataFormatImpl.java 1.28 04/05/05
  3. *
  4. * Copyright 2004 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<String> 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 <T> void addObjectValue(String elementName,
  658. Class<T> classType,
  659. boolean required,
  660. T defaultValue)
  661. {
  662. Element element = getElement(elementName);
  663. ObjectValue obj = new ObjectValue();
  664. obj.valueType = VALUE_ARBITRARY;
  665. obj.classType = classType;
  666. obj.defaultValue = defaultValue;
  667. element.objectValue = obj;
  668. }
  669. /**
  670. * Allows an <code>Object</code> reference of a given class type
  671. * to be stored in nodes implementing the named element. The
  672. * value of the <code>Object</code> must be one of the values
  673. * given by <code>enumeratedValues</code>.
  674. *
  675. * <p> If an <code>Object</code> reference was previously allowed,
  676. * the previous settings are overwritten.
  677. *
  678. * @param elementName the name of the element.
  679. * @param classType a <code>Class</code> variable indicating the
  680. * legal class type for the object value.
  681. * @param required <code>true</code> if an object value must be present.
  682. * @param defaultValue the default value for the
  683. * <code>Object</code> reference, or <code>null</code>.
  684. * @param enumeratedValues a <code>List</code> of
  685. * <code>Object</code>s containing the legal values for the
  686. * object reference.
  687. *
  688. * @exception IllegalArgumentException if <code>elementName</code>
  689. * is <code>null</code>, or is not a legal element name for this format.
  690. * @exception IllegalArgumentException if
  691. * <code>enumeratedValues</code> is <code>null</code>.
  692. * @exception IllegalArgumentException if
  693. * <code>enumeratedValues</code> does not contain at least one
  694. * entry.
  695. * @exception IllegalArgumentException if
  696. * <code>enumeratedValues</code> contains an element that is not
  697. * an instance of the class type denoted by <code>classType</code>
  698. * or is <code>null</code>.
  699. */
  700. protected <T> void addObjectValue(String elementName,
  701. Class<T> classType,
  702. boolean required,
  703. T defaultValue,
  704. List<? extends T> enumeratedValues)
  705. {
  706. Element element = getElement(elementName);
  707. if (enumeratedValues == null) {
  708. throw new IllegalArgumentException("enumeratedValues == null!");
  709. }
  710. if (enumeratedValues.size() == 0) {
  711. throw new IllegalArgumentException("enumeratedValues is empty!");
  712. }
  713. Iterator iter = enumeratedValues.iterator();
  714. while (iter.hasNext()) {
  715. Object o = iter.next();
  716. if (o == null) {
  717. throw new IllegalArgumentException("enumeratedValues contains a null!");
  718. }
  719. if (!classType.isInstance(o)) {
  720. throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
  721. }
  722. }
  723. ObjectValue obj = new ObjectValue();
  724. obj.valueType = VALUE_ENUMERATION;
  725. obj.classType = classType;
  726. obj.defaultValue = defaultValue;
  727. obj.enumeratedValues = enumeratedValues;
  728. element.objectValue = obj;
  729. }
  730. /**
  731. * Allows an <code>Object</code> reference of a given class type
  732. * to be stored in nodes implementing the named element. The
  733. * value of the <code>Object</code> must be within the range given
  734. * by <code>minValue</code> and <code>maxValue</code>.
  735. * Furthermore, the class type must implement the
  736. * <code>Comparable</code> interface.
  737. *
  738. * <p> If an <code>Object</code> reference was previously allowed,
  739. * the previous settings are overwritten.
  740. *
  741. * @param elementName the name of the element.
  742. * @param classType a <code>Class</code> variable indicating the
  743. * legal class type for the object value.
  744. * @param defaultValue the default value for the
  745. * @param minValue the smallest (inclusive or exclusive depending
  746. * on the value of <code>minInclusive</code>) legal value for the
  747. * object value, as a <code>String</code>.
  748. * @param maxValue the largest (inclusive or exclusive depending
  749. * on the value of <code>minInclusive</code>) legal value for the
  750. * object value, as a <code>String</code>.
  751. * @param minInclusive <code>true</code> if <code>minValue</code>
  752. * is inclusive.
  753. * @param maxInclusive <code>true</code> if <code>maxValue</code>
  754. * is inclusive.
  755. *
  756. * @exception IllegalArgumentException if <code>elementName</code>
  757. * is <code>null</code>, or is not a legal element name for this
  758. * format.
  759. */
  760. protected <T extends Object & Comparable<? super T>> void
  761. addObjectValue(String elementName,
  762. Class<T> classType,
  763. T defaultValue,
  764. Comparable<? super T> minValue,
  765. Comparable<? super T> maxValue,
  766. boolean minInclusive,
  767. boolean maxInclusive)
  768. {
  769. Element element = getElement(elementName);
  770. ObjectValue obj = new ObjectValue();
  771. obj.valueType = VALUE_RANGE;
  772. if (minInclusive) {
  773. obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
  774. }
  775. if (maxInclusive) {
  776. obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
  777. }
  778. obj.classType = classType;
  779. obj.defaultValue = defaultValue;
  780. obj.minValue = minValue;
  781. obj.maxValue = maxValue;
  782. element.objectValue = obj;
  783. }
  784. /**
  785. * Allows an <code>Object</code> reference of a given class type
  786. * to be stored in nodes implementing the named element. The
  787. * value of the <code>Object</code> must an array of objects of
  788. * class type given by <code>classType</code>, with at least
  789. * <code>arrayMinLength</code> and at most
  790. * <code>arrayMaxLength</code> elements.
  791. *
  792. * <p> If an <code>Object</code> reference was previously allowed,
  793. * the previous settings are overwritten.
  794. *
  795. * @param elementName the name of the element.
  796. * @param classType a <code>Class</code> variable indicating the
  797. * legal class type for the object value.
  798. * @param arrayMinLength the smallest legal length for the array.
  799. * @param arrayMaxLength the largest legal length for the array.
  800. *
  801. * @exception IllegalArgumentException if <code>elementName</code> is
  802. * not a legal element name for this format.
  803. */
  804. protected void addObjectValue(String elementName,
  805. Class<?> classType,
  806. int arrayMinLength,
  807. int arrayMaxLength) {
  808. Element element = getElement(elementName);
  809. ObjectValue obj = new ObjectValue();
  810. obj.valueType = VALUE_LIST;
  811. obj.classType = classType;
  812. obj.arrayMinLength = arrayMinLength;
  813. obj.arrayMaxLength = arrayMaxLength;
  814. element.objectValue = obj;
  815. }
  816. /**
  817. * Disallows an <code>Object</code> reference from being stored in
  818. * nodes implementing the named element.
  819. *
  820. * @param elementName the name of the element.
  821. *
  822. * @exception IllegalArgumentException if <code>elementName</code> is
  823. * not a legal element name for this format.
  824. */
  825. protected void removeObjectValue(String elementName) {
  826. Element element = getElement(elementName);
  827. element.objectValue = null;
  828. }
  829. // Utility method
  830. // Methods from IIOMetadataFormat
  831. // Root
  832. public String getRootName() {
  833. return rootName;
  834. }
  835. // Multiplicity
  836. public abstract boolean canNodeAppear(String elementName,
  837. ImageTypeSpecifier imageType);
  838. public int getElementMinChildren(String elementName) {
  839. Element element = getElement(elementName);
  840. if (element.childPolicy != CHILD_POLICY_REPEAT) {
  841. throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
  842. }
  843. return element.minChildren;
  844. }
  845. public int getElementMaxChildren(String elementName) {
  846. Element element = getElement(elementName);
  847. if (element.childPolicy != CHILD_POLICY_REPEAT) {
  848. throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
  849. }
  850. return element.maxChildren;
  851. }
  852. private String getResource(String key, Locale locale) {
  853. if (locale == null) {
  854. locale = Locale.getDefault();
  855. }
  856. /**
  857. * If an applet supplies an implementation of IIOMetadataFormat and
  858. * resource bundles, then the resource bundle will need to be
  859. * accessed via the applet class loader. So first try the context
  860. * class loader to locate the resource bundle.
  861. * If that throws MissingResourceException, then try the
  862. * system class loader.
  863. */
  864. ClassLoader loader = (ClassLoader)
  865. java.security.AccessController.doPrivileged(
  866. new java.security.PrivilegedAction() {
  867. public Object run() {
  868. return Thread.currentThread().getContextClassLoader();
  869. }
  870. });
  871. ResourceBundle bundle = null;
  872. try {
  873. bundle = ResourceBundle.getBundle(resourceBaseName,
  874. locale, loader);
  875. } catch (MissingResourceException mre) {
  876. try {
  877. bundle = ResourceBundle.getBundle(resourceBaseName, locale);
  878. } catch (MissingResourceException mre1) {
  879. return null;
  880. }
  881. }
  882. try {
  883. return bundle.getString(key);
  884. } catch (MissingResourceException e) {
  885. return null;
  886. }
  887. }
  888. /**
  889. * Returns a <code>String</code> containing a description of the
  890. * named element, or <code>null</code>. The desciption will be
  891. * localized for the supplied <code>Locale</code> if possible.
  892. *
  893. * <p> The default implementation will first locate a
  894. * <code>ResourceBundle</code> using the current resource base
  895. * name set by <code>setResourceBaseName</code> and the supplied
  896. * <code>Locale</code>, using the fallback mechanism described in
  897. * the comments for <code>ResourceBundle.getBundle</code>. If a
  898. * <code>ResourceBundle</code> is found, the element name will be
  899. * used as a key to its <code>getString</code> method, and the
  900. * result returned. If no <code>ResourceBundle</code> is found,
  901. * or no such key is present, <code>null</code> will be returned.
  902. *
  903. * <p> If <code>locale</code> is <code>null</code>, the current
  904. * default <code>Locale</code> returned by <code>Locale.getLocale</code>
  905. * will be used.
  906. *
  907. * @param elementName the name of the element.
  908. * @param locale the <code>Locale</code> for which localization
  909. * will be attempted.
  910. *
  911. * @return the element description.
  912. *
  913. * @exception IllegalArgumentException if <code>elementName</code>
  914. * is <code>null</code>, or is not a legal element name for this format.
  915. *
  916. * @see #setResourceBaseName
  917. */
  918. public String getElementDescription(String elementName,
  919. Locale locale) {
  920. Element element = getElement(elementName);
  921. return getResource(elementName, locale);
  922. }
  923. // Children
  924. public int getChildPolicy(String elementName) {
  925. Element element = getElement(elementName);
  926. return element.childPolicy;
  927. }
  928. public String[] getChildNames(String elementName) {
  929. Element element = getElement(elementName);
  930. if (element.childPolicy == CHILD_POLICY_EMPTY) {
  931. return null;
  932. }
  933. return (String[])element.childList.toArray(new String[0]);
  934. }
  935. // Attributes
  936. public String[] getAttributeNames(String elementName) {
  937. Element element = getElement(elementName);
  938. List names = element.attrList;
  939. String[] result = new String[names.size()];
  940. return (String[])names.toArray(result);
  941. }
  942. public int getAttributeValueType(String elementName, String attrName) {
  943. Attribute attr = getAttribute(elementName, attrName);
  944. return attr.valueType;
  945. }
  946. public int getAttributeDataType(String elementName, String attrName) {
  947. Attribute attr = getAttribute(elementName, attrName);
  948. return attr.dataType;
  949. }
  950. public boolean isAttributeRequired(String elementName, String attrName) {
  951. Attribute attr = getAttribute(elementName, attrName);
  952. return attr.required;
  953. }
  954. public String getAttributeDefaultValue(String elementName,
  955. String attrName) {
  956. Attribute attr = getAttribute(elementName, attrName);
  957. return attr.defaultValue;
  958. }
  959. public String[] getAttributeEnumerations(String elementName,
  960. String attrName) {
  961. Attribute attr = getAttribute(elementName, attrName);
  962. if (attr.valueType != VALUE_ENUMERATION) {
  963. throw new IllegalArgumentException
  964. ("Attribute not an enumeration!");
  965. }
  966. List values = attr.enumeratedValues;
  967. Iterator iter = values.iterator();
  968. String[] result = new String[values.size()];
  969. return (String[])values.toArray(result);
  970. }
  971. public String getAttributeMinValue(String elementName, String attrName) {
  972. Attribute attr = getAttribute(elementName, attrName);
  973. if (attr.valueType != VALUE_RANGE &&
  974. attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
  975. attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
  976. attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
  977. throw new IllegalArgumentException("Attribute not a range!");
  978. }
  979. return attr.minValue;
  980. }
  981. public String getAttributeMaxValue(String elementName, String attrName) {
  982. Attribute attr = getAttribute(elementName, attrName);
  983. if (attr.valueType != VALUE_RANGE &&
  984. attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
  985. attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
  986. attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
  987. throw new IllegalArgumentException("Attribute not a range!");
  988. }
  989. return attr.maxValue;
  990. }
  991. public int getAttributeListMinLength(String elementName, String attrName) {
  992. Attribute attr = getAttribute(elementName, attrName);
  993. if (attr.valueType != VALUE_LIST) {
  994. throw new IllegalArgumentException("Attribute not a list!");
  995. }
  996. return attr.listMinLength;
  997. }
  998. public int getAttributeListMaxLength(String elementName, String attrName) {
  999. Attribute attr = getAttribute(elementName, attrName);
  1000. if (attr.valueType != VALUE_LIST) {
  1001. throw new IllegalArgumentException("Attribute not a list!");
  1002. }
  1003. return attr.listMaxLength;
  1004. }
  1005. /**
  1006. * Returns a <code>String</code> containing a description of the
  1007. * named attribute, or <code>null</code>. The desciption will be
  1008. * localized for the supplied <code>Locale</code> if possible.
  1009. *
  1010. * <p> The default implementation will first locate a
  1011. * <code>ResourceBundle</code> using the current resource base
  1012. * name set by <code>setResourceBaseName</code> and the supplied
  1013. * <code>Locale</code>, using the fallback mechanism described in
  1014. * the comments for <code>ResourceBundle.getBundle</code>. If a
  1015. * <code>ResourceBundle</code> is found, the element name followed
  1016. * by a "/" character followed by the attribute name
  1017. * (<code>elementName + "/" + attrName</code>) will be used as a
  1018. * key to its <code>getString</code> method, and the result
  1019. * returned. If no <code>ResourceBundle</code> is found, or no
  1020. * such key is present, <code>null</code> will be returned.
  1021. *
  1022. * <p> If <code>locale</code> is <code>null</code>, the current
  1023. * default <code>Locale</code> returned by <code>Locale.getLocale</code>
  1024. * will be used.
  1025. *
  1026. * @param elementName the name of the element.
  1027. * @param attrName the name of the attribute.
  1028. * @param locale the <code>Locale</code> for which localization
  1029. * will be attempted, or <code>null</code>.
  1030. *
  1031. * @return the attribute description.
  1032. *
  1033. * @exception IllegalArgumentException if <code>elementName</code>
  1034. * is <code>null</code>, or is not a legal element name for this format.
  1035. * @exception IllegalArgumentException if <code>attrName</code> is
  1036. * <code>null</code> or is not a legal attribute name for this
  1037. * element.
  1038. *
  1039. * @see #setResourceBaseName
  1040. */
  1041. public String getAttributeDescription(String elementName,
  1042. String attrName,
  1043. Locale locale) {
  1044. Element element = getElement(elementName);
  1045. if (attrName == null) {
  1046. throw new IllegalArgumentException("attrName == null!");
  1047. }
  1048. Attribute attr = (Attribute)element.attrMap.get(attrName);
  1049. if (attr == null) {
  1050. throw new IllegalArgumentException("No such attribute!");
  1051. }
  1052. String key = elementName + "/" + attrName;
  1053. return getResource(key, locale);
  1054. }
  1055. private ObjectValue getObjectValue(String elementName) {
  1056. Element element = getElement(elementName);
  1057. ObjectValue objv = (ObjectValue)element.objectValue;
  1058. if (objv == null) {
  1059. throw new IllegalArgumentException("No object within element " +
  1060. elementName + "!");
  1061. }
  1062. return objv;
  1063. }
  1064. public int getObjectValueType(String elementName) {
  1065. Element element = getElement(elementName);
  1066. ObjectValue objv = (ObjectValue)element.objectValue;
  1067. if (objv == null) {
  1068. return VALUE_NONE;
  1069. }
  1070. return objv.valueType;
  1071. }
  1072. public Class<?> getObjectClass(String elementName) {
  1073. ObjectValue objv = getObjectValue(elementName);
  1074. return objv.classType;
  1075. }
  1076. public Object getObjectDefaultValue(String elementName) {
  1077. ObjectValue objv = getObjectValue(elementName);
  1078. return objv.defaultValue;
  1079. }
  1080. public Object[] getObjectEnumerations(String elementName) {
  1081. ObjectValue objv = getObjectValue(elementName);
  1082. if (objv.valueType != VALUE_ENUMERATION) {
  1083. throw new IllegalArgumentException("Not an enumeration!");
  1084. }
  1085. List vlist = objv.enumeratedValues;
  1086. Object[] values = new Object[vlist.size()];
  1087. return vlist.toArray(values);
  1088. }
  1089. public Comparable<?> getObjectMinValue(String elementName) {
  1090. ObjectValue objv = getObjectValue(elementName);
  1091. if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
  1092. throw new IllegalArgumentException("Not a range!");
  1093. }
  1094. return objv.minValue;
  1095. }
  1096. public Comparable<?> getObjectMaxValue(String elementName) {
  1097. ObjectValue objv = getObjectValue(elementName);
  1098. if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
  1099. throw new IllegalArgumentException("Not a range!");
  1100. }
  1101. return objv.maxValue;
  1102. }
  1103. public int getObjectArrayMinLength(String elementName) {
  1104. ObjectValue objv = getObjectValue(elementName);
  1105. if (objv.valueType != VALUE_LIST) {
  1106. throw new IllegalArgumentException("Not a list!");
  1107. }
  1108. return objv.arrayMinLength;
  1109. }
  1110. public int getObjectArrayMaxLength(String elementName) {
  1111. ObjectValue objv = getObjectValue(elementName);
  1112. if (objv.valueType != VALUE_LIST) {
  1113. throw new IllegalArgumentException("Not a list!");
  1114. }
  1115. return objv.arrayMaxLength;
  1116. }
  1117. // Standard format descriptor
  1118. private synchronized static void createStandardFormat() {
  1119. if (standardFormat == null) {
  1120. standardFormat = new StandardMetadataFormat();
  1121. }
  1122. }
  1123. /**
  1124. * Returns an <code>IIOMetadataFormat</code> object describing the
  1125. * standard, plug-in neutral <code>javax.imageio_1.0</code>
  1126. * metadata document format described in the comment of the
  1127. * <code>javax.imageio.metadata</code> package.
  1128. *
  1129. * @return a predefined <code>IIOMetadataFormat</code> instance.
  1130. */
  1131. public static IIOMetadataFormat getStandardFormatInstance() {
  1132. createStandardFormat();
  1133. return standardFormat;
  1134. }
  1135. }