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.filters; 020 021import java.io.File; 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.net.MalformedURLException; 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.net.URL; 028import java.util.Map; 029import java.util.regex.PatternSyntaxException; 030 031import javax.xml.parsers.ParserConfigurationException; 032 033import org.xml.sax.Attributes; 034import org.xml.sax.InputSource; 035import org.xml.sax.SAXException; 036 037import com.google.common.collect.Maps; 038import com.puppycrawl.tools.checkstyle.api.AbstractLoader; 039import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 040import com.puppycrawl.tools.checkstyle.api.FilterSet; 041 042/** 043 * Loads a filter chain of suppressions. 044 * @author Rick Giles 045 */ 046public final class SuppressionsLoader 047 extends AbstractLoader 048{ 049 /** the public ID for the configuration dtd */ 050 private static final String DTD_PUBLIC_ID_1_0 = 051 "-//Puppy Crawl//DTD Suppressions 1.0//EN"; 052 /** the resource for the configuration dtd */ 053 private static final String DTD_RESOURCE_NAME_1_0 = 054 "com/puppycrawl/tools/checkstyle/suppressions_1_0.dtd"; 055 /** the public ID for the configuration dtd */ 056 private static final String DTD_PUBLIC_ID_1_1 = 057 "-//Puppy Crawl//DTD Suppressions 1.1//EN"; 058 /** the resource for the configuration dtd */ 059 private static final String DTD_RESOURCE_NAME_1_1 = 060 "com/puppycrawl/tools/checkstyle/suppressions_1_1.dtd"; 061 062 /** 063 * the filter chain to return in getAFilterChain(), 064 * configured during parsing 065 */ 066 private final FilterSet filterChain = new FilterSet(); 067 068 /** 069 * Creates a new <code>SuppressionsLoader</code> instance. 070 * @throws ParserConfigurationException if an error occurs 071 * @throws SAXException if an error occurs 072 */ 073 private SuppressionsLoader() 074 throws ParserConfigurationException, SAXException 075 { 076 super(createIdToResourceNameMap()); 077 } 078 079 /** 080 * Returns the loaded filter chain. 081 * @return the loaded filter chain. 082 */ 083 public FilterSet getFilterChain() 084 { 085 return filterChain; 086 } 087 088 @Override 089 public void startElement(String namespaceURI, 090 String localName, 091 String qName, 092 Attributes atts) 093 throws SAXException 094 { 095 if ("suppress".equals(qName)) { 096 //add SuppressElement filter to the filter chain 097 final String files = atts.getValue("files"); 098 if (files == null) { 099 throw new SAXException("missing files attribute"); 100 } 101 final String checks = atts.getValue("checks"); 102 final String modId = atts.getValue("id"); 103 if ((checks == null) && (modId == null)) { 104 throw new SAXException("missing checks and id attribute"); 105 } 106 final SuppressElement suppress; 107 try { 108 suppress = new SuppressElement(files); 109 if (modId != null) { 110 suppress.setModuleId(modId); 111 } 112 if (checks != null) { 113 suppress.setChecks(checks); 114 } 115 } 116 catch (final PatternSyntaxException e) { 117 throw new SAXException("invalid files or checks format"); 118 } 119 final String lines = atts.getValue("lines"); 120 if (lines != null) { 121 suppress.setLines(lines); 122 } 123 final String columns = atts.getValue("columns"); 124 if (columns != null) { 125 suppress.setColumns(columns); 126 } 127 filterChain.addFilter(suppress); 128 } 129 } 130 131 /** 132 * Returns the suppression filters in a specified file. 133 * @param filename name of the suppresssions file. 134 * @return the filter chain of suppression elements specified in the file. 135 * @throws CheckstyleException if an error occurs. 136 */ 137 public static FilterSet loadSuppressions(String filename) 138 throws CheckstyleException 139 { 140 try { 141 // figure out if this is a File or a URL 142 URI uri; 143 try { 144 final URL url = new URL(filename); 145 uri = url.toURI(); 146 } 147 catch (final MalformedURLException ex) { 148 uri = null; 149 } 150 catch (final URISyntaxException ex) { 151 // URL violating RFC 2396 152 uri = null; 153 } 154 if (uri == null) { 155 final File file = new File(filename); 156 if (file.exists()) { 157 uri = file.toURI(); 158 } 159 else { 160 // check to see if the file is in the classpath 161 try { 162 final URL configUrl = SuppressionsLoader.class 163 .getResource(filename); 164 if (configUrl == null) { 165 throw new FileNotFoundException(filename); 166 } 167 uri = configUrl.toURI(); 168 } 169 catch (final URISyntaxException e) { 170 throw new FileNotFoundException(filename); 171 } 172 } 173 } 174 final InputSource source = new InputSource(uri.toString()); 175 return loadSuppressions(source, filename); 176 } 177 catch (final FileNotFoundException e) { 178 throw new CheckstyleException("unable to find " + filename, e); 179 } 180 } 181 182 /** 183 * Returns the suppression filters in a specified source. 184 * @param source the source for the suppressions. 185 * @param sourceName the name of the source. 186 * @return the filter chain of suppression elements in source. 187 * @throws CheckstyleException if an error occurs. 188 */ 189 private static FilterSet loadSuppressions( 190 InputSource source, String sourceName) 191 throws CheckstyleException 192 { 193 try { 194 final SuppressionsLoader suppressionsLoader = 195 new SuppressionsLoader(); 196 suppressionsLoader.parseInputSource(source); 197 return suppressionsLoader.getFilterChain(); 198 } 199 catch (final FileNotFoundException e) { 200 throw new CheckstyleException("unable to find " + sourceName, e); 201 } 202 catch (final ParserConfigurationException e) { 203 throw new CheckstyleException("unable to parse " + sourceName, e); 204 } 205 catch (final SAXException e) { 206 throw new CheckstyleException("unable to parse " 207 + sourceName + " - " + e.getMessage(), e); 208 } 209 catch (final IOException e) { 210 throw new CheckstyleException("unable to read " + sourceName, e); 211 } 212 catch (final NumberFormatException e) { 213 throw new CheckstyleException("number format exception " 214 + sourceName + " - " + e.getMessage(), e); 215 } 216 } 217 218 /** 219 * Creates mapping between local resources and dtd ids. 220 * @return map between local resources and dtd ids. 221 */ 222 private static Map<String, String> createIdToResourceNameMap() 223 { 224 final Map<String, String> map = Maps.newHashMap(); 225 map.put(DTD_PUBLIC_ID_1_0, DTD_RESOURCE_NAME_1_0); 226 map.put(DTD_PUBLIC_ID_1_1, DTD_RESOURCE_NAME_1_1); 227 return map; 228 } 229}