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 }