Coverage Report - com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
SuppressionsLoader
100%
67/67
100%
16/16
3.3
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 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.filters;
 21  
 
 22  
 import java.io.FileNotFoundException;
 23  
 import java.io.IOException;
 24  
 import java.net.URI;
 25  
 import java.util.HashMap;
 26  
 import java.util.HashSet;
 27  
 import java.util.Locale;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 import java.util.regex.PatternSyntaxException;
 31  
 
 32  
 import javax.xml.parsers.ParserConfigurationException;
 33  
 
 34  
 import org.xml.sax.Attributes;
 35  
 import org.xml.sax.InputSource;
 36  
 import org.xml.sax.SAXException;
 37  
 
 38  
 import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
 39  
 import com.puppycrawl.tools.checkstyle.XmlLoader;
 40  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 41  
 import com.puppycrawl.tools.checkstyle.api.FilterSet;
 42  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 43  
 
 44  
 /**
 45  
  * Loads a filter chain of suppressions.
 46  
  * @author Rick Giles
 47  
  */
 48  
 public final class SuppressionsLoader
 49  
     extends XmlLoader {
 50  
     /** The public ID for the configuration dtd. */
 51  
     private static final String DTD_PUBLIC_ID_1_0 =
 52  
         "-//Puppy Crawl//DTD Suppressions 1.0//EN";
 53  
     /** The resource for the configuration dtd. */
 54  
     private static final String DTD_SUPPRESSIONS_NAME_1_0 =
 55  
         "com/puppycrawl/tools/checkstyle/suppressions_1_0.dtd";
 56  
     /** The public ID for the configuration dtd. */
 57  
     private static final String DTD_PUBLIC_ID_1_1 =
 58  
         "-//Puppy Crawl//DTD Suppressions 1.1//EN";
 59  
     /** The resource for the configuration dtd. */
 60  
     private static final String DTD_SUPPRESSIONS_NAME_1_1 =
 61  
         "com/puppycrawl/tools/checkstyle/suppressions_1_1.dtd";
 62  
     /** The public ID for the configuration dtd. */
 63  
     private static final String DTD_PUBLIC_ID_1_2 =
 64  
         "-//Puppy Crawl//DTD Suppressions 1.2//EN";
 65  
     /** The resource for the configuration dtd. */
 66  
     private static final String DTD_SUPPRESSIONS_NAME_1_2 =
 67  
         "com/puppycrawl/tools/checkstyle/suppressions_1_2.dtd";
 68  
     /** The public ID for the configuration dtd. */
 69  
     private static final String DTD_PUBLIC_ID_1_1_XPATH =
 70  
             "-//Puppy Crawl//DTD Suppressions Xpath Experimental 1.1//EN";
 71  
     /** The resource for the configuration dtd. */
 72  
     private static final String DTD_SUPPRESSIONS_NAME_1_1_XPATH =
 73  
             "com/puppycrawl/tools/checkstyle/suppressions_1_1_xpath_experimental.dtd";
 74  
     /** The public ID for the configuration dtd. */
 75  
     private static final String DTD_PUBLIC_ID_1_2_XPATH =
 76  
             "-//Puppy Crawl//DTD Suppressions Xpath Experimental 1.2//EN";
 77  
     /** The resource for the configuration dtd. */
 78  
     private static final String DTD_SUPPRESSIONS_NAME_1_2_XPATH =
 79  
             "com/puppycrawl/tools/checkstyle/suppressions_1_2_xpath_experimental.dtd";
 80  
     /** File search error message. **/
 81  
     private static final String UNABLE_TO_FIND_ERROR_MESSAGE = "Unable to find: ";
 82  
     /** String literal for attribute name. **/
 83  
     private static final String ATTRIBUTE_NAME_FILES = "files";
 84  
     /** String literal for attribute name. **/
 85  
     private static final String ATTRIBUTE_NAME_CHECKS = "checks";
 86  
     /** String literal for attribute name. **/
 87  
     private static final String ATTRIBUTE_NAME_MESSAGE = "message";
 88  
     /** String literal for attribute name. **/
 89  
     private static final String ATTRIBUTE_NAME_ID = "id";
 90  
     /** String literal for attribute name. **/
 91  
     private static final String ATTRIBUTE_NAME_QUERY = "query";
 92  
     /** String literal for attribute name. **/
 93  
     private static final String ATTRIBUTE_NAME_LINES = "lines";
 94  
     /** String literal for attribute name. **/
 95  
     private static final String ATTRIBUTE_NAME_COLUMNS = "columns";
 96  
 
 97  
     /**
 98  
      * The filter chain to return in getAFilterChain(),
 99  
      * configured during parsing.
 100  
      */
 101  35
     private final FilterSet filterChain = new FilterSet();
 102  
 
 103  
     /**
 104  
      * The set of the {@code TreeWalkerFilter} filters. Being filled during parsing.
 105  
      */
 106  35
     private final Set<TreeWalkerFilter> treeWalkerFilters = new HashSet<>();
 107  
 
 108  
     /**
 109  
      * Creates a new {@code SuppressionsLoader} instance.
 110  
      * @throws ParserConfigurationException if an error occurs
 111  
      * @throws SAXException if an error occurs
 112  
      */
 113  
     private SuppressionsLoader()
 114  
             throws ParserConfigurationException, SAXException {
 115  35
         super(createIdToResourceNameMap());
 116  35
     }
 117  
 
 118  
     @Override
 119  
     public void startElement(String namespaceUri,
 120  
                              String localName,
 121  
                              String qName,
 122  
                              Attributes attributes)
 123  
             throws SAXException {
 124  140
         if ("suppress".equals(qName)) {
 125  
             //add SuppressElement filter to the filter chain
 126  98
             final SuppressElement suppress = getSuppressElement(attributes);
 127  94
             filterChain.addFilter(suppress);
 128  94
         }
 129  42
         else if ("suppress-xpath".equals(qName)) {
 130  9
             final XpathFilter filter = getXpathFilter(attributes);
 131  6
             treeWalkerFilters.add(filter);
 132  
         }
 133  133
     }
 134  
 
 135  
     /**
 136  
      * Returns the suppress element, initialized from given attributes.
 137  
      * @param attributes the attributes of xml-tag "<suppress></suppress>", specified inside
 138  
      *                   suppression file.
 139  
      * @return the suppress element
 140  
      * @throws SAXException if an error occurs.
 141  
      */
 142  
     private static SuppressElement getSuppressElement(Attributes attributes) throws SAXException {
 143  98
         final String checks = attributes.getValue(ATTRIBUTE_NAME_CHECKS);
 144  98
         final String modId = attributes.getValue(ATTRIBUTE_NAME_ID);
 145  98
         final String message = attributes.getValue(ATTRIBUTE_NAME_MESSAGE);
 146  98
         if (checks == null && modId == null && message == null) {
 147  
             // -@cs[IllegalInstantiation] SAXException is in the overridden method signature
 148  1
             throw new SAXException("missing checks or id or message attribute");
 149  
         }
 150  
         final SuppressElement suppress;
 151  
         try {
 152  97
             final String files = attributes.getValue(ATTRIBUTE_NAME_FILES);
 153  97
             final String lines = attributes.getValue(ATTRIBUTE_NAME_LINES);
 154  97
             final String columns = attributes.getValue(ATTRIBUTE_NAME_COLUMNS);
 155  97
             suppress = new SuppressElement(files, checks, message, modId, lines, columns);
 156  
         }
 157  2
         catch (final PatternSyntaxException ex) {
 158  
             // -@cs[IllegalInstantiation] SAXException is in the overridden method signature
 159  2
             throw new SAXException("invalid files or checks or message format", ex);
 160  94
         }
 161  94
         return suppress;
 162  
     }
 163  
 
 164  
     /**
 165  
      * Returns the xpath filter, initialized from given attributes.
 166  
      * @param attributes the attributes of xml-tag "<suppress-xpath></suppress-xpath>",
 167  
      *                   specified inside suppression file.
 168  
      * @return the xpath filter
 169  
      * @throws SAXException if an error occurs.
 170  
      */
 171  
     private static XpathFilter getXpathFilter(Attributes attributes) throws SAXException {
 172  9
         final String checks = attributes.getValue(ATTRIBUTE_NAME_CHECKS);
 173  9
         final String modId = attributes.getValue(ATTRIBUTE_NAME_ID);
 174  9
         final String message = attributes.getValue(ATTRIBUTE_NAME_MESSAGE);
 175  9
         if (checks == null && modId == null && message == null) {
 176  
             // -@cs[IllegalInstantiation] SAXException is in the overridden method signature
 177  1
             throw new SAXException("missing checks or id or message attribute for suppress-xpath");
 178  
         }
 179  
         final XpathFilter filter;
 180  
         try {
 181  8
             final String files = attributes.getValue(ATTRIBUTE_NAME_FILES);
 182  8
             final String xpathQuery = attributes.getValue(ATTRIBUTE_NAME_QUERY);
 183  8
             filter = new XpathFilter(files, checks, message, modId, xpathQuery);
 184  
         }
 185  2
         catch (final PatternSyntaxException ex) {
 186  
             // -@cs[IllegalInstantiation] SAXException is in the overridden method signature
 187  2
             throw new SAXException("invalid files or checks or message format for suppress-xpath",
 188  
                     ex);
 189  6
         }
 190  6
         return filter;
 191  
     }
 192  
 
 193  
     /**
 194  
      * Returns the suppression filters in a specified file.
 195  
      * @param filename name of the suppressions file.
 196  
      * @return the filter chain of suppression elements specified in the file.
 197  
      * @throws CheckstyleException if an error occurs.
 198  
      */
 199  
     public static FilterSet loadSuppressions(String filename)
 200  
             throws CheckstyleException {
 201  
         // figure out if this is a File or a URL
 202  23
         final URI uri = CommonUtils.getUriByFilename(filename);
 203  20
         final InputSource source = new InputSource(uri.toString());
 204  20
         return loadSuppressions(source, filename);
 205  
     }
 206  
 
 207  
     /**
 208  
      * Returns the suppression filters in a specified source.
 209  
      * @param source the source for the suppressions.
 210  
      * @param sourceName the name of the source.
 211  
      * @return the filter chain of suppression elements in source.
 212  
      * @throws CheckstyleException if an error occurs.
 213  
      */
 214  
     private static FilterSet loadSuppressions(
 215  
             InputSource source, String sourceName)
 216  
             throws CheckstyleException {
 217  22
         return getSuppressionLoader(source, sourceName).filterChain;
 218  
     }
 219  
 
 220  
     /**
 221  
      * Returns the suppression {@code TreeWalker} filters in a specified file.
 222  
      * @param filename name of the suppressions file.
 223  
      * @return the set of xpath suppression elements specified in the file.
 224  
      * @throws CheckstyleException if an error occurs.
 225  
      */
 226  
     public static Set<TreeWalkerFilter> loadXpathSuppressions(String filename)
 227  
             throws CheckstyleException {
 228  
         // figure out if this is a File or a URL
 229  14
         final URI uri = CommonUtils.getUriByFilename(filename);
 230  13
         final InputSource source = new InputSource(uri.toString());
 231  13
         return loadXpathSuppressions(source, filename);
 232  
     }
 233  
 
 234  
     /**
 235  
      * Returns the suppression {@code TreeWalker} filters in a specified source.
 236  
      * @param source the source for the suppressions.
 237  
      * @param sourceName the name of the source.
 238  
      * @return the set of xpath suppression elements specified in source.
 239  
      * @throws CheckstyleException if an error occurs.
 240  
      */
 241  
     private static Set<TreeWalkerFilter> loadXpathSuppressions(
 242  
             InputSource source, String sourceName)
 243  
             throws CheckstyleException {
 244  13
         return getSuppressionLoader(source, sourceName).treeWalkerFilters;
 245  
     }
 246  
 
 247  
     /**
 248  
      * Parses specified source and returns the suppression loader.
 249  
      * @param source the source for the suppressions.
 250  
      * @param sourceName the name of the source.
 251  
      * @return the suppression loader
 252  
      * @throws CheckstyleException if an error occurs.
 253  
      */
 254  
     private static SuppressionsLoader getSuppressionLoader(InputSource source, String sourceName)
 255  
             throws CheckstyleException {
 256  
         try {
 257  35
             final SuppressionsLoader suppressionsLoader =
 258  
                 new SuppressionsLoader();
 259  35
             suppressionsLoader.parseInputSource(source);
 260  24
             return suppressionsLoader;
 261  
         }
 262  1
         catch (final FileNotFoundException ex) {
 263  1
             throw new CheckstyleException(UNABLE_TO_FIND_ERROR_MESSAGE + sourceName, ex);
 264  
         }
 265  8
         catch (final ParserConfigurationException | SAXException ex) {
 266  16
             final String message = String.format(Locale.ROOT, "Unable to parse %s - %s",
 267  8
                     sourceName, ex.getMessage());
 268  8
             throw new CheckstyleException(message, ex);
 269  
         }
 270  1
         catch (final IOException ex) {
 271  1
             throw new CheckstyleException("Unable to read " + sourceName, ex);
 272  
         }
 273  1
         catch (final NumberFormatException ex) {
 274  2
             final String message = String.format(Locale.ROOT, "Number format exception %s - %s",
 275  1
                     sourceName, ex.getMessage());
 276  1
             throw new CheckstyleException(message, ex);
 277  
         }
 278  
     }
 279  
 
 280  
     /**
 281  
      * Creates mapping between local resources and dtd ids.
 282  
      * @return map between local resources and dtd ids.
 283  
      */
 284  
     private static Map<String, String> createIdToResourceNameMap() {
 285  35
         final Map<String, String> map = new HashMap<>();
 286  35
         map.put(DTD_PUBLIC_ID_1_0, DTD_SUPPRESSIONS_NAME_1_0);
 287  35
         map.put(DTD_PUBLIC_ID_1_1, DTD_SUPPRESSIONS_NAME_1_1);
 288  35
         map.put(DTD_PUBLIC_ID_1_2, DTD_SUPPRESSIONS_NAME_1_2);
 289  35
         map.put(DTD_PUBLIC_ID_1_1_XPATH, DTD_SUPPRESSIONS_NAME_1_1_XPATH);
 290  35
         map.put(DTD_PUBLIC_ID_1_2_XPATH, DTD_SUPPRESSIONS_NAME_1_2_XPATH);
 291  35
         return map;
 292  
     }
 293  
 }