4 * Copyright 2010 Codist Monk.
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 package net
.sourceforge
.aprog
.xml
;
27 import java
.io
.IOException
;
28 import java
.util
.logging
.Logger
;
29 import javax
.xml
.transform
.TransformerConfigurationException
;
30 import javax
.xml
.transform
.TransformerException
;
31 import static net
.sourceforge
.aprog
.tools
.Tools
.*;
33 import java
.io
.ByteArrayInputStream
;
34 import java
.io
.ByteArrayOutputStream
;
36 import java
.io
.InputStream
;
37 import java
.io
.OutputStream
;
38 import java
.util
.ArrayList
;
39 import java
.util
.List
;
40 import java
.util
.logging
.Level
;
41 import javax
.xml
.XMLConstants
;
42 import javax
.xml
.namespace
.QName
;
44 import javax
.xml
.parsers
.DocumentBuilderFactory
;
45 import javax
.xml
.parsers
.ParserConfigurationException
;
46 import javax
.xml
.parsers
.SAXParserFactory
;
47 import javax
.xml
.transform
.OutputKeys
;
48 import javax
.xml
.transform
.Result
;
49 import javax
.xml
.transform
.Source
;
50 import javax
.xml
.transform
.Transformer
;
51 import javax
.xml
.transform
.TransformerFactory
;
52 import javax
.xml
.transform
.dom
.DOMSource
;
53 import javax
.xml
.transform
.stream
.StreamResult
;
54 import javax
.xml
.transform
.stream
.StreamSource
;
55 import javax
.xml
.validation
.SchemaFactory
;
56 import javax
.xml
.validation
.Validator
;
57 import javax
.xml
.xpath
.XPathConstants
;
58 import javax
.xml
.xpath
.XPathFactory
;
60 import net
.sourceforge
.aprog
.tools
.IllegalInstantiationException
;
62 import org
.w3c
.dom
.Document
;
63 import org
.w3c
.dom
.Node
;
64 import org
.w3c
.dom
.NodeList
;
65 import org
.xml
.sax
.InputSource
;
66 import org
.xml
.sax
.SAXException
;
67 import org
.xml
.sax
.SAXParseException
;
68 import org
.xml
.sax
.helpers
.DefaultHandler
;
72 * @author codistmonk (creation 2010-07-02)
74 public final class XMLTools
{
77 * @throws IllegalInstantiationException To prevent instantiation
80 throw new IllegalInstantiationException();
83 public static final String XML_1_UTF8_NOT_STANDALONE
=
84 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
86 public static final String XML_1_UTF8_STANDALONE
=
87 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
96 * @throws RuntimeException if an error occurs
98 public static final Document
parse(final String xmlInput
) {
99 return parse(new ByteArrayInputStream(xmlInput
.getBytes()));
104 * @param xmlInputStream
110 * @throws RuntimeException if an error occurs
112 public static final Document
parse(final InputStream xmlInputStream
) {
113 return parse(new InputSource(xmlInputStream
));
124 * @throws RuntimeException if an error occurs
126 public static final Document
parse(final InputSource inputSource
) {
128 return DocumentBuilderFactory
.newInstance().newDocumentBuilder().parse(inputSource
);
129 } catch (final Exception exception
) {
130 throw unchecked(exception
);
140 public static final Document
newDocument() {
142 final Document result
= DocumentBuilderFactory
.newInstance().newDocumentBuilder().newDocument();
144 result
.setXmlStandalone(true);
147 } catch (final ParserConfigurationException exception
) {
148 throw unchecked(exception
);
157 * @return {@code document}
160 public static final Document
normalize(final Document document
) {
161 document
.normalize();
171 * @return {@code document}
174 public static final Document
standalone(final Document document
) {
175 document
.setXmlStandalone(true);
188 * <br>Range: {@code [0 .. Integer.MAX_VALUE]}
190 public static final void write(final Node node
, final File outputFile
, final int indent
) {
191 write(node
, new StreamResult(outputFile
.getAbsolutePath()), indent
);
202 * <br>Range: {@code [0 .. Integer.MAX_VALUE]}
204 public static final void write(final Node node
, final OutputStream output
, final int indent
) {
205 write(node
, new StreamResult(output
), indent
);
216 * <br>Range: {@code [0 .. Integer.MAX_VALUE]}
218 public static final void write(final Node node
, final Result output
, final int indent
) {
220 final TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
222 transformerFactory
.setAttribute("indent-number", indent
);
224 final Transformer transformer
= transformerFactory
.newTransformer();
226 transformer
.setOutputProperty(OutputKeys
.METHOD
, "xml");
227 transformer
.setOutputProperty(OutputKeys
.INDENT
, indent
!= 0 ?
"yes" : "no");
228 transformer
.setOutputProperty(OutputKeys
.OMIT_XML_DECLARATION
, node
instanceof Document ?
"no" : "yes");
229 transformer
.setOutputProperty(OutputKeys
.STANDALONE
, "yes");
231 transformer
.transform(new DOMSource(node
), output
);
232 } catch (final Exception exception
) {
233 throw unchecked(exception
);
239 * @param resourceName
245 public static final Source
getResourceAsSource(final String resourceName
) {
246 final ClassLoader classLoader
= getCallerClass().getClassLoader();
247 final Source result
= new StreamSource(classLoader
.getResourceAsStream(resourceName
));
249 result
.setSystemId(classLoader
.getResource(resourceName
).toString());
262 public static final List
<Node
> newList(final NodeList nodeList
) {
263 final List
<Node
> result
= new ArrayList
<Node
>();
265 for (int i
= 0; i
< nodeList
.getLength(); ++i
) {
266 result
.add(nodeList
.item(i
));
274 * @param xmlInputStream IN NOT_NULL
275 * @param dtdOrSchema IN NOT_NULL
276 * @return NEW NOT_NULL
278 public static final List
<Throwable
> validate(final InputStream xmlInputStream
, final Source dtdOrSchema
) {
279 final List
<Throwable
> exceptions
= new ArrayList
<Throwable
>();
280 final String schemaLanguage
= getSchemaLanguage(dtdOrSchema
);
283 if (schemaLanguage
!= null) {
284 final Validator validator
= SchemaFactory
.newInstance(schemaLanguage
).newSchema(dtdOrSchema
).newValidator();
286 validator
.validate(new StreamSource(xmlInputStream
));
288 final Transformer addDoctypeInformation
= TransformerFactory
.newInstance().newTransformer();
290 addDoctypeInformation
.setOutputProperty(OutputKeys
.DOCTYPE_SYSTEM
, dtdOrSchema
.getSystemId());
292 final ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
294 addDoctypeInformation
.transform(new StreamSource(xmlInputStream
), new StreamResult(buffer
));
296 final SAXParserFactory saxParserFactory
= SAXParserFactory
.newInstance();
298 saxParserFactory
.setValidating(true);
299 saxParserFactory
.newSAXParser().parse(new ByteArrayInputStream(buffer
.toByteArray()), new DefaultHandler() {
302 public final void error(final SAXParseException exception
) throws SAXException
{
303 exceptions
.add(exception
);
305 getLoggerForThisMethod().log(Level
.WARNING
, "", exceptions
);
309 public final void fatalError(final SAXParseException exception
) throws SAXException
{
310 exceptions
.add(exception
);
312 getLoggerForThisMethod().log(Level
.WARNING
, "", exceptions
);
318 public final void warning(final SAXParseException exception
) throws SAXException
{
319 getLoggerForThisMethod().log(Level
.WARNING
, "", exceptions
);
324 } catch (final TransformerConfigurationException exception
) {
325 throw unchecked(exception
);
326 } catch (final TransformerException exception
) {
327 exceptions
.add(exception
);
328 } catch (final SAXException exception
) {
329 exceptions
.add(exception
);
330 } catch (final Exception exception
) {
331 throw unchecked(exception
);
341 * @return {@code null} for a DTD
343 * <br>Range: { {@code null}, {@link XMLConstants#W3C_XML_SCHEMA_NS_URI}, {@link XMLConstants#RELAXNG_NS_URI} }
344 * @throws IllegalArgumentException If {@code dtdOrSchema}'s system id does not indicate a DTD or a schema (XSD or RNG)
346 public static final String
getSchemaLanguage(final Source dtdOrSchema
) {
347 final String dtdOrSchemaId
= dtdOrSchema
.getSystemId().toLowerCase();
349 if (dtdOrSchemaId
.endsWith(".xsd")) {
350 return XMLConstants
.W3C_XML_SCHEMA_NS_URI
;
351 } else if (dtdOrSchemaId
.endsWith(".rng") || dtdOrSchemaId
.endsWith(".rnc")) {
352 return XMLConstants
.RELAXNG_NS_URI
;
353 } else if (dtdOrSchemaId
.endsWith(".dtd")) {
357 throw new IllegalArgumentException("Unsupported extension for " + dtdOrSchema
+
358 " (the name must end with .xsd, .rng, .rnc or .dtd (case-insensitive))");
371 public static final Node
getNode(final Node context
, final String xPath
) {
372 return getNode(context
, xPath
);
385 public static final Node
getOrCreateNode(final Node context
, final String xPath
) {
386 return getOrCreateNode(context
, xPath
);
399 public static final NodeList
getNodes(final Node context
, final String xPath
) {
400 return getNodes(context
, xPath
);
405 * @param <N> The expected node type
414 @SuppressWarnings("unchecked")
415 public static final <N
> N
getNode(final Object context
, final String xPath
) {
416 return (N
) get(context
, xPath
, XPathConstants
.NODE
);
421 * @param <S> The expected node set type
430 @SuppressWarnings("unchecked")
431 public static final <S
> S
getNodes(final Object context
, final String xPath
) {
432 return (S
) get(context
, xPath
, XPathConstants
.NODESET
);
445 public static final Boolean
getBoolean(final Object context
, final String xPath
) {
446 return (Boolean
) get(context
, xPath
, XPathConstants
.BOOLEAN
);
451 * @param <T> The expected return type
459 * {@link XPathConstants#BOOLEAN},
460 * {@link XPathConstants#NODE},
461 * {@link XPathConstants#NODESET},
462 * {@link XPathConstants#NUMBER},
463 * {@link XPathConstants#STRING}
467 * @throws RuntimeException If an error occurs
469 @SuppressWarnings("unchecked")
470 public static final <T
> T
get(final Object context
, final String xPath
, final QName returnType
) {
472 return (T
) XPathFactory
.newInstance().newXPath().compile(xPath
)
473 .evaluate(context
, returnType
);
474 } catch (final Exception exception
) {
475 throw unchecked(exception
);
481 * @param <N> The expected node type
490 public static final <N
> N
getOrCreateNode(final Object context
, final String xPath
) {