source code formatting.
[utils] / support / general / src / main / java / org / wamblee / xml / DomUtils.java
1 /*
2  * Copyright 2005 the original author or authors.
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 package org.wamblee.xml;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import org.apache.xml.serialize.OutputFormat;
22 import org.apache.xml.serialize.XMLSerializer;
23
24 import org.dom4j.DocumentException;
25
26 import org.dom4j.io.DOMReader;
27 import org.dom4j.io.DOMWriter;
28
29 import org.w3c.dom.Attr;
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.NamedNodeMap;
33 import org.w3c.dom.Node;
34 import org.w3c.dom.NodeList;
35
36 import org.xml.sax.SAXException;
37
38 import java.io.ByteArrayInputStream;
39 import java.io.ByteArrayOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.TreeMap;
48
49 import javax.xml.XMLConstants;
50 import javax.xml.parsers.DocumentBuilder;
51 import javax.xml.parsers.DocumentBuilderFactory;
52 import javax.xml.parsers.ParserConfigurationException;
53 import javax.xml.transform.stream.StreamSource;
54 import javax.xml.validation.Schema;
55 import javax.xml.validation.SchemaFactory;
56
57
58 /**
59  * Some basic XML utilities for common reoccuring tasks for DOM documents.
60  *
61  * @author Erik Brakkee
62  */
63 public final class DomUtils {
64     /**
65      * DOCUMENT ME!
66      */
67     private static final Log LOG = LogFactory.getLog(DomUtils.class);
68
69 /**
70      * Disabled default constructor.
71      *
72      */
73     private DomUtils() {
74         // Empty.
75     }
76
77     /**
78      * Parses an XML document from a string.
79      *
80      * @param aDocument document.
81      *
82      * @return
83      *
84      * @throws XMLException DOCUMENT ME!
85      */
86     public static Document read(String aDocument) throws XMLException {
87         ByteArrayInputStream is = new ByteArrayInputStream(aDocument.getBytes());
88
89         return read(is);
90     }
91
92     /**
93      * Parses an XML document from a stream.
94      *
95      * @param aIs Input stream.
96      *
97      * @return
98      *
99      * @throws XMLException DOCUMENT ME!
100      */
101     public static Document read(InputStream aIs) throws XMLException {
102         try {
103             DocumentBuilder builder = DocumentBuilderFactory.newInstance()
104                 .newDocumentBuilder();
105
106             return builder.parse(aIs);
107         } catch (SAXException e) {
108             throw new XMLException(e.getMessage(), e);
109         } catch (IOException e) {
110             throw new XMLException(e.getMessage(), e);
111         } catch (ParserConfigurationException e) {
112             throw new XMLException(e.getMessage(), e);
113         } finally {
114             try {
115                 aIs.close();
116             } catch (Exception e) {
117                 LOG.warn("Error closing XML file", e);
118             }
119         }
120     }
121
122     /**
123      * Reads and validates a document against a schema.
124      *
125      * @param aIs Input stream.
126      * @param aSchema Schema.
127      *
128      * @return Parsed and validated document.
129      *
130      * @throws XMLException DOCUMENT ME!
131      */
132     public static Document readAndValidate(InputStream aIs, InputStream aSchema)
133         throws XMLException {
134         try {
135             final Schema                 schema  = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
136                 .newSchema(new StreamSource(aSchema));
137
138             final DocumentBuilderFactory factory = DocumentBuilderFactory
139                 .newInstance();
140             factory.setValidating(true);
141             factory.setNamespaceAware(true);
142             factory.setSchema(schema);
143
144             return factory.newDocumentBuilder().parse(aIs);
145         } catch (SAXException e) {
146             throw new XMLException(e.getMessage(), e);
147         } catch (IOException e) {
148             throw new XMLException(e.getMessage(), e);
149         } catch (ParserConfigurationException e) {
150             throw new XMLException(e.getMessage(), e);
151         } finally {
152             try {
153                 aSchema.close();
154             } catch (Exception e) {
155                 LOG.warn("Error closing schema", e);
156             }
157
158             try {
159                 aIs.close();
160             } catch (Exception e) {
161                 LOG.warn("Error closing XML file", e);
162             }
163         }
164     }
165
166     /**
167      * Serializes an XML document to a stream.
168      *
169      * @param aDocument Document to serialize.
170      * @param aOs Output stream.
171      *
172      * @throws IOException DOCUMENT ME!
173      */
174     public static void serialize(Document aDocument, OutputStream aOs)
175         throws IOException {
176         XMLSerializer serializer = new XMLSerializer(aOs, new OutputFormat());
177         serializer.serialize(aDocument);
178     }
179
180     /**
181      * Serializes an XML document.
182      *
183      * @param aDocument Document to serialize.
184      *
185      * @return Serialized document.
186      *
187      * @throws IOException DOCUMENT ME!
188      */
189     public static String serialize(Document aDocument)
190         throws IOException {
191         ByteArrayOutputStream os = new ByteArrayOutputStream();
192         serialize(aDocument, os);
193
194         return os.toString();
195     }
196
197     /**
198      * Converts a dom4j document into a w3c DOM document.
199      *
200      * @param aDocument Document to convert.
201      *
202      * @return W3C DOM document.
203      *
204      * @throws DocumentException DOCUMENT ME!
205      */
206     public static Document convert(org.dom4j.Document aDocument)
207         throws DocumentException {
208         return new DOMWriter().write(aDocument);
209     }
210
211     /**
212      * Converts a W3C DOM document into a dom4j document.
213      *
214      * @param aDocument Document to convert.
215      *
216      * @return Dom4j document.
217      */
218     public static org.dom4j.Document convert(Document aDocument) {
219         return new DOMReader().read(aDocument);
220     }
221
222     /**
223      * Removes duplicate attributes from a DOM tree.This is useful for
224      * postprocessing the output of JTidy as a workaround for a bug in JTidy.
225      *
226      * @param aNode Node to remove duplicate attributes from (recursively).
227      *        Attributes of the node itself are not dealt with. Only the child
228      *        nodes are dealt with.
229      */
230     public static void removeDuplicateAttributes(Node aNode) {
231         NodeList list = aNode.getChildNodes();
232
233         for (int i = 0; i < list.getLength(); i++) {
234             Node node = list.item(i);
235
236             if (node instanceof Element) {
237                 removeDuplicateAttributes((Element) node);
238                 removeDuplicateAttributes(node);
239             }
240         }
241     }
242
243     /**
244      * Removes duplicate attributes from an element.
245      *
246      * @param aElement Element.
247      */
248     private static void removeDuplicateAttributes(Element aElement) {
249         NamedNodeMap      attributes       = aElement.getAttributes();
250         Map<String, Attr> uniqueAttributes = new TreeMap<String, Attr>();
251         List<Attr>        attlist          = new ArrayList<Attr>();
252
253         for (int i = 0; i < attributes.getLength(); i++) {
254             Attr attribute = (Attr) attributes.item(i);
255
256             if (uniqueAttributes.containsKey(attribute.getNodeName())) {
257                 LOG.info("Detected duplicate attribute (will be removed)'"
258                     + attribute.getNodeName() + "'");
259             }
260
261             uniqueAttributes.put(attribute.getNodeName(), attribute);
262             attlist.add(attribute);
263         }
264
265         // Remove all attributes from the element.
266         for (Attr att : attlist) {
267             aElement.removeAttributeNode(att);
268         }
269
270         // Add the unique attributes back to the element.
271         for (Attr att : uniqueAttributes.values()) {
272             aElement.setAttributeNode(att);
273         }
274     }
275 }