1. /*
  2. * @(#)IIOMetadataNode.java 1.36 02/03/21
  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.Iterator;
  10. import java.util.List;
  11. import org.w3c.dom.Attr;
  12. import org.w3c.dom.Document;
  13. import org.w3c.dom.Element;
  14. import org.w3c.dom.DOMException;
  15. import org.w3c.dom.NamedNodeMap;
  16. import org.w3c.dom.Node;
  17. import org.w3c.dom.NodeList;
  18. class IIODOMException extends DOMException {
  19. public IIODOMException(short code, String message) {
  20. super(code, message);
  21. }
  22. }
  23. class IIONamedNodeMap implements NamedNodeMap {
  24. List nodes;
  25. public IIONamedNodeMap(List nodes) {
  26. this.nodes = nodes;
  27. }
  28. public int getLength() {
  29. return nodes.size();
  30. }
  31. public Node getNamedItem(String name) {
  32. Iterator iter = nodes.iterator();
  33. while (iter.hasNext()) {
  34. Node node = (Node)iter.next();
  35. if (name.equals(node.getNodeName())) {
  36. return node;
  37. }
  38. }
  39. return null;
  40. }
  41. public Node item(int index) {
  42. Node node = (Node)nodes.get(index);
  43. return node;
  44. }
  45. public Node removeNamedItem(java.lang.String name) {
  46. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
  47. "This NamedNodeMap is read-only!");
  48. }
  49. public Node setNamedItem(Node arg) {
  50. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
  51. "This NamedNodeMap is read-only!");
  52. }
  53. /**
  54. * Equivalent to <code>getNamedItem(localName)</code>.
  55. */
  56. public Node getNamedItemNS(String namespaceURI, String localName) {
  57. return getNamedItem(localName);
  58. }
  59. /**
  60. * Equivalent to <code>setNamedItem(arg)</code>.
  61. */
  62. public Node setNamedItemNS(Node arg) {
  63. return setNamedItem(arg);
  64. }
  65. /**
  66. * Equivalent to <code>removeNamedItem(localName)</code>.
  67. */
  68. public Node removeNamedItemNS(String namespaceURI, String localName) {
  69. return removeNamedItem(localName);
  70. }
  71. }
  72. class IIONodeList implements NodeList {
  73. List nodes;
  74. public IIONodeList(List nodes) {
  75. this.nodes = nodes;
  76. }
  77. public int getLength() {
  78. return nodes.size();
  79. }
  80. public Node item(int index) {
  81. if (index < 0 || index > nodes.size()) {
  82. return null;
  83. }
  84. return (Node)nodes.get(index);
  85. }
  86. }
  87. class IIOAttr extends IIOMetadataNode implements Attr {
  88. boolean specified = true;
  89. Element owner;
  90. String name;
  91. String value;
  92. public IIOAttr(Element owner, String name, String value) {
  93. this.owner = owner;
  94. this.name = name;
  95. this.value = value;
  96. }
  97. public String getName() {
  98. return name;
  99. }
  100. public String getNodeName() {
  101. return name;
  102. }
  103. public short getNodeType() {
  104. return ATTRIBUTE_NODE;
  105. }
  106. public boolean getSpecified() {
  107. return specified;
  108. }
  109. public String getValue() {
  110. return value;
  111. }
  112. public String getNodeValue() {
  113. return value;
  114. }
  115. public void setValue(String value) {
  116. this.value = value;
  117. }
  118. public void setNodeValue(String value) {
  119. this.value = value;
  120. }
  121. public Element getOwnerElement() {
  122. return owner;
  123. }
  124. }
  125. /**
  126. * A class representing a node in a meta-data tree, which implements
  127. * the <a
  128. * href="../../../../api/org/w3c/dom/Element.html">
  129. * <code>org.w3c.dom.Element</code></a> interface and additionally allows
  130. * for the storage of non-textual objects via the
  131. * <code>getUserObject</code> and <code>setUserObject</code> methods.
  132. *
  133. * <p> This class is not intended to be used for general XML
  134. * processing. In particular, <code>Element</code> nodes created
  135. * within the Image I/O API are not compatible with those created by
  136. * Sun's standard implementation of the <code>org.w3.dom</code> API.
  137. * In particular, the implementation is tuned for simple uses and may
  138. * not perform well for intensive processing.
  139. *
  140. * <p> Namespaces are ignored in this implementation. The terms "tag
  141. * name" and "node name" are always considered to be synonymous.
  142. *
  143. * @see IIOMetadata#getAsTree
  144. * @see IIOMetadata#setFromTree
  145. * @see IIOMetadata#mergeTree
  146. *
  147. * @version 0.5
  148. */
  149. public class IIOMetadataNode implements Element, NodeList {
  150. /**
  151. * The name of the node as a <code>String</code>.
  152. */
  153. private String nodeName = null;
  154. /**
  155. * The value of the node as a <code>String</code>. The Image I/O
  156. * API typically does not make use of the node value.
  157. */
  158. private String nodeValue = null;
  159. /**
  160. * The <code>Object</code> value associated with this node.
  161. */
  162. private Object userObject = null;
  163. /**
  164. * The parent node of this node, or <code>null</code> if this node
  165. * forms the root of its own tree.
  166. */
  167. private IIOMetadataNode parent = null;
  168. /**
  169. * The number of child nodes.
  170. */
  171. private int numChildren = 0;
  172. /**
  173. * The first (leftmost) child node of this node, or
  174. * <code>null</code> if this node is a leaf node.
  175. */
  176. private IIOMetadataNode firstChild = null;
  177. /**
  178. * The last (rightmost) child node of this node, or
  179. * <code>null</code> if this node is a leaf node.
  180. */
  181. private IIOMetadataNode lastChild = null;
  182. /**
  183. * The next (right) sibling node of this node, or
  184. * <code>null</code> if this node is its parent's last child node.
  185. */
  186. private IIOMetadataNode nextSibling = null;
  187. /**
  188. * The previous (left) sibling node of this node, or
  189. * <code>null</code> if this node is its parent's first child node.
  190. */
  191. private IIOMetadataNode previousSibling = null;
  192. /**
  193. * A <code>List</code> of <code>IIOAttr</code> nodes representing
  194. * attributes.
  195. */
  196. private List attributes = new ArrayList();
  197. /**
  198. * Constructs an empty <code>IIOMetadataNode</code>.
  199. */
  200. public IIOMetadataNode() {}
  201. /**
  202. * Constructs an <code>IIOMetadataNode</code> with a given node
  203. * name.
  204. *
  205. * @param nodeName the name of the node, as a <code>String</code>.
  206. */
  207. public IIOMetadataNode(String nodeName) {
  208. this.nodeName = nodeName;
  209. }
  210. /**
  211. * Check that the node is either <code>null</code> or an
  212. * <code>IIOMetadataNode</code>.
  213. */
  214. private void checkNode(Node node) throws DOMException {
  215. if (node == null) {
  216. return;
  217. }
  218. if (!(node instanceof IIOMetadataNode)) {
  219. throw new IIODOMException(DOMException.WRONG_DOCUMENT_ERR,
  220. "Node not an IIOMetadataNode!");
  221. }
  222. }
  223. // Methods from Node
  224. /**
  225. * Returns the node name associated with this node.
  226. *
  227. * @return the node name, as a <code>String</code>.
  228. */
  229. public String getNodeName() {
  230. return nodeName;
  231. }
  232. public String getNodeValue() throws DOMException {
  233. return nodeValue;
  234. }
  235. public void setNodeValue(String nodeValue) throws DOMException {
  236. this.nodeValue = nodeValue;
  237. }
  238. /**
  239. * Returns the node type, which is always
  240. * <code>ELEMENT_NODE</code>.
  241. *
  242. * @return the <code>short</code> value <code>ELEMENT_NODE</code>.
  243. */
  244. public short getNodeType() {
  245. return ELEMENT_NODE;
  246. }
  247. /**
  248. * Returns the parent of this node. A <code>null</code> value
  249. * indicates that the node is the root of its own tree. To add a
  250. * node to an existing tree, use one of the
  251. * <code>insertBefore</code>, <code>replaceChild</code>, or
  252. * <code>appendChild</code> methods.
  253. *
  254. * @return the parent, as a <code>Node</code>.
  255. *
  256. * @see #insertBefore
  257. * @see #replaceChild
  258. * @see #appendChild
  259. */
  260. public Node getParentNode() {
  261. return parent;
  262. }
  263. public NodeList getChildNodes() {
  264. return this;
  265. }
  266. /**
  267. * Returns the first child of this node, or <code>null</code> if
  268. * the node has no children.
  269. *
  270. * @return the first child, as a <code>Node</code>, or
  271. * <code>null</code>
  272. */
  273. public Node getFirstChild() {
  274. return firstChild;
  275. }
  276. /**
  277. * Returns the last child of this node, or <code>null</code> if
  278. * the node has no children.
  279. *
  280. * @return the last child, as a <code>Node</code>, or
  281. * <code>null</code>.
  282. */
  283. public Node getLastChild() {
  284. return lastChild;
  285. }
  286. /**
  287. * Returns the previous sibling of this node, or <code>null</code>
  288. * if this node has no previous sibling.
  289. *
  290. * @return the previous sibling, as a <code>Node</code>, or
  291. * <code>null</code>.
  292. */
  293. public Node getPreviousSibling() {
  294. return previousSibling;
  295. }
  296. /**
  297. * Returns the next sibling of this node, or <code>null</code> if
  298. * the node has no next sibling.
  299. *
  300. * @return the next sibling, as a <code>Node</code>, or
  301. * <code>null</code>.
  302. */
  303. public Node getNextSibling() {
  304. return nextSibling;
  305. }
  306. public NamedNodeMap getAttributes() {
  307. return new IIONamedNodeMap(attributes);
  308. }
  309. /**
  310. * Returns <code>null</code>, since <code>IIOMetadataNode</code>s
  311. * do not belong to any <code>Document</code>.
  312. *
  313. * @return <code>null</code>.
  314. */
  315. public Document getOwnerDocument() {
  316. return null;
  317. }
  318. /**
  319. * Inserts the node <code>newChild</code> before the existing
  320. * child node <code>refChild</code>. If <code>refChild</code> is
  321. * <code>null</code>, insert <code>newChild</code> at the end of
  322. * the list of children.
  323. *
  324. * @param newChild the <code>Node</code> to insert.
  325. * @param refChild the reference <code>Node</code>.
  326. *
  327. * @return the node being inserted.
  328. *
  329. * @exception IllegalArgumentException if <code>newChild</code> is
  330. * <code>null</code>.
  331. */
  332. public Node insertBefore(Node newChild,
  333. Node refChild) {
  334. if (newChild == null) {
  335. throw new IllegalArgumentException("newChild == null!");
  336. }
  337. checkNode(newChild);
  338. checkNode(refChild);
  339. IIOMetadataNode newChildNode = (IIOMetadataNode)newChild;
  340. IIOMetadataNode refChildNode = (IIOMetadataNode)refChild;
  341. // Siblings, can be null.
  342. IIOMetadataNode previous = null;
  343. IIOMetadataNode next = null;
  344. if (refChild == null) {
  345. previous = this.lastChild;
  346. next = null;
  347. this.lastChild = newChildNode;
  348. } else {
  349. previous = refChildNode.previousSibling;
  350. next = refChildNode;
  351. }
  352. if (previous != null) {
  353. previous.nextSibling = newChildNode;
  354. }
  355. if (next != null) {
  356. next.previousSibling = newChildNode;
  357. }
  358. newChildNode.parent = this;
  359. newChildNode.previousSibling = previous;
  360. newChildNode.nextSibling = next;
  361. // N.B.: O.K. if refChild == null
  362. if (this.firstChild == refChildNode) {
  363. this.firstChild = newChildNode;
  364. }
  365. ++numChildren;
  366. return newChildNode;
  367. }
  368. /**
  369. * Replaces the child node <code>oldChild</code> with
  370. * <code>newChild</code> in the list of children, and returns the
  371. * <code>oldChild</code> node.
  372. *
  373. * @param newChild the <code>Node</code> to insert.
  374. * @param oldChild the <code>Node</code> to be replaced.
  375. *
  376. * @return the node replaced.
  377. *
  378. * @exception IllegalArgumentException if <code>newChild</code> is
  379. * <code>null</code>.
  380. */
  381. public Node replaceChild(Node newChild,
  382. Node oldChild) {
  383. if (newChild == null) {
  384. throw new IllegalArgumentException("newChild == null!");
  385. }
  386. checkNode(newChild);
  387. checkNode(oldChild);
  388. IIOMetadataNode newChildNode = (IIOMetadataNode)newChild;
  389. IIOMetadataNode oldChildNode = (IIOMetadataNode)oldChild;
  390. IIOMetadataNode previous = oldChildNode.previousSibling;
  391. IIOMetadataNode next = oldChildNode.nextSibling;
  392. if (previous != null) {
  393. previous.nextSibling = newChildNode;
  394. }
  395. if (next != null) {
  396. next.previousSibling = newChildNode;
  397. }
  398. newChildNode.parent = this;
  399. newChildNode.previousSibling = previous;
  400. newChildNode.nextSibling = next;
  401. if (firstChild == oldChildNode) {
  402. firstChild = newChildNode;
  403. }
  404. if (lastChild == oldChildNode) {
  405. lastChild = newChildNode;
  406. }
  407. oldChildNode.parent = null;
  408. oldChildNode.previousSibling = null;
  409. oldChildNode.nextSibling = null;
  410. return oldChildNode;
  411. }
  412. /**
  413. * Removes the child node indicated by <code>oldChild</code> from
  414. * the list of children, and returns it.
  415. *
  416. * @param oldChild the <code>Node</code> to be removed.
  417. *
  418. * @return the node removed.
  419. *
  420. * @exception IllegalArgumentException if <code>oldChild</code> is
  421. * <code>null</code>.
  422. */
  423. public Node removeChild(Node oldChild) {
  424. if (oldChild == null) {
  425. throw new IllegalArgumentException("oldChild == null!");
  426. }
  427. checkNode(oldChild);
  428. IIOMetadataNode oldChildNode = (IIOMetadataNode)oldChild;
  429. IIOMetadataNode previous = oldChildNode.previousSibling;
  430. IIOMetadataNode next = oldChildNode.nextSibling;
  431. if (previous != null) {
  432. previous.nextSibling = next;
  433. }
  434. if (next != null) {
  435. next.previousSibling = previous;
  436. }
  437. if (this.firstChild == oldChildNode) {
  438. this.firstChild = next;
  439. }
  440. if (this.lastChild == oldChildNode) {
  441. this.lastChild = previous;
  442. }
  443. oldChildNode.parent = null;
  444. oldChildNode.previousSibling = null;
  445. oldChildNode.nextSibling = null;
  446. --numChildren;
  447. return oldChildNode;
  448. }
  449. /**
  450. * Adds the node <code>newChild</code> to the end of the list of
  451. * children of this node.
  452. *
  453. * @param newChild the <code>Node</code> to insert.
  454. *
  455. * @return the node added.
  456. *
  457. * @exception IllegalArgumentException if <code>newChild</code> is
  458. * <code>null</code>.
  459. */
  460. public Node appendChild(Node newChild) {
  461. if (newChild == null) {
  462. throw new IllegalArgumentException("newChild == null!");
  463. }
  464. checkNode(newChild);
  465. // insertBefore will increment numChildren
  466. return insertBefore(newChild, null);
  467. }
  468. /**
  469. * Returns <code>true</code> if this node has child nodes.
  470. *
  471. * @return <code>true</code> if this node has children.
  472. */
  473. public boolean hasChildNodes() {
  474. return numChildren > 0;
  475. }
  476. /**
  477. * Returns a duplicate of this node. The duplicate node has no
  478. * parent (<code>getParentNode</code> returns <code>null</code>).
  479. * If a shallow clone is being performed (<code>deep</code> is
  480. * <code>false</code>), the new node will not have any children or
  481. * siblings. If a deep clone is being performed, the new node
  482. * will form the root of a complete cloned subtree.
  483. *
  484. * @param deep if <code>true</code>, recursively clone the subtree
  485. * under the specified node; if <code>false</code>, clone only the
  486. * node itself.
  487. *
  488. * @return the duplicate node.
  489. */
  490. public Node cloneNode(boolean deep) {
  491. IIOMetadataNode newNode = new IIOMetadataNode(this.nodeName);
  492. newNode.setUserObject(getUserObject());
  493. // Attributes
  494. if (deep) {
  495. for (IIOMetadataNode child = firstChild;
  496. child != null;
  497. child = child.nextSibling) {
  498. newNode.appendChild(child.cloneNode(true));
  499. }
  500. }
  501. return newNode;
  502. }
  503. /**
  504. * Does nothing, since <code>IIOMetadataNode</code>s do not
  505. * contain <code>Text</code> children.
  506. */
  507. public void normalize() {
  508. }
  509. /**
  510. * Returns <code>false</code> since DOM features are not
  511. * supported.
  512. *
  513. * @return <code>false</code>.
  514. *
  515. * @param feature a <code>String</code>, which is ignored.
  516. * @param version a <code>String</code>, which is ignored.
  517. */
  518. public boolean isSupported(String feature, String version) {
  519. return false;
  520. }
  521. /**
  522. * Returns <code>null</code>, since namespaces are not supported.
  523. */
  524. public String getNamespaceURI() throws DOMException {
  525. return null;
  526. }
  527. /**
  528. * Returns <code>null</code>, since namespaces are not supported.
  529. *
  530. * @return <code>null</code>.
  531. *
  532. * @see #setPrefix
  533. */
  534. public String getPrefix() {
  535. return null;
  536. }
  537. /**
  538. * Does nothing, since namespaces are not supported.
  539. *
  540. * @param prefix a <code>String</code>, which is ignored.
  541. *
  542. * @see #getPrefix
  543. */
  544. public void setPrefix(String prefix) {
  545. }
  546. /**
  547. * Equivalent to <code>getNodeName</code>.
  548. *
  549. * @return the node name, as a <code>String</code>.
  550. */
  551. public String getLocalName() {
  552. return nodeName;
  553. }
  554. // Methods from Element
  555. public String getTagName() {
  556. return nodeName;
  557. }
  558. public String getAttribute(String name) {
  559. Attr attr = getAttributeNode(name);
  560. if (attr == null) {
  561. return "";
  562. }
  563. return attr.getValue();
  564. }
  565. /**
  566. * Equivalent to <code>getAttribute(localName)</code>.
  567. *
  568. * @see #setAttributeNS
  569. */
  570. public String getAttributeNS(String namespaceURI, String localName) {
  571. return getAttribute(localName);
  572. }
  573. public void setAttribute(String name, String value) {
  574. // Note minor dependency on Crimson package
  575. // Steal the code if Crimson ever goes away
  576. if (!org.apache.crimson.util.XmlNames.isName(name)) {
  577. throw new IIODOMException(DOMException.INVALID_CHARACTER_ERR,
  578. "Attribute name is illegal!");
  579. }
  580. removeAttribute(name, false);
  581. attributes.add(new IIOAttr(this, name, value));
  582. }
  583. /**
  584. * Equivalent to <code>setAttribute(qualifiedName, value)</code>.
  585. *
  586. * @see #getAttributeNS
  587. */
  588. public void setAttributeNS(String namespaceURI,
  589. String qualifiedName, String value) {
  590. setAttribute(qualifiedName, value);
  591. }
  592. public void removeAttribute(String name) {
  593. removeAttribute(name, true);
  594. }
  595. private void removeAttribute(String name, boolean checkPresent) {
  596. int numAttributes = attributes.size();
  597. for (int i = 0; i < numAttributes; i++) {
  598. Attr attr = (Attr)attributes.get(i);
  599. if (name.equals(attr.getName())) {
  600. attributes.remove(i);
  601. return;
  602. }
  603. }
  604. // If we get here, the attribute doesn't exist
  605. if (checkPresent) {
  606. throw new IIODOMException(DOMException.NOT_FOUND_ERR,
  607. "No such attribute!");
  608. }
  609. }
  610. /**
  611. * Equivalent to <code>removeAttribute(localName)</code>.
  612. */
  613. public void removeAttributeNS(String namespaceURI,
  614. String localName) {
  615. removeAttribute(localName);
  616. }
  617. public Attr getAttributeNode(String name) {
  618. Node node = getAttributes().getNamedItem(name);
  619. return (Attr)node;
  620. }
  621. /**
  622. * Equivalent to <code>getAttributeNode(localName)</code>.
  623. *
  624. * @see #setAttributeNodeNS
  625. */
  626. public Attr getAttributeNodeNS(String namespaceURI,
  627. String localName) {
  628. return getAttributeNode(localName);
  629. }
  630. public Attr setAttributeNode(Attr newAttr) throws DOMException {
  631. IIOAttr attr;
  632. if (newAttr instanceof IIOAttr) {
  633. attr = (IIOAttr)newAttr;
  634. } else {
  635. attr = new IIOAttr(newAttr.getOwnerElement(),
  636. newAttr.getName(),
  637. newAttr.getValue());
  638. }
  639. attributes.add(attr);
  640. return attr;
  641. }
  642. /**
  643. * Equivalent to <code>setAttributeNode(newAttr)</code>.
  644. *
  645. * @see #getAttributeNodeNS
  646. */
  647. public Attr setAttributeNodeNS(Attr newAttr) {
  648. return setAttributeNode(newAttr);
  649. }
  650. public Attr removeAttributeNode(Attr oldAttr) {
  651. removeAttribute(oldAttr.getName());
  652. return oldAttr;
  653. }
  654. public NodeList getElementsByTagName(String name) {
  655. List l = new ArrayList();
  656. getElementsByTagName(name, l);
  657. return new IIONodeList(l);
  658. }
  659. private void getElementsByTagName(String name, List l) {
  660. if (nodeName.equals(name)) {
  661. l.add(this);
  662. }
  663. Node child = getFirstChild();
  664. while (child != null) {
  665. ((IIOMetadataNode)child).getElementsByTagName(name, l);
  666. child = child.getNextSibling();
  667. }
  668. }
  669. /**
  670. * Equivalent to <code>getElementsByTagName(localName)</code>.
  671. */
  672. public NodeList getElementsByTagNameNS(String namespaceURI,
  673. String localName) {
  674. return getElementsByTagName(localName);
  675. }
  676. public boolean hasAttributes() {
  677. return attributes.size() > 0;
  678. }
  679. public boolean hasAttribute(String name) {
  680. return getAttributeNode(name) != null;
  681. }
  682. /**
  683. * Equivalent to <code>hasAttribute(localName)</code>.
  684. */
  685. public boolean hasAttributeNS(String namespaceURI,
  686. String localName) {
  687. return hasAttribute(localName);
  688. }
  689. // Methods from NodeList
  690. public int getLength() {
  691. return numChildren;
  692. }
  693. public Node item(int index) {
  694. if (index < 0) {
  695. return null;
  696. }
  697. Node child = getFirstChild();
  698. while (child != null && index-- > 0) {
  699. child = child.getNextSibling();
  700. }
  701. return child;
  702. }
  703. /**
  704. * Returns the <code>Object</code> value associated with this node.
  705. *
  706. * @return the user <code>Object</code>.
  707. *
  708. * @see #setUserObject
  709. */
  710. public Object getUserObject() {
  711. return userObject;
  712. }
  713. /**
  714. * Sets the value associated with this node.
  715. *
  716. * @param userObject the user <code>Object</code>.
  717. *
  718. * @see #getUserObject
  719. */
  720. public void setUserObject(Object userObject) {
  721. this.userObject = userObject;
  722. }
  723. }