View Javadoc

1   /*
2    * Copyright 2004-2005 The Apache Software Foundation or its licensors,
3    *                     as applicable.
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package net.sf.exorcist.midgard;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Properties;
22  
23  import javax.xml.parsers.DocumentBuilder;
24  import javax.xml.parsers.DocumentBuilderFactory;
25  
26  import org.w3c.dom.Attr;
27  import org.w3c.dom.CharacterData;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.NamedNodeMap;
31  import org.w3c.dom.Node;
32  import org.w3c.dom.NodeList;
33  
34  /***
35   * Document walker class. This class provides an intuitive
36   * interface for traversing a parsed DOM document.
37   */
38  public final class DOMWalker {
39  
40      /*** Static factory for creating stream to DOM transformers. */
41      private static final DocumentBuilderFactory factory =
42          DocumentBuilderFactory.newInstance();
43  
44      private final String namespace;
45  
46      /*** The DOM document being traversed by this walker. */
47      private final Document document;
48  
49      /*** The current element. */
50      private Element current;
51  
52      /***
53       * Creates a walker for traversing a DOM document read from the given
54       * input stream. The root element of the document is set as the current
55       * element.
56       *
57       * @param xml XML input stream
58       * @throws IOException if a document cannot be read from the stream
59       */
60      public DOMWalker(InputStream xml, String namespace) throws IOException {
61          this.namespace = namespace;
62          try {
63              factory.setNamespaceAware(true);
64              DocumentBuilder builder = factory.newDocumentBuilder();
65              document = builder.parse(xml);
66              current = document.getDocumentElement();
67          } catch (IOException e) {
68              throw e;
69          } catch (Exception e) {
70              throw (IOException)new IOException(e.getMessage()).initCause(e);
71          }
72      }
73  
74      public Object getCurrent() {
75          return current;
76      }
77  
78      /***
79       * Returns the namespace mappings defined in the current element.
80       * The returned property set contains the prefix to namespace
81       * mappings specified by the <code>xmlns</code> attributes of the
82       * current element.
83       *
84       * @return prefix to namespace mappings of the current element
85       */
86      public Properties getNamespaces() {
87          Properties namespaces = new Properties();
88          NamedNodeMap attributes = current.getAttributes();
89          for (int i = 0; i < attributes.getLength(); i++) {
90              Attr attribute = (Attr) attributes.item(i);
91              if (attribute.getName().startsWith("xmlns:")) {
92                  namespaces.setProperty(
93                          attribute.getName().substring(6), attribute.getValue());
94              }
95          }
96          return namespaces;
97      }
98  
99      /***
100      * Returns the name of the current element.
101      *
102      * @return element name
103      */
104     public String getName() {
105         return current.getLocalName();
106     }
107 
108     /***
109      * Returns the value of the named attribute of the current element.
110      *
111      * @param name attribute name
112      * @return attribute value, or <code>null</code> if not found
113      */
114     public String getAttribute(String name) {
115         Attr attribute = current.getAttributeNodeNS(namespace, name);
116         if (attribute != null) {
117             return attribute.getValue();
118         } else {
119             return null;
120         }
121     }
122 
123     /***
124      * Returns the text content of the current element.
125      *
126      * @return text content
127      */
128     public String getContent() {
129         StringBuffer content = new StringBuffer();
130 
131         NodeList nodes = current.getChildNodes();
132         for (int i = 0; i < nodes.getLength(); i++) {
133             Node node = nodes.item(i);
134             if (node.getNodeType() == Node.TEXT_NODE) {
135                 content.append(((CharacterData) node).getData());
136             }
137         }
138 
139         return content.toString();
140     }
141 
142     public String getContent(String name) {
143         String content = "";
144         if (enterElement(name)) {
145             content = getContent();
146             leaveElement();
147         }
148         return content;
149     }
150 
151     /***
152      * Enters the named child element. If the named child element is
153      * found, then it is made the current element and <code>true</code>
154      * is returned. Otherwise the current element is not changed and
155      * <code>false</code> is returned.
156      * <p>
157      * The standard call sequence for this method is show below.
158      * <pre>
159      *     DOMWalker walker = ...;
160      *     if (walker.enterElement("...")) {
161      *         ...;
162      *         walker.leaveElement();
163      *     }
164      * </pre>
165      *
166      * @param name child element name
167      * @return <code>true</code> if the element was entered,
168      *         <code>false</code> otherwise
169      */
170     public boolean enterElement(String name) {
171         NodeList children = current.getChildNodes();
172         for (int i = 0; i < children.getLength(); i++) {
173             Node child = children.item(i);
174             if (child.getNodeType() == Node.ELEMENT_NODE
175                     && namespace.equals(child.getNamespaceURI())
176                     && name.equals(child.getLocalName())) {
177                 current = (Element) child;
178                 return true;
179             }
180         }
181         return false;
182     }
183 
184     /***
185      * Leaves the current element. The parent element is set as the new
186      * current element.
187      *
188      * @see #enterElement(String)
189      */
190     public void leaveElement() {
191         current = (Element) current.getParentNode();
192     }
193 
194     /***
195      * Iterates through the named child elements over multiple calls.
196      * This method makes it possible to use the following code to
197      * walk through all the child elements with the given name.
198      * <pre>
199      *     DOMWalker walker = ...;
200      *     while (walker.iterateElements("...")) {
201      *         ...;
202      *     }
203      * </pre>
204      *
205      * @param name name of the iterated elements
206      * @return <code>true</code> if another iterated element was entered, or
207      *         <code>false</code> if no more iterated elements were found
208      *         and the original element is restored as the current element
209      */
210     public boolean iterateElements(String name, Object context) {
211         Node next;
212         if (current == context) {
213             next = current.getFirstChild();
214         } else {
215             next = current.getNextSibling();
216         }
217 
218         while (next != null) {
219             if (next.getNodeType() == Node.ELEMENT_NODE
220                     && namespace.equals(next.getNamespaceURI())
221                     && name.equals(next.getLocalName())) {
222                 current = (Element) next;
223                 return true;
224             } else {
225                 next = next.getNextSibling();
226             }
227         }
228 
229         if (current != context) {
230             current = (Element) current.getParentNode();
231         }
232         return false;
233     }
234 
235 }