View Javadoc

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.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; // conforming to NodeList interface
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             // this should never happen
189             throw new IllegalStateException("Invalid node type: " + child.getNodeType());
190         }
191 
192         // okay we have a copy of the child, now the child becomes the parent
193         // and we are iterating recursively over its children.
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         // bingo append it. (this should normally not be done here)
209         parent.appendChild(copy);
210         return copy;
211     }
212 }