05eae128640fa9b70a829a7e7c33814e379ca891
[utils] / support / src / 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
17 package org.wamblee.xml;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.TreeMap;
28
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.xml.serialize.OutputFormat;
36 import org.apache.xml.serialize.XMLSerializer;
37 import org.dom4j.DocumentException;
38 import org.dom4j.io.DOMReader;
39 import org.dom4j.io.DOMWriter;
40 import org.w3c.dom.Attr;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.NamedNodeMap;
44 import org.w3c.dom.Node;
45 import org.w3c.dom.NodeList;
46 import org.xml.sax.SAXException;
47
48 /**
49  * Some basic XML utilities for common reoccuring tasks for 
50  * DOM documents.  
51  */
52 public final class DomUtils {
53     
54     private static final Log LOG = LogFactory.getLog(DomUtils.class);
55     
56     /**
57      * Disabled default constructor. 
58      *
59      */
60     private DomUtils() { 
61         // Empty. 
62     }
63     
64     /**
65      * Parses an XML document from a string. 
66      * @param aDocument document. 
67      * @return 
68      */
69     public static Document read(String aDocument) throws SAXException, ParserConfigurationException, IOException {
70         ByteArrayInputStream is = new ByteArrayInputStream(aDocument.getBytes());
71         return read(is);          
72     }
73     
74     /**
75      * Parses an XML document from a stream. 
76      * @param aIs Input stream. 
77      * @return
78      */
79     public static Document read(InputStream aIs) throws SAXException, ParserConfigurationException, IOException { 
80         DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 
81         return builder.parse(aIs);     
82     }
83
84     /**
85      * Serializes an XML document to a stream. 
86      * @param aDocument Document to serialize. 
87      * @param aOs Output stream. 
88      */
89     public static void serialize(Document aDocument, OutputStream aOs) throws IOException { 
90         XMLSerializer serializer = new XMLSerializer(aOs, new OutputFormat());
91         serializer.serialize(aDocument);
92     }
93     
94     /**
95      * Serializes an XML document. 
96      * @param aDocument Document to serialize. 
97      * @return Serialized document. 
98      */
99     public static String serialize(Document aDocument) throws IOException {
100         ByteArrayOutputStream os = new ByteArrayOutputStream();
101         serialize(aDocument, os); 
102         return os.toString(); 
103     }
104     
105     /**
106      * Converts a dom4j document into a w3c DOM document. 
107      * @param aDocument Document to convert. 
108      * @return W3C DOM document. 
109      */
110     public static Document convert(org.dom4j.Document aDocument) throws DocumentException {
111         return new DOMWriter().write(aDocument);
112     }
113
114     /**
115      * Converts a W3C DOM document into a dom4j document. 
116      * @param aDocument Document to convert. 
117      * @return Dom4j document.
118      */
119     public static org.dom4j.Document convert(Document aDocument) { 
120         return new DOMReader().read(aDocument); 
121     }
122     
123     /**
124      * Removes duplicate attributes from a DOM tree.This is useful for postprocessing the
125      * output of JTidy as a workaround for a bug in JTidy. 
126      * 
127      * @param aNode
128      *            Node to remove duplicate attributes from (recursively).
129      *            Attributes of the node itself are not dealt with. Only the
130      *            child nodes are dealt with.
131      */
132     public static void removeDuplicateAttributes(Node aNode) {
133         NodeList list = aNode.getChildNodes();
134         for (int i = 0; i < list.getLength(); i++) {
135             Node node = list.item(i);
136             if (node instanceof Element) {
137                 removeDuplicateAttributes((Element) node);
138                 removeDuplicateAttributes(node);
139             }
140         }
141     }
142
143     /**
144      * Removes duplicate attributes from an element.
145      * 
146      * @param aElement
147      *            Element.
148      */
149     private static void removeDuplicateAttributes(Element aElement) {
150         NamedNodeMap attributes = aElement.getAttributes();
151         Map<String, Attr> uniqueAttributes = new TreeMap<String, Attr>();
152         List<Attr> attlist = new ArrayList<Attr>();
153         for (int i = 0; i < attributes.getLength(); i++) {
154             Attr attribute = (Attr) attributes.item(i);
155             if (uniqueAttributes.containsKey(attribute.getNodeName())) {
156                 LOG.info("Detected duplicate attribute (will be removed)'"
157                         + attribute.getNodeName() + "'");
158             }
159             uniqueAttributes.put(attribute.getNodeName(), attribute);
160             attlist.add(attribute);
161         }
162         // Remove all attributes from the element.
163         for (Attr att : attlist) {
164             aElement.removeAttributeNode(att);
165         }
166         // Add the unique attributes back to the element.
167         for (Attr att : uniqueAttributes.values()) {
168             aElement.setAttributeNode(att);
169         }
170     }
171 }