Migrate common/util to use JDT annotations
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / xml / UntrustedXML.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util.xml;
9
10 import com.google.common.annotations.Beta;
11 import java.io.InputStream;
12 import java.io.Reader;
13 import java.nio.charset.Charset;
14 import javax.xml.XMLConstants;
15 import javax.xml.parsers.DocumentBuilder;
16 import javax.xml.parsers.DocumentBuilderFactory;
17 import javax.xml.parsers.ParserConfigurationException;
18 import javax.xml.parsers.SAXParser;
19 import javax.xml.parsers.SAXParserFactory;
20 import javax.xml.stream.XMLInputFactory;
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.XMLStreamReader;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.xml.sax.SAXException;
25 import org.xml.sax.SAXNotRecognizedException;
26 import org.xml.sax.SAXNotSupportedException;
27
28 /**
29  * Set of utility methods for instantiating parser that deal with untrusted XML sources.
30  *
31  * @author Robert Varga
32  */
33 @Beta
34 public final class UntrustedXML {
35     private static final @NonNull DocumentBuilderFactory DBF;
36
37     static {
38         final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
39         f.setCoalescing(true);
40         f.setExpandEntityReferences(false);
41         f.setIgnoringElementContentWhitespace(true);
42         f.setIgnoringComments(true);
43         f.setNamespaceAware(true);
44         f.setXIncludeAware(false);
45         try {
46             f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
47             f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
48             f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
49             f.setFeature("http://xml.org/sax/features/external-general-entities", false);
50             f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
51         } catch (final ParserConfigurationException e) {
52             throw new ExceptionInInitializerError(e);
53         }
54         DBF = f;
55     }
56
57     private static final SAXParserFactory SPF;
58
59     static {
60         final SAXParserFactory f = SAXParserFactory.newInstance();
61         f.setNamespaceAware(true);
62         f.setXIncludeAware(false);
63         try {
64             f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
65             f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
66             f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
67             f.setFeature("http://xml.org/sax/features/external-general-entities", false);
68             f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
69         } catch (final SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) {
70             throw new ExceptionInInitializerError(e);
71         }
72
73         SPF = f;
74     }
75
76     private static final XMLInputFactory XIF;
77
78     static {
79         final XMLInputFactory f = XMLInputFactory.newInstance();
80
81         f.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
82         f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);
83         f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
84         f.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
85
86         XIF = f;
87     }
88
89     private UntrustedXML() {
90         throw new UnsupportedOperationException();
91     }
92
93     /**
94      * Create a new {@link DocumentBuilder} for dealing with untrusted XML data. This method is equivalent to
95      * {@link DocumentBuilderFactory#newDocumentBuilder()}, except it does not throw a checked exception.
96      *
97      * @return A new DocumentBuilder
98      * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
99      */
100     public static @NonNull DocumentBuilder newDocumentBuilder() {
101         try {
102             return DBF.newDocumentBuilder();
103         } catch (ParserConfigurationException e) {
104             throw new UnsupportedOperationException("Failed to instantiate a DocumentBuilder", e);
105         }
106     }
107
108     /**
109      * Create a new {@link SAXParser} for dealing with untrusted XML data. This method is equivalent to
110      * {@link SAXParserFactory#newSAXParser()}, except it does not throw a checked exception.
111      *
112      * @return A new SAXParser
113      * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
114      */
115     public static @NonNull SAXParser newSAXParser() {
116         try {
117             return SPF.newSAXParser();
118         } catch (ParserConfigurationException | SAXException e) {
119             throw new UnsupportedOperationException("Failed to instantiate a SAXParser", e);
120         }
121     }
122
123     /**
124      * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
125      * {@link XMLInputFactory#createXMLStreamReader(InputStream)}.
126      *
127      * @return A new XMLStreamReader
128      * @throws XMLStreamException when the underlying factory throws it
129      */
130     public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream) throws XMLStreamException {
131         return XIF.createXMLStreamReader(stream);
132     }
133
134     /**
135      * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
136      * {@link XMLInputFactory#createXMLStreamReader(InputStream, String)}, except it takes an explict charset argument.
137      *
138      * @return A new XMLStreamReader
139      * @throws XMLStreamException when the underlying factory throws it
140      */
141     public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream, final Charset charset)
142             throws XMLStreamException {
143         return XIF.createXMLStreamReader(stream, charset.name());
144     }
145
146     /**
147      * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
148      * {@link XMLInputFactory#createXMLStreamReader(Reader)}.
149      *
150      * @return A new XMLStreamReader
151      * @throws XMLStreamException when the underlying factory throws it
152      */
153     public static @NonNull XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
154         return XIF.createXMLStreamReader(reader);
155     }
156 }