aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/puppycrawl/tools/checkstyle/XmlLoader.java
blob: 5a410f045ec958f4385ba94885c829fb24b4ee5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2017 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Contains the common implementation of a loader, for loading a configuration
 * from an XML file.
 * <p>
 * The error handling policy can be described as being austere, dead set,
 * disciplinary, dour, draconian, exacting, firm, forbidding, grim, hard, hard-
 * boiled, harsh, harsh, in line, iron-fisted, no-nonsense, oppressive,
 * persnickety, picky, prudish, punctilious, puritanical, rigid, rigorous,
 * scrupulous, set, severe, square, stern, stickler, straight, strait-laced,
 * stringent, stuffy, stuffy, tough, unpermissive, unsparing and uptight.
 * </p>
 *
 * @author Oliver Burn
 * @noinspection ThisEscapedInObjectConstruction
 */
public class XmlLoader
    extends DefaultHandler {
    /** Maps public id to resolve to resource name for the DTD. */
    private final Map<String, String> publicIdToResourceNameMap;
    /** Parser to read XML files. **/
    private final XMLReader parser;

    /**
     * Creates a new instance.
     * @param publicId the public ID for the DTD to resolve
     * @param dtdResourceName the resource for the DTD
     * @throws SAXException if an error occurs
     * @throws ParserConfigurationException if an error occurs
     */
    protected XmlLoader(String publicId, String dtdResourceName)
            throws SAXException, ParserConfigurationException {
        this(new HashMap<>(1));
        publicIdToResourceNameMap.put(publicId, dtdResourceName);
    }

    /**
     * Creates a new instance.
     * @param publicIdToResourceNameMap maps public IDs to DTD resource names
     * @throws SAXException if an error occurs
     * @throws ParserConfigurationException if an error occurs
     */
    protected XmlLoader(Map<String, String> publicIdToResourceNameMap)
            throws SAXException, ParserConfigurationException {
        this.publicIdToResourceNameMap = new HashMap<>(publicIdToResourceNameMap);
        final SAXParserFactory factory = SAXParserFactory.newInstance();
        FeaturesForVerySecureJavaInstallations.addFeaturesForVerySecureJavaInstallations(factory);
        factory.setValidating(true);
        factory.setNamespaceAware(true);
        parser = factory.newSAXParser().getXMLReader();
        parser.setContentHandler(this);
        parser.setEntityResolver(this);
        parser.setErrorHandler(this);
    }

    /**
     * Parses the specified input source.
     * @param inputSource the input source to parse.
     * @throws IOException if an error occurs
     * @throws SAXException in an error occurs
     */
    public void parseInputSource(InputSource inputSource)
            throws IOException, SAXException {
        parser.parse(inputSource);
    }

    @Override
    public InputSource resolveEntity(String publicId, String systemId)
            throws SAXException, IOException {
        final InputSource inputSource;
        if (publicIdToResourceNameMap.keySet().contains(publicId)) {
            final String dtdResourceName =
                    publicIdToResourceNameMap.get(publicId);
            final ClassLoader loader =
                getClass().getClassLoader();
            final InputStream dtdIs =
                loader.getResourceAsStream(dtdResourceName);

            inputSource = new InputSource(dtdIs);
        }
        else {
            inputSource = super.resolveEntity(publicId, systemId);
        }
        return inputSource;
    }

    @Override
    public void error(SAXParseException exception) throws SAXException {
        throw exception;
    }

    @Override
    public void fatalError(SAXParseException exception) throws SAXException {
        throw exception;
    }

    /**
     * Used for setting specific for secure java installations features to SAXParserFactory.
     * Pulled out as a separate class in order to suppress Pitest mutations.
     */
    public static final class FeaturesForVerySecureJavaInstallations {
        /** Feature that enables loading external DTD when loading XML files. */
        private static final String LOAD_EXTERNAL_DTD =
                "http://apache.org/xml/features/nonvalidating/load-external-dtd";
        /** Feature that enables including external general entities in XML files. */
        private static final String EXTERNAL_GENERAL_ENTITIES =
                "http://xml.org/sax/features/external-general-entities";

        /** Stop instances being created. **/
        private FeaturesForVerySecureJavaInstallations() {
        }

        /**
         * Configures SAXParserFactory with features required
         * for execution on very secured environments.
         * @param factory factory to be configured with special features
         * @throws SAXException if an error occurs
         * @throws ParserConfigurationException if an error occurs
         */
        public static void addFeaturesForVerySecureJavaInstallations(SAXParserFactory factory)
                throws SAXException, ParserConfigurationException {
            factory.setFeature(LOAD_EXTERNAL_DTD, true);
            factory.setFeature(EXTERNAL_GENERAL_ENTITIES, true);
        }
    }
}