1. /*
  2. * Copyright 2001-2002,2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.junit;
  18. import java.util.Vector;
  19. import org.w3c.dom.Attr;
  20. import org.w3c.dom.CDATASection;
  21. import org.w3c.dom.Comment;
  22. import org.w3c.dom.DOMException;
  23. import org.w3c.dom.Document;
  24. import org.w3c.dom.Element;
  25. import org.w3c.dom.NamedNodeMap;
  26. import org.w3c.dom.Node;
  27. import org.w3c.dom.NodeList;
  28. import org.w3c.dom.ProcessingInstruction;
  29. import org.w3c.dom.Text;
  30. /**
  31. * Some utilities that might be useful when manipulating DOM trees.
  32. *
  33. */
  34. public final class DOMUtil {
  35. /** unused constructor */
  36. private DOMUtil() {
  37. }
  38. /**
  39. * Filter interface to be applied when iterating over a DOM tree.
  40. * Just think of it like a <tt>FileFilter</tt> clone.
  41. */
  42. public interface NodeFilter {
  43. /**
  44. * @param node the node to check for acceptance.
  45. * @return <tt>true</tt> if the node is accepted by this filter,
  46. * otherwise <tt>false</tt>
  47. */
  48. boolean accept(Node node);
  49. }
  50. /**
  51. * list a set of node that match a specific filter. The list can be made
  52. * recursively or not.
  53. * @param parent the parent node to search from
  54. * @param filter the filter that children should match.
  55. * @param recurse <tt>true</tt> if you want the list to be made recursively
  56. * otherwise <tt>false</tt>.
  57. */
  58. public static NodeList listChildNodes(Node parent, NodeFilter filter, boolean recurse) {
  59. NodeListImpl matches = new NodeListImpl();
  60. NodeList children = parent.getChildNodes();
  61. if (children != null) {
  62. final int len = children.getLength();
  63. for (int i = 0; i < len; i++) {
  64. Node child = children.item(i);
  65. if (filter.accept(child)) {
  66. matches.addElement(child);
  67. }
  68. if (recurse) {
  69. NodeList recmatches = listChildNodes(child, filter, recurse);
  70. final int reclength = matches.getLength();
  71. for (int j = 0; j < reclength; j++) {
  72. matches.addElement(recmatches.item(i));
  73. }
  74. }
  75. }
  76. }
  77. return matches;
  78. }
  79. /** custom implementation of a nodelist */
  80. public static class NodeListImpl extends Vector implements NodeList {
  81. public int getLength() {
  82. return size();
  83. }
  84. public Node item(int i) {
  85. try {
  86. return (Node) elementAt(i);
  87. } catch (ArrayIndexOutOfBoundsException e) {
  88. return null; // conforming to NodeList interface
  89. }
  90. }
  91. }
  92. /**
  93. * return the attribute value of an element.
  94. * @param node the node to get the attribute from.
  95. * @param name the name of the attribute we are looking for the value.
  96. * @return the value of the requested attribute or <tt>null</tt> if the
  97. * attribute was not found or if <tt>node</tt> is not an <tt>Element</tt>.
  98. */
  99. public static String getNodeAttribute(Node node, String name) {
  100. if (node instanceof Element) {
  101. Element element = (Element) node;
  102. return element.getAttribute(name);
  103. }
  104. return null;
  105. }
  106. /**
  107. * Iterate over the children of a given node and return the first node
  108. * that has a specific name.
  109. * @param parent the node to search child from. Can be <tt>null</tt>.
  110. * @param tagname the child name we are looking for. Cannot be <tt>null</tt>.
  111. * @return the first child that matches the given name or <tt>null</tt> if
  112. * the parent is <tt>null</tt> or if a child does not match the
  113. * given name.
  114. */
  115. public static Element getChildByTagName (Node parent, String tagname) {
  116. if (parent == null) {
  117. return null;
  118. }
  119. NodeList childList = parent.getChildNodes();
  120. final int len = childList.getLength();
  121. for (int i = 0; i < len; i++) {
  122. Node child = childList.item(i);
  123. if (child != null && child.getNodeType() == Node.ELEMENT_NODE
  124. && child.getNodeName().equals(tagname)) {
  125. return (Element) child;
  126. }
  127. }
  128. return null;
  129. }
  130. /**
  131. * Simple tree walker that will clone recursively a node. This is to
  132. * avoid using parser-specific API such as Sun's <tt>changeNodeOwner</tt>
  133. * when we are dealing with DOM L1 implementations since <tt>cloneNode(boolean)</tt>
  134. * will not change the owner document.
  135. * <tt>changeNodeOwner</tt> is much faster and avoid the costly cloning process.
  136. * <tt>importNode</tt> is in the DOM L2 interface.
  137. * @param parent the node parent to which we should do the import to.
  138. * @param child the node to clone recursively. Its clone will be
  139. * appended to <tt>parent</tt>.
  140. * @return the cloned node that is appended to <tt>parent</tt>
  141. */
  142. public static final Node importNode(Node parent, Node child) {
  143. Node copy = null;
  144. final Document doc = parent.getOwnerDocument();
  145. switch (child.getNodeType()) {
  146. case Node.CDATA_SECTION_NODE:
  147. copy = doc.createCDATASection(((CDATASection) child).getData());
  148. break;
  149. case Node.COMMENT_NODE:
  150. copy = doc.createComment(((Comment) child).getData());
  151. break;
  152. case Node.DOCUMENT_FRAGMENT_NODE:
  153. copy = doc.createDocumentFragment();
  154. break;
  155. case Node.ELEMENT_NODE:
  156. final Element elem = doc.createElement(((Element) child).getTagName());
  157. copy = elem;
  158. final NamedNodeMap attributes = child.getAttributes();
  159. if (attributes != null) {
  160. final int size = attributes.getLength();
  161. for (int i = 0; i < size; i++) {
  162. final Attr attr = (Attr) attributes.item(i);
  163. elem.setAttribute(attr.getName(), attr.getValue());
  164. }
  165. }
  166. break;
  167. case Node.ENTITY_REFERENCE_NODE:
  168. copy = doc.createEntityReference(child.getNodeName());
  169. break;
  170. case Node.PROCESSING_INSTRUCTION_NODE:
  171. final ProcessingInstruction pi = (ProcessingInstruction) child;
  172. copy = doc.createProcessingInstruction(pi.getTarget(), pi.getData());
  173. break;
  174. case Node.TEXT_NODE:
  175. copy = doc.createTextNode(((Text) child).getData());
  176. break;
  177. default:
  178. // this should never happen
  179. throw new IllegalStateException("Invalid node type: " + child.getNodeType());
  180. }
  181. // okay we have a copy of the child, now the child becomes the parent
  182. // and we are iterating recursively over its children.
  183. try {
  184. final NodeList children = child.getChildNodes();
  185. if (children != null) {
  186. final int size = children.getLength();
  187. for (int i = 0; i < size; i++) {
  188. final Node newChild = children.item(i);
  189. if (newChild != null) {
  190. importNode(copy, newChild);
  191. }
  192. }
  193. }
  194. } catch (DOMException ignored) {
  195. }
  196. // bingo append it. (this should normally not be done here)
  197. parent.appendChild(copy);
  198. return copy;
  199. }
  200. }