Coverage Report - com.puppycrawl.tools.checkstyle.checks.regexp.RegexpOnFilenameCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexpOnFilenameCheck
100%
42/42
100%
26/26
2.083
 
 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.checks.regexp;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.util.regex.Pattern;
 25  
 
 26  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 29  
 import com.puppycrawl.tools.checkstyle.api.FileText;
 30  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 31  
 
 32  
 /**
 33  
  * <p>
 34  
  * Implementation of a check that looks for a file name and/or path match (or
 35  
  * mis-match) against specified patterns. It can also be used to verify files
 36  
  * match specific naming patterns not covered by other checks (Ex: properties,
 37  
  * xml, etc.).
 38  
  * </p>
 39  
  *
 40  
  * <p>
 41  
  * When customizing the check, the properties are applied in a specific order.
 42  
  * The fileExtensions property first picks only files that match any of the
 43  
  * specific extensions supplied. Once files are matched against the
 44  
  * fileExtensions, the match property is then used in conjunction with the
 45  
  * patterns to determine if the check is looking for a match or mis-match on
 46  
  * those files. If the fileNamePattern is supplied, the matching is only applied
 47  
  * to the fileNamePattern and not the folderPattern. If no fileNamePattern is
 48  
  * supplied, then matching is applied to the folderPattern only and will result
 49  
  * in all files in a folder to be reported on violations. If no folderPattern is
 50  
  * supplied, then all folders that checkstyle finds are examined for violations.
 51  
  * The ignoreFileNameExtensions property drops the file extension and applies
 52  
  * the fileNamePattern only to the rest of file name. For example, if the file
 53  
  * is named 'test.java' and this property is turned on, the pattern is only
 54  
  * applied to 'test'.
 55  
  * </p>
 56  
  *
 57  
  * <p>
 58  
  * If this check is configured with no properties, then the default behavior of
 59  
  * this check is to report file names with spaces in them. When at least one
 60  
  * pattern property is supplied, the entire check is under the user's control to
 61  
  * allow them to fully customize the behavior.
 62  
  * </p>
 63  
  *
 64  
  * <p>
 65  
  * It is recommended that if you create your own pattern, to also specify a
 66  
  * custom error message. This allows the error message printed to be clear what
 67  
  * the violation is, especially if multiple RegexpOnFilename checks are used.
 68  
  * Argument 0 for the message populates the check's folderPattern. Argument 1
 69  
  * for the message populates the check's fileNamePattern. The file name is not
 70  
  * passed as an argument since it is part of CheckStyle's default error
 71  
  * messages.
 72  
  * </p>
 73  
  *
 74  
  * <p>
 75  
  * Check have following options:
 76  
  * </p>
 77  
  * <ul>
 78  
  * <li>
 79  
  * folderPattern - Regular expression to match the folder path against. Default
 80  
  * value is null.</li>
 81  
  *
 82  
  * <li>
 83  
  * fileNamePattern - Regular expression to match the file name against. Default
 84  
  * value is null.</li>
 85  
  *
 86  
  * <li>
 87  
  * match - Whether to look for a match or mis-match on the file name, if the
 88  
  * fileNamePattern is supplied, otherwise it is applied on the folderPattern.
 89  
  * Default value is true.</li>
 90  
  *
 91  
  * <li>
 92  
  * ignoreFileNameExtensions - Whether to ignore the file extension for the file
 93  
  * name match. Default value is false.</li>
 94  
  *
 95  
  * <li>
 96  
  * fileExtensions - File type extension of files to process. If this is
 97  
  * specified, then only files that match these types are examined with the other
 98  
  * patterns. Default value is {}.</li>
 99  
  * </ul>
 100  
  * <br>
 101  
  *
 102  
  * <p>
 103  
  * To configure the check to report file names that contain a space:
 104  
  * </p>
 105  
  *
 106  
  * <pre>
 107  
  * &lt;module name=&quot;RegexpOnFilename&quot;/&gt;
 108  
  * </pre>
 109  
  * <p>
 110  
  * To configure the check to force picture files to not be 'gif':
 111  
  * </p>
 112  
  *
 113  
  * <pre>
 114  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 115  
  *   &lt;property name=&quot;fileNamePattern&quot; value=&quot;\\.gif$&quot;/&gt;
 116  
  * &lt;/module&gt;
 117  
  * </pre>
 118  
  * <p>
 119  
  * OR:
 120  
  * </p>
 121  
  *
 122  
  * <pre>
 123  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 124  
  *   &lt;property name=&quot;fileNamePattern&quot; value=&quot;.&quot;/&gt;
 125  
  *   &lt;property name=&quot;fileExtensions&quot; value=&quot;gif&quot;/&gt;
 126  
  * &lt;/module&gt;
 127  
  * </pre>
 128  
  *
 129  
  * <p>
 130  
  * To configure the check to only allow property and xml files to be located in
 131  
  * the resource folder:
 132  
  * </p>
 133  
  *
 134  
  * <pre>
 135  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 136  
  *   &lt;property name=&quot;folderPattern&quot;
 137  
  *     value=&quot;[\\/]src[\\/]\\w+[\\/]resources[\\/]&quot;/&gt;
 138  
  *   &lt;property name=&quot;match&quot; value=&quot;false&quot;/&gt;
 139  
  *   &lt;property name=&quot;fileExtensions&quot; value=&quot;properties, xml&quot;/&gt;
 140  
  * &lt;/module&gt;
 141  
  * </pre>
 142  
  *
 143  
  * <p>
 144  
  * To configure the check to only allow Java and XML files in your folders use
 145  
  * the below.
 146  
  * </p>
 147  
  *
 148  
  * <pre>
 149  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 150  
  *   &lt;property name=&quot;fileNamePattern&quot; value=&quot;\\.(java|xml)$&quot;/&gt;
 151  
  *   &lt;property name=&quot;match&quot; value=&quot;false&quot;/&gt;
 152  
  * &lt;/module&gt;
 153  
  * </pre>
 154  
  * <p>
 155  
  * To configure the check to only allow Java and XML files only in your source
 156  
  * folder and ignore any other folders:
 157  
  * </p>
 158  
  *
 159  
  * <p>
 160  
  * <b>Note:</b> 'folderPattern' must be specified if checkstyle is analyzing
 161  
  * more than the normal source folder, like the 'bin' folder where class files
 162  
  * can be located.
 163  
  * </p>
 164  
  *
 165  
  * <pre>
 166  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 167  
  *   &lt;property name=&quot;folderPattern&quot; value=&quot;[\\/]src[\\/]&quot;/&gt;
 168  
  *   &lt;property name=&quot;fileNamePattern&quot; value=&quot;\\.(java|xml)$&quot;/&gt;
 169  
  *   &lt;property name=&quot;match&quot; value=&quot;false&quot;/&gt;
 170  
  * &lt;/module&gt;
 171  
  * </pre>
 172  
  * <p>
 173  
  * To configure the check to only allow file names to be camel case:
 174  
  * </p>
 175  
  *
 176  
  * <pre>
 177  
  * &lt;module name=&quot;RegexpOnFilename&quot;&gt;
 178  
  *   &lt;property name=&quot;fileNamePattern&quot;
 179  
  *     value=&quot;^([A-Z][a-z0-9]+\.?)+$&quot;/&gt;
 180  
  *   &lt;property name=&quot;match&quot; value=&quot;false&quot;/&gt;
 181  
  *   &lt;property name=&quot;ignoreFileNameExtensions&quot; value=&quot;true&quot;/&gt;
 182  
  * &lt;/module&gt;
 183  
  * </pre>
 184  
  *
 185  
  * @author Richard Veach
 186  
  */
 187  
 @StatelessCheck
 188  30
 public class RegexpOnFilenameCheck extends AbstractFileSetCheck {
 189  
     /**
 190  
      * A key is pointing to the warning message text in "messages.properties"
 191  
      * file.
 192  
      */
 193  
     public static final String MSG_MATCH = "regexp.filename.match";
 194  
     /**
 195  
      * A key is pointing to the warning message text in "messages.properties"
 196  
      * file.
 197  
      */
 198  
     public static final String MSG_MISMATCH = "regexp.filename.mismatch";
 199  
 
 200  
     /** Compiled regexp to match a folder. */
 201  
     private Pattern folderPattern;
 202  
     /** Compiled regexp to match a file. */
 203  
     private Pattern fileNamePattern;
 204  
     /** Whether to look for a file name match or mismatch. */
 205  30
     private boolean match = true;
 206  
     /** Whether to ignore the file's extension when looking for matches. */
 207  
     private boolean ignoreFileNameExtensions;
 208  
 
 209  
     /**
 210  
      * Setter for folder format.
 211  
      *
 212  
      * @param folderPattern format of folder.
 213  
      */
 214  
     public void setFolderPattern(Pattern folderPattern) {
 215  13
         this.folderPattern = folderPattern;
 216  13
     }
 217  
 
 218  
     /**
 219  
      * Setter for file name format.
 220  
      *
 221  
      * @param fileNamePattern format of file.
 222  
      */
 223  
     public void setFileNamePattern(Pattern fileNamePattern) {
 224  19
         this.fileNamePattern = fileNamePattern;
 225  19
     }
 226  
 
 227  
     /**
 228  
      * Sets whether the check should look for a file name match or mismatch.
 229  
      *
 230  
      * @param match check's option for matching file names.
 231  
      */
 232  
     public void setMatch(boolean match) {
 233  19
         this.match = match;
 234  19
     }
 235  
 
 236  
     /**
 237  
      * Sets whether file name matching should drop the file extension or not.
 238  
      *
 239  
      * @param ignoreFileNameExtensions check's option for ignoring file extension.
 240  
      */
 241  
     public void setIgnoreFileNameExtensions(boolean ignoreFileNameExtensions) {
 242  3
         this.ignoreFileNameExtensions = ignoreFileNameExtensions;
 243  3
     }
 244  
 
 245  
     @Override
 246  
     public void init() {
 247  28
         if (fileNamePattern == null && folderPattern == null) {
 248  5
             fileNamePattern = CommonUtils.createPattern("\\s");
 249  
         }
 250  28
     }
 251  
 
 252  
     @Override
 253  
     protected void processFiltered(File file, FileText fileText) throws CheckstyleException {
 254  22
         final String fileName = getFileName(file);
 255  22
         final String folderPath = getFolderPath(file);
 256  
 
 257  21
         if (isMatchFolder(folderPath) && isMatchFile(fileName)) {
 258  7
             log();
 259  
         }
 260  21
     }
 261  
 
 262  
     /**
 263  
      * Retrieves the file name from the given {@code file}.
 264  
      *
 265  
      * @param file Input file to examine.
 266  
      * @return The file name.
 267  
      */
 268  
     private String getFileName(File file) {
 269  22
         String fileName = file.getName();
 270  
 
 271  22
         if (ignoreFileNameExtensions) {
 272  2
             fileName = CommonUtils.getFileNameWithoutExtension(fileName);
 273  
         }
 274  
 
 275  22
         return fileName;
 276  
     }
 277  
 
 278  
     /**
 279  
      * Retrieves the folder path from the given {@code file}.
 280  
      *
 281  
      * @param file Input file to examine.
 282  
      * @return The folder path.
 283  
      * @throws CheckstyleException if there is an error getting the canonical
 284  
      *         path of the {@code file}.
 285  
      */
 286  
     private static String getFolderPath(File file) throws CheckstyleException {
 287  
         try {
 288  22
             return file.getCanonicalFile().getParent();
 289  
         }
 290  1
         catch (IOException ex) {
 291  1
             throw new CheckstyleException("unable to create canonical path names for "
 292  1
                     + file.getAbsolutePath(), ex);
 293  
         }
 294  
     }
 295  
 
 296  
     /**
 297  
      * Checks if the given {@code folderPath} matches the specified
 298  
      * {@link #folderPattern}.
 299  
      *
 300  
      * @param folderPath Input folder path to examine.
 301  
      * @return true if they do match.
 302  
      */
 303  
     private boolean isMatchFolder(String folderPath) {
 304  
         final boolean result;
 305  
 
 306  
         // null pattern always matches, regardless of value of 'match'
 307  21
         if (folderPattern == null) {
 308  10
             result = true;
 309  
         }
 310  
         else {
 311  
             // null pattern means 'match' applies to the folderPattern matching
 312  11
             final boolean useMatch = fileNamePattern != null || match;
 313  11
             result = folderPattern.matcher(folderPath).find() == useMatch;
 314  
         }
 315  
 
 316  21
         return result;
 317  
     }
 318  
 
 319  
     /**
 320  
      * Checks if the given {@code fileName} matches the specified
 321  
      * {@link #fileNamePattern}.
 322  
      *
 323  
      * @param fileName Input file name to examine.
 324  
      * @return true if they do match.
 325  
      */
 326  
     private boolean isMatchFile(String fileName) {
 327  
         // null pattern always matches, regardless of value of 'match'
 328  15
         return fileNamePattern == null || fileNamePattern.matcher(fileName).find() == match;
 329  
     }
 330  
 
 331  
     /** Logs the errors for the check. */
 332  
     private void log() {
 333  7
         final String folder = getStringOrDefault(folderPattern, "");
 334  7
         final String fileName = getStringOrDefault(fileNamePattern, "");
 335  
 
 336  7
         if (match) {
 337  4
             log(0, MSG_MATCH, folder, fileName);
 338  
         }
 339  
         else {
 340  3
             log(0, MSG_MISMATCH, folder, fileName);
 341  
         }
 342  7
     }
 343  
 
 344  
     /**
 345  
      * Retrieves the String form of the {@code pattern} or {@code defaultString}
 346  
      * if null.
 347  
      *
 348  
      * @param pattern The pattern to convert.
 349  
      * @param defaultString The result to use if {@code pattern} is null.
 350  
      * @return The String form of the {@code pattern}.
 351  
      */
 352  
     private static String getStringOrDefault(Pattern pattern, String defaultString) {
 353  
         final String result;
 354  
 
 355  14
         if (pattern == null) {
 356  5
             result = defaultString;
 357  
         }
 358  
         else {
 359  9
             result = pattern.toString();
 360  
         }
 361  
 
 362  14
         return result;
 363  
     }
 364  
 }