001 package ezvcard.util;
002
003 import java.io.IOException;
004 import java.io.InputStream;
005 import java.io.Reader;
006 import java.io.StringReader;
007 import java.io.StringWriter;
008 import java.io.Writer;
009 import java.util.ArrayList;
010 import java.util.HashMap;
011 import java.util.List;
012 import java.util.Map;
013
014 import javax.xml.parsers.DocumentBuilder;
015 import javax.xml.parsers.DocumentBuilderFactory;
016 import javax.xml.parsers.ParserConfigurationException;
017 import javax.xml.transform.Transformer;
018 import javax.xml.transform.TransformerConfigurationException;
019 import javax.xml.transform.TransformerException;
020 import javax.xml.transform.TransformerFactory;
021 import javax.xml.transform.TransformerFactoryConfigurationError;
022 import javax.xml.transform.dom.DOMSource;
023 import javax.xml.transform.stream.StreamResult;
024
025 import org.w3c.dom.Document;
026 import org.w3c.dom.Element;
027 import org.w3c.dom.Node;
028 import org.w3c.dom.NodeList;
029 import org.xml.sax.InputSource;
030 import org.xml.sax.SAXException;
031
032 /*
033 Copyright (c) 2013, Michael Angstadt
034 All rights reserved.
035
036 Redistribution and use in source and binary forms, with or without
037 modification, are permitted provided that the following conditions are met:
038
039 1. Redistributions of source code must retain the above copyright notice, this
040 list of conditions and the following disclaimer.
041 2. Redistributions in binary form must reproduce the above copyright notice,
042 this list of conditions and the following disclaimer in the documentation
043 and/or other materials provided with the distribution.
044
045 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
046 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
047 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
048 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
049 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
050 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
051 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
052 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
053 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
054 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
055
056 The views and conclusions contained in the software and documentation are those
057 of the authors and should not be interpreted as representing official policies,
058 either expressed or implied, of the FreeBSD Project.
059 */
060
061 /**
062 * Generic XML utility methods.
063 * @author Michael Angstadt
064 */
065 public class XmlUtils {
066 /**
067 * Creates a new XML document.
068 * @return the XML document
069 */
070 public static Document createDocument() {
071 try {
072 DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
073 fact.setNamespaceAware(true);
074 DocumentBuilder db = fact.newDocumentBuilder();
075 return db.newDocument();
076 } catch (ParserConfigurationException e) {
077 //will probably never be thrown because we're not doing anything fancy with the configuration
078 throw new RuntimeException(e);
079 }
080 }
081
082 /**
083 * Parses an XML string into a DOM.
084 * @param xml the XML string
085 * @return the parsed DOM
086 * @throws SAXException if the string is not valid XML
087 */
088 public static Document toDocument(String xml) throws SAXException {
089 try {
090 return toDocument(new StringReader(xml));
091 } catch (IOException e) {
092 //reading from string
093 throw new RuntimeException(e);
094 }
095 }
096
097 /**
098 * Parses an XML document from an input stream.
099 * @param in the input stream
100 * @return the parsed DOM
101 * @throws SAXException if the XML is not valid
102 * @throws IOException if there is a problem reading from the input stream
103 */
104 public static Document toDocument(InputStream in) throws SAXException, IOException {
105 return toDocument(new InputSource(in));
106 }
107
108 /**
109 * <p>
110 * Parses an XML document from a reader.
111 * </p>
112 * <p>
113 * Note that use of this method is discouraged. It ignores the character
114 * encoding that is defined within the XML document itself, and should only
115 * be used if the encoding is undefined or if the encoding needs to be
116 * ignored for whatever reason. The {@link #toDocument(InputStream)} method
117 * should be used instead, since it takes the XML document's character
118 * encoding into account when parsing.
119 * </p>
120 * @param reader the reader
121 * @return the parsed DOM
122 * @throws SAXException if the XML is not valid
123 * @throws IOException if there is a problem reading from the reader
124 * @see <a
125 * href="http://stackoverflow.com/q/3482494/13379">http://stackoverflow.com/q/3482494/13379</a>
126 */
127 public static Document toDocument(Reader reader) throws SAXException, IOException {
128 return toDocument(new InputSource(reader));
129 }
130
131 private static Document toDocument(InputSource in) throws SAXException, IOException {
132 try {
133 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
134 dbf.setNamespaceAware(true);
135 dbf.setIgnoringComments(true);
136 DocumentBuilder db = dbf.newDocumentBuilder();
137 return db.parse(in);
138 } catch (ParserConfigurationException e) {
139 //will probably never be thrown because we're not doing anything fancy with the configuration
140 throw new RuntimeException(e);
141 }
142 }
143
144 /**
145 * Converts an XML node to a string.
146 * @param node the XML node
147 * @return the string
148 */
149 public static String toString(Node node) {
150 return toString(node, new HashMap<String, String>());
151 }
152
153 /**
154 * Converts an XML node to a string.
155 * @param node the XML node
156 * @param outputProperties the output properties
157 * @return the string
158 */
159 public static String toString(Node node, Map<String, String> outputProperties) {
160 try {
161 StringWriter writer = new StringWriter();
162 toWriter(node, writer, outputProperties);
163 return writer.toString();
164 } catch (TransformerException e) {
165 //should never be thrown because we're writing to a string
166 throw new RuntimeException(e);
167 }
168 }
169
170 /**
171 * Writes an XML node to a writer.
172 * @param node the XML node
173 * @param writer the writer
174 * @throws TransformerException if there's a problem writing to the writer
175 */
176 public static void toWriter(Node node, Writer writer) throws TransformerException {
177 toWriter(node, writer, new HashMap<String, String>());
178 }
179
180 /**
181 * Writes an XML node to a writer.
182 * @param node the XML node
183 * @param writer the writer
184 * @param outputProperties the output properties
185 * @throws TransformerException if there's a problem writing to the writer
186 */
187 public static void toWriter(Node node, Writer writer, Map<String, String> outputProperties) throws TransformerException {
188 try {
189 Transformer transformer = TransformerFactory.newInstance().newTransformer();
190 for (Map.Entry<String, String> property : outputProperties.entrySet()) {
191 try {
192 transformer.setOutputProperty(property.getKey(), property.getValue());
193 } catch (IllegalArgumentException e) {
194 //ignore invalid output properties
195 }
196 }
197
198 DOMSource source = new DOMSource(node);
199 StreamResult result = new StreamResult(writer);
200 transformer.transform(source, result);
201 } catch (TransformerConfigurationException e) {
202 //no complex configurations
203 } catch (TransformerFactoryConfigurationError e) {
204 //no complex configurations
205 }
206 }
207
208 /**
209 * Gets all the elements out of a {@link NodeList}.
210 * @param nodeList the node list
211 * @return the elements
212 */
213 public static List<Element> toElementList(NodeList nodeList) {
214 List<Element> elements = new ArrayList<Element>();
215 for (int i = 0; i < nodeList.getLength(); i++) {
216 Node node = nodeList.item(i);
217 if (node instanceof Element) {
218 elements.add((Element) node);
219 }
220 }
221 return elements;
222 }
223
224 /**
225 * Gets the root element of a document.
226 * @param parent the document
227 * @return the root element
228 */
229 public static Element getRootElement(Document parent) {
230 return getFirstChildElement((Node) parent);
231 }
232
233 /**
234 * Gets the first child element of an element.
235 * @param parent the parent element
236 * @return the first child element or null if there are no child elements
237 */
238 public static Element getFirstChildElement(Element parent) {
239 return getFirstChildElement((Node) parent);
240 }
241
242 /**
243 * Gets the first child element of a node.
244 * @param parent the node
245 * @return the first child element or null if there are no child elements
246 */
247 private static Element getFirstChildElement(Node parent) {
248 NodeList nodeList = parent.getChildNodes();
249 for (int i = 0; i < nodeList.getLength(); i++) {
250 Node node = nodeList.item(i);
251 if (node instanceof Element) {
252 return (Element) node;
253 }
254 }
255 return null;
256 }
257
258 private XmlUtils() {
259 //hide
260 }
261 }