1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 }