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