1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Map;
25
26 import javax.xml.parsers.ParserConfigurationException;
27 import javax.xml.parsers.SAXParserFactory;
28
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.SAXParseException;
32 import org.xml.sax.XMLReader;
33 import org.xml.sax.helpers.DefaultHandler;
34
35 import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
36
37 /**
38 * Contains the common implementation of a loader, for loading a configuration
39 * from an XML file.
40 *
41 * <p>
42 * The error handling policy can be described as being austere, dead set,
43 * disciplinary, dour, draconian, exacting, firm, forbidding, grim, hard, hard-
44 * boiled, harsh, harsh, in line, iron-fisted, no-nonsense, oppressive,
45 * persnickety, picky, prudish, punctilious, puritanical, rigid, rigorous,
46 * scrupulous, set, severe, square, stern, stickler, straight, strait-laced,
47 * stringent, stuffy, stuffy, tough, unpermissive, unsparing and uptight.
48 * </p>
49 *
50 * @noinspection ThisEscapedInObjectConstruction
51 * @noinspectionreason ThisEscapedInObjectConstruction - only reference is used and not
52 * accessed until initialized
53 */
54 public class XmlLoader
55 extends DefaultHandler {
56
57 /** Maps public id to resolve to resource name for the DTD. */
58 private final Map<String, String> publicIdToResourceNameMap;
59 /** Parser to read XML files. **/
60 private final XMLReader parser;
61
62 /**
63 * Creates a new instance.
64 *
65 * @param publicIdToResourceNameMap maps public IDs to DTD resource names
66 * @throws SAXException if an error occurs
67 * @throws ParserConfigurationException if an error occurs
68 */
69 protected XmlLoader(Map<String, String> publicIdToResourceNameMap)
70 throws SAXException, ParserConfigurationException {
71 this.publicIdToResourceNameMap =
72 UnmodifiableCollectionUtil.copyOfMap(publicIdToResourceNameMap);
73 parser = createXmlReader(this);
74 }
75
76 /**
77 * Parses the specified input source.
78 *
79 * @param inputSource the input source to parse.
80 * @throws IOException if an error occurs
81 * @throws SAXException in an error occurs
82 */
83 public void parseInputSource(InputSource inputSource)
84 throws IOException, SAXException {
85 parser.parse(inputSource);
86 }
87
88 @Override
89 public InputSource resolveEntity(String publicId, String systemId) {
90 InputSource inputSource = null;
91 if (publicId != null) {
92 final String dtdResourceName = publicIdToResourceNameMap.get(publicId);
93
94 if (dtdResourceName != null) {
95 final ClassLoader loader = getClass().getClassLoader();
96 final InputStream dtdIs = loader.getResourceAsStream(dtdResourceName);
97 inputSource = new InputSource(dtdIs);
98 }
99 }
100 return inputSource;
101 }
102
103 @Override
104 public void error(SAXParseException exception) throws SAXException {
105 throw exception;
106 }
107
108 /**
109 * Helper method to create {@code XMLReader}.
110 *
111 * @param handler the content handler
112 * @return new XMLReader instance
113 * @throws ParserConfigurationException if a parser cannot be created
114 * @throws SAXException for SAX errors
115 */
116 private static XMLReader createXmlReader(DefaultHandler handler)
117 throws SAXException, ParserConfigurationException {
118 final SAXParserFactory factory = SAXParserFactory.newInstance();
119 LoadExternalDtdFeatureProvider.setFeaturesBySystemProperty(factory);
120 factory.setValidating(true);
121 final XMLReader xmlReader = factory.newSAXParser().getXMLReader();
122 xmlReader.setContentHandler(handler);
123 xmlReader.setEntityResolver(handler);
124 xmlReader.setErrorHandler(handler);
125 return xmlReader;
126 }
127
128 /**
129 * Used for setting specific for secure java installations features to SAXParserFactory.
130 * Pulled out as a separate class in order to suppress Pitest mutations.
131 */
132 public static final class LoadExternalDtdFeatureProvider {
133
134 /** System property name to enable external DTD load. */
135 public static final String ENABLE_EXTERNAL_DTD_LOAD = "checkstyle.enableExternalDtdLoad";
136
137 /** Feature that enables loading external DTD when loading XML files. */
138 public static final String LOAD_EXTERNAL_DTD =
139 "http://apache.org/xml/features/nonvalidating/load-external-dtd";
140 /** Feature that enables including external general entities in XML files. */
141 public static final String EXTERNAL_GENERAL_ENTITIES =
142 "http://xml.org/sax/features/external-general-entities";
143 /** Feature that enables including external parameter entities in XML files. */
144 public static final String EXTERNAL_PARAMETER_ENTITIES =
145 "http://xml.org/sax/features/external-parameter-entities";
146
147 /** Stop instances being created. **/
148 private LoadExternalDtdFeatureProvider() {
149 }
150
151 /**
152 * Configures SAXParserFactory with features required
153 * to use external DTD file loading, this is not activated by default to no allow
154 * usage of schema files that checkstyle do not know
155 * it is even security problem to allow files from outside.
156 *
157 * @param factory factory to be configured with special features
158 * @throws SAXException if an error occurs
159 * @throws ParserConfigurationException if an error occurs
160 */
161 public static void setFeaturesBySystemProperty(SAXParserFactory factory)
162 throws SAXException, ParserConfigurationException {
163
164 final boolean enableExternalDtdLoad = Boolean.parseBoolean(
165 System.getProperty(ENABLE_EXTERNAL_DTD_LOAD, "false"));
166
167 factory.setFeature(LOAD_EXTERNAL_DTD, enableExternalDtdLoad);
168 factory.setFeature(EXTERNAL_GENERAL_ENTITIES, enableExternalDtdLoad);
169 factory.setFeature(EXTERNAL_PARAMETER_ENTITIES, enableExternalDtdLoad);
170 }
171
172 }
173
174 }