83515816d5c7f5906133b9bd6f5362a0a89c09b0
[jSite.git] / src / main / java / de / todesbaum / util / xml / SimpleXML.java
1 /*
2  * jSite - SimpleXML.java - Copyright © 2006–2012 David Roden
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18
19 package de.todesbaum.util.xml;
20
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import javax.xml.parsers.DocumentBuilder;
25 import javax.xml.parsers.DocumentBuilderFactory;
26 import javax.xml.parsers.ParserConfigurationException;
27
28 import org.w3c.dom.Document;
29 import org.w3c.dom.Element;
30 import org.w3c.dom.Node;
31 import org.w3c.dom.NodeList;
32 import org.w3c.dom.Text;
33
34 /**
35  * SimpleXML is a helper class to construct XML trees in a fast and simple way.
36  * Construct a new XML tree by calling {@link #SimpleXML(String)} and append new
37  * nodes by calling {@link #append(String)}.
38  *
39  * @author David Roden <droden@gmail.com>
40  * @version $Id:SimpleXML.java 221 2006-03-06 14:46:49Z bombe $
41  */
42 public class SimpleXML {
43
44         /**
45          * A {@link List} containing all child nodes of this node.
46          */
47         private List<SimpleXML> children = new ArrayList<SimpleXML>();
48
49         /**
50          * The name of this node.
51          */
52         private String name = null;
53
54         /**
55          * The value of this node.
56          */
57         private String value = null;
58
59         /**
60          * Constructs a new XML node without a name.
61          */
62         public SimpleXML() {
63                 super();
64         }
65
66         /**
67          * Constructs a new XML node with the specified name.
68          *
69          * @param name
70          *            The name of the new node
71          */
72         public SimpleXML(String name) {
73                 this.name = name;
74         }
75
76         /**
77          * Returns the child node of this node with the specified name. If there are
78          * several child nodes with the specified name only the first node is
79          * returned.
80          *
81          * @param nodeName
82          *            The name of the child node
83          * @return The child node, or <code>null</code> if there is no child node
84          *         with the specified name
85          */
86         public SimpleXML getNode(String nodeName) {
87                 for (int index = 0, count = children.size(); index < count; index++) {
88                         if (children.get(index).name.equals(nodeName)) {
89                                 return children.get(index);
90                         }
91                 }
92                 return null;
93         }
94
95         /**
96          * Returns the child node that is specified by the names. The first element
97          * of <code>nodeNames</code> is the name of the child node of this node, the
98          * second element of <code>nodeNames</code> is the name of a child node's
99          * child node, and so on. By using this method you can descend into an XML
100          * tree pretty fast.
101          *
102          * <pre>
103          *
104          * SimpleXML deepNode = topNode.getNodes(new String[] { &quot;person&quot;, &quot;address&quot;, &quot;number&quot; });
105          * </pre>
106          *
107          * @param nodeNames
108          * @return A node that is a deep child of this node, or <code>null</code> if
109          *         the specified node does not eixst
110          */
111         public SimpleXML getNode(String[] nodeNames) {
112                 SimpleXML node = this;
113                 for (String nodeName : nodeNames) {
114                         node = node.getNode(nodeName);
115                 }
116                 return node;
117         }
118
119         /**
120          * Returns all child nodes of this node.
121          *
122          * @return All child nodes of this node
123          */
124         public SimpleXML[] getNodes() {
125                 return getNodes(null);
126         }
127
128         /**
129          * Returns all child nodes of this node with the specified name. If there
130          * are no child nodes with the specified name an empty array is returned.
131          *
132          * @param nodeName
133          *            The name of the nodes to retrieve, or <code>null</code> to
134          *            retrieve all nodes
135          * @return All child nodes with the specified name
136          */
137         public SimpleXML[] getNodes(String nodeName) {
138                 List<SimpleXML> resultList = new ArrayList<SimpleXML>();
139                 for (SimpleXML child : children) {
140                         if ((nodeName == null) || child.name.equals(nodeName)) {
141                                 resultList.add(child);
142                         }
143                 }
144                 return resultList.toArray(new SimpleXML[resultList.size()]);
145         }
146
147         /**
148          * Appends a new XML node with the specified name and returns the new node.
149          * With this method you can create deep structures very fast.
150          *
151          * <pre>
152          *
153          * SimpleXML mouseNode = topNode.append(&quot;computer&quot;).append(&quot;bus&quot;).append(&quot;usb&quot;).append(&quot;mouse&quot;);
154          * </pre>
155          *
156          * @param nodeName
157          *            The name of the node to append as a child to this node
158          * @return The new node
159          */
160         public SimpleXML append(String nodeName) {
161                 return append(new SimpleXML(nodeName));
162         }
163
164         /**
165          * Appends a new XML node with the specified name and value and returns the
166          * new node.
167          *
168          * @param nodeName
169          *            The name of the node to append
170          * @param nodeValue
171          *            The value of the node to append
172          * @return The newly appended node
173          */
174         public SimpleXML append(String nodeName, String nodeValue) {
175                 return append(nodeName).setValue(nodeValue);
176         }
177
178         /**
179          * Appends the node with all its child nodes to this node and returns the
180          * child node.
181          *
182          * @param newChild
183          *            The node to append as a child
184          * @return The child node that was appended
185          */
186         public SimpleXML append(SimpleXML newChild) {
187                 children.add(newChild);
188                 return newChild;
189         }
190
191         public void remove(SimpleXML child) {
192                 children.remove(child);
193         }
194
195         public void remove(String childName) {
196                 SimpleXML child = getNode(childName);
197                 if (child != null) {
198                         remove(child);
199                 }
200         }
201
202         public void replace(String childName, String value) {
203                 remove(childName);
204                 append(childName, value);
205         }
206
207         public void replace(SimpleXML childNode) {
208                 remove(childNode.getName());
209                 append(childNode);
210         }
211
212         public void removeAll() {
213                 children.clear();
214         }
215
216         /**
217          * Sets the value of this node.
218          *
219          * @param nodeValue
220          *            The new value of this node
221          * @return This node
222          */
223         public SimpleXML setValue(String nodeValue) {
224                 value = nodeValue;
225                 return this;
226         }
227
228         /**
229          * Returns the name of this node.
230          *
231          * @return The name of this node
232          */
233         public String getName() {
234                 return name;
235         }
236
237         /**
238          * Returns the value of this node.
239          *
240          * @return The value of this node
241          */
242         public String getValue() {
243                 return value;
244         }
245
246         /**
247          * Returns the value of this node. If the node does not have a value, the
248          * given default value is returned.
249          *
250          *@param defaultValue
251          *            The default value to return if the node does not have a value
252          * @return The value of this node
253          */
254         public String getValue(String defaultValue) {
255                 return (value == null) ? defaultValue : value;
256         }
257
258         /**
259          * Creates a {@link Document} from this node and all its child nodes.
260          *
261          * @return The {@link Document} created from this node
262          */
263         public Document getDocument() {
264                 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
265                 try {
266                         DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
267                         Document document = documentBuilder.newDocument();
268                         Element rootElement = document.createElement(name);
269                         document.appendChild(rootElement);
270                         addChildren(rootElement);
271                         return document;
272                 } catch (ParserConfigurationException e) {
273                 }
274                 return null;
275         }
276
277         /**
278          * Appends all children of this node to the specified {@link Element}. If a
279          * node has a value that is not <code>null</code> the value is appended as a
280          * text node.
281          *
282          * @param rootElement
283          *            The element to attach this node's children to
284          */
285         private void addChildren(Element rootElement) {
286                 for (SimpleXML child : children) {
287                         Element childElement = rootElement.getOwnerDocument().createElement(child.name);
288                         rootElement.appendChild(childElement);
289                         if (child.value != null) {
290                                 Text childText = rootElement.getOwnerDocument().createTextNode(child.value);
291                                 childElement.appendChild(childText);
292                         } else {
293                                 child.addChildren(childElement);
294                         }
295                 }
296         }
297
298         /**
299          * Creates a SimpleXML node from the specified {@link Document}. The
300          * SimpleXML node of the document's top-level node is returned.
301          *
302          * @param document
303          *            The {@link Document} to create a SimpleXML node from
304          * @return The SimpleXML node created from the document's top-level node
305          */
306         public static SimpleXML fromDocument(Document document) {
307                 SimpleXML xmlDocument = new SimpleXML(document.getFirstChild().getNodeName());
308                 document.normalizeDocument();
309                 return addDocumentChildren(xmlDocument, document.getFirstChild());
310         }
311
312         /**
313          * Appends the child nodes of the specified {@link Document} to this node.
314          * Text nodes are converted into a node's value.
315          *
316          * @param xmlDocument
317          *            The SimpleXML node to append the child nodes to
318          * @param document
319          *            The document whose child nodes to append
320          * @return The SimpleXML node the child nodes were appended to
321          */
322         private static SimpleXML addDocumentChildren(SimpleXML xmlDocument, Node document) {
323                 NodeList childNodes = document.getChildNodes();
324                 for (int childIndex = 0, childCount = childNodes.getLength(); childIndex < childCount; childIndex++) {
325                         Node childNode = childNodes.item(childIndex);
326                         if ((childNode.getChildNodes().getLength() == 1) && (childNode.getFirstChild().getNodeName().equals("#text"))) {
327                                 xmlDocument.append(childNode.getNodeName(), childNode.getFirstChild().getNodeValue());
328                         } else {
329                                 if (!childNode.getNodeName().equals("#text") || (childNode.getChildNodes().getLength() != 0)) {
330                                         SimpleXML newXML = xmlDocument.append(childNode.getNodeName());
331                                         addDocumentChildren(newXML, childNode);
332                                 }
333                         }
334                 }
335                 return xmlDocument;
336         }
337
338 }