001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.api; 020 021import com.google.common.collect.Maps; 022import java.io.IOException; 023import java.io.InputStream; 024import java.util.HashMap; 025import java.util.Map; 026import javax.xml.parsers.ParserConfigurationException; 027import javax.xml.parsers.SAXParserFactory; 028import org.xml.sax.InputSource; 029import org.xml.sax.SAXException; 030import org.xml.sax.SAXParseException; 031import org.xml.sax.XMLReader; 032import org.xml.sax.helpers.DefaultHandler; 033 034/** 035 * Contains the common implementation of a loader, for loading a configuration 036 * from an XML file. 037 * <p> 038 * The error handling policy can be described as being austere, dead set, 039 * disciplinary, dour, draconian, exacting, firm, forbidding, grim, hard, hard- 040 * boiled, harsh, harsh, in line, iron-fisted, no-nonsense, oppressive, 041 * persnickety, picky, prudish, punctilious, puritanical, rigid, rigorous, 042 * scrupulous, set, severe, square, stern, stickler, straight, strait-laced, 043 * stringent, stuffy, stuffy, tough, unpermissive, unsparing and uptight. 044 * </p> 045 * 046 * @author Oliver Burn 047 */ 048public abstract class AbstractLoader 049 extends DefaultHandler 050{ 051 /** maps public id to resolve to esource name for the DTD */ 052 private final Map<String, String> mPublicIdToResourceNameMap; 053 /** parser to read XML files **/ 054 private final XMLReader mParser; 055 056 /** 057 * Creates a new instance. 058 * @param aPublicId the public ID for the DTD to resolve 059 * @param aDtdResourceName the resource for the DTD 060 * @throws SAXException if an error occurs 061 * @throws ParserConfigurationException if an error occurs 062 */ 063 protected AbstractLoader(String aPublicId, String aDtdResourceName) 064 throws SAXException, ParserConfigurationException 065 { 066 this(new HashMap<String, String>(1)); 067 mPublicIdToResourceNameMap.put(aPublicId, aDtdResourceName); 068 } 069 070 /** 071 * Creates a new instance. 072 * @param aPublicIdToResourceNameMap maps public IDs to DTD resource names 073 * @throws SAXException if an error occurs 074 * @throws ParserConfigurationException if an error occurs 075 */ 076 protected AbstractLoader(Map<String, String> aPublicIdToResourceNameMap) 077 throws SAXException, ParserConfigurationException 078 { 079 mPublicIdToResourceNameMap = 080 Maps.newHashMap(aPublicIdToResourceNameMap); 081 final SAXParserFactory factory = SAXParserFactory.newInstance(); 082 LoadExternalDtdFeatureProvider.setFeaturesBySystemProperty(factory); 083 factory.setValidating(true); 084 factory.setNamespaceAware(true); 085 mParser = factory.newSAXParser().getXMLReader(); 086 mParser.setContentHandler(this); 087 mParser.setEntityResolver(this); 088 mParser.setErrorHandler(this); 089 } 090 091 /** 092 * Parses the specified input source. 093 * @param aInputSource the input source to parse. 094 * @throws IOException if an error occurs 095 * @throws SAXException in an error occurs 096 */ 097 public void parseInputSource(InputSource aInputSource) 098 throws IOException, SAXException 099 { 100 mParser.parse(aInputSource); 101 } 102 103 @Override 104 public InputSource resolveEntity(String aPublicId, String aSystemId) 105 throws SAXException, IOException 106 { 107 if (mPublicIdToResourceNameMap.keySet().contains(aPublicId)) { 108 final String dtdResourceName = 109 mPublicIdToResourceNameMap.get(aPublicId); 110 final ClassLoader loader = 111 this.getClass().getClassLoader(); 112 final InputStream dtdIS = 113 loader.getResourceAsStream(dtdResourceName); 114 if (dtdIS == null) { 115 throw new SAXException( 116 "Unable to load internal dtd " + dtdResourceName); 117 } 118 return new InputSource(dtdIS); 119 } 120 return super.resolveEntity(aPublicId, aSystemId); 121 } 122 123 @Override 124 public void warning(SAXParseException aEx) throws SAXException 125 { 126 throw aEx; 127 } 128 129 @Override 130 public void error(SAXParseException aEx) throws SAXException 131 { 132 throw aEx; 133 } 134 135 @Override 136 public void fatalError(SAXParseException aEx) throws SAXException 137 { 138 throw aEx; 139 } 140 141 /** 142 * Used for setting specific for secure java installations features to SAXParserFactory. 143 * Pulled out as a separate class in order to suppress Pitest mutations. 144 */ 145 public static final class LoadExternalDtdFeatureProvider { 146 147 /** System property name to enable external DTD load. */ 148 public static final String ENABLE_EXTERNAL_DTD_LOAD = "checkstyle.enableExternalDtdLoad"; 149 150 /** Feature that enables loading external DTD when loading XML files. */ 151 public static final String LOAD_EXTERNAL_DTD = 152 "http://apache.org/xml/features/nonvalidating/load-external-dtd"; 153 /** Feature that enables including external general entities in XML files. */ 154 public static final String EXTERNAL_GENERAL_ENTITIES = 155 "http://xml.org/sax/features/external-general-entities"; 156 157 /** Stop instances being created. **/ 158 private LoadExternalDtdFeatureProvider() { 159 } 160 161 /** 162 * Configures SAXParserFactory with features required 163 * to use external DTD file loading, this is not activated by default to no allow 164 * usage of schema files that checkstyle do not know 165 * it is even security problem to allow files from outside. 166 * @param factory factory to be configured with special features 167 * @throws SAXException if an error occurs 168 * @throws ParserConfigurationException if an error occurs 169 */ 170 public static void setFeaturesBySystemProperty(SAXParserFactory factory) 171 throws SAXException, ParserConfigurationException { 172 173 final boolean enableExternalDtdLoad = Boolean.valueOf( 174 System.getProperty(ENABLE_EXTERNAL_DTD_LOAD, "false")); 175 176 factory.setFeature(LOAD_EXTERNAL_DTD, enableExternalDtdLoad); 177 factory.setFeature(EXTERNAL_GENERAL_ENTITIES, enableExternalDtdLoad); 178 } 179 180 } 181}