Coverage Report - com.puppycrawl.tools.checkstyle.checks.RegexpCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexpCheck
100%
61/61
97%
41/42
2.818
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.checks;
 20  
 
 21  
 import com.google.common.collect.Lists;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.FileContents;
 24  
 import java.util.List;
 25  
 import java.util.regex.Matcher;
 26  
 import java.util.regex.Pattern;
 27  
 
 28  
 /**
 29  
  * <p>
 30  
  * A check that makes sure that a specified pattern exists (or not) in the file.
 31  
  * </p>
 32  
  * <p>
 33  
  * An example of how to configure the check to make sure a copyright statement
 34  
  * is included in the file (but without requirements on where in the file
 35  
  * it should be):
 36  
  * </p>
 37  
  * <pre>
 38  
  * &lt;module name="RequiredRegexp"&gt;
 39  
  *    &lt;property name="format" value="This code is copyrighted"/&gt;
 40  
  * &lt;/module&gt;
 41  
  * </pre>
 42  
  * <p>
 43  
  * And to make sure the same statement appears at the beginning of the file.
 44  
  * </p>
 45  
  * <pre>
 46  
  * &lt;module name="RequiredRegexp"&gt;
 47  
  *    &lt;property name="format" value="\AThis code is copyrighted"/&gt;
 48  
  * &lt;/module&gt;
 49  
  * </pre>
 50  
  * @author Stan Quinn
 51  
  */
 52  
 public class RegexpCheck extends AbstractFormatCheck
 53  
 {
 54  
     /** Default duplicate limit */
 55  
     private static final int DEFAULT_DUPLICATE_LIMIT = -1;
 56  
 
 57  
     /** Default error report limit */
 58  
     private static final int DEFAULT_ERROR_LIMIT = 100;
 59  
 
 60  
     /** Error count exceeded message */
 61  
     private static final String ERROR_LIMIT_EXCEEDED_MESSAGE =
 62  
         "The error limit has been exceeded, "
 63  
         + "the check is aborting, there may be more unreported errors.";
 64  
 
 65  
     /** Custom message for report. */
 66  22
     private String mMessage = "";
 67  
 
 68  
     /** Ignore matches within comments? **/
 69  
     private boolean mIgnoreComments;
 70  
 
 71  
     /** Pattern illegal? */
 72  
     private boolean mIllegalPattern;
 73  
 
 74  
     /** Error report limit */
 75  22
     private int mErrorLimit = DEFAULT_ERROR_LIMIT;
 76  
 
 77  
     /** Disallow more than x duplicates? */
 78  
     private int mDuplicateLimit;
 79  
 
 80  
     /** Boolean to say if we should check for duplicates. */
 81  
     private boolean mCheckForDuplicates;
 82  
 
 83  
     /** Tracks number of matches made */
 84  
     private int mMatchCount;
 85  
 
 86  
     /** Tracks number of errors */
 87  
     private int mErrorCount;
 88  
 
 89  
     /** Relates StringBuffer positions to line # and column */
 90  22
     private final List<Integer[]> mCharacters = Lists.newArrayList();
 91  
 
 92  
     /** The mMatcher */
 93  
     private Matcher mMatcher;
 94  
 
 95  
     /**
 96  
      * Instantiates an new RegexpCheck.
 97  
      */
 98  
     public RegexpCheck()
 99  
     {
 100  22
         super("$^", Pattern.MULTILINE); // the empty language
 101  22
     }
 102  
 
 103  
     /**
 104  
      * Setter for message property.
 105  
      * @param aMessage custom message which should be used in report.
 106  
      */
 107  
     public void setMessage(String aMessage)
 108  
     {
 109  2
         mMessage = (aMessage == null) ? "" : aMessage;
 110  2
     }
 111  
 
 112  
     /**
 113  
      * Getter for message property.
 114  
      * I'm not sure if this gets used by anything outside,
 115  
      * I just included it because GenericIllegalRegexp had it,
 116  
      * it's being used in logMessage() so it's covered in EMMA.
 117  
      * @return custom message to be used in report.
 118  
      */
 119  
     public String getMessage()
 120  
     {
 121  16
         return mMessage;
 122  
     }
 123  
 
 124  
     /**
 125  
      * Sets if matches within comments should be ignored.
 126  
      * @param aIgnoreComments True if comments should be ignored.
 127  
      */
 128  
     public void setIgnoreComments(boolean aIgnoreComments)
 129  
     {
 130  10
         mIgnoreComments = aIgnoreComments;
 131  10
     }
 132  
 
 133  
     /**
 134  
      * Sets if pattern is illegal, otherwise pattern is required.
 135  
      * @param aIllegalPattern True if pattern is not allowed.
 136  
      */
 137  
     public void setIllegalPattern(boolean aIllegalPattern)
 138  
     {
 139  18
         mIllegalPattern = aIllegalPattern;
 140  18
     }
 141  
 
 142  
     /**
 143  
      * Sets the limit on the number of errors to report.
 144  
      * @param aErrorLimit the number of errors to report.
 145  
      */
 146  
     public void setErrorLimit(int aErrorLimit)
 147  
     {
 148  2
         mErrorLimit = aErrorLimit;
 149  2
     }
 150  
 
 151  
     /**
 152  
      * Sets the maximum number of instances of required pattern allowed.
 153  
      * @param aDuplicateLimit negative values mean no duplicate checking,
 154  
      * any positive value is used as the limit.
 155  
      */
 156  
     public void setDuplicateLimit(int aDuplicateLimit)
 157  
     {
 158  2
         mDuplicateLimit = aDuplicateLimit;
 159  2
         mCheckForDuplicates = (mDuplicateLimit > DEFAULT_DUPLICATE_LIMIT);
 160  2
     }
 161  
 
 162  
     @Override
 163  
     public int[] getDefaultTokens()
 164  
     {
 165  22
         return new int[0];
 166  
     }
 167  
 
 168  
     @Override
 169  
     public void beginTree(DetailAST aRootAST)
 170  
     {
 171  22
         mCharacters.clear();
 172  22
         final Pattern pattern = getRegexp();
 173  22
         final String[] lines = getLines();
 174  22
         final StringBuffer sb = new StringBuffer();
 175  2666
         for (int i = 0; i < lines.length; i++) {
 176  2644
             sb.append(lines[i]);
 177  2644
             sb.append('\n');
 178  64748
             for (int j = 0; j < (lines[i].length() + 1); j++) {
 179  62104
                 mCharacters.add(new Integer[] {i + 1, j});
 180  
             }
 181  
         }
 182  22
         mMatcher = pattern.matcher(sb.toString());
 183  22
         mMatchCount = 0;
 184  22
         mErrorCount = 0;
 185  22
         findMatch();
 186  22
     }
 187  
 
 188  
     /** recursive method that finds the matches. */
 189  
     private void findMatch()
 190  
     {
 191  
         int startLine;
 192  
         int startColumn;
 193  
         int endLine;
 194  
         int endColumn;
 195  
         boolean foundMatch;
 196  44
         boolean ignore = false;
 197  
 
 198  44
         foundMatch = mMatcher.find();
 199  44
         if (!foundMatch && !mIllegalPattern && (mMatchCount == 0)) {
 200  1
             logMessage(0);
 201  
         }
 202  43
         else if (foundMatch) {
 203  24
             startLine = (mCharacters.get(mMatcher.start()))[0].
 204  
                     intValue();
 205  24
             startColumn = (mCharacters.get(mMatcher.start()))[1].
 206  
                     intValue();
 207  24
             endLine = (mCharacters.get(mMatcher.end() - 1))[0].
 208  
                     intValue();
 209  24
             endColumn = (mCharacters.get(mMatcher.end() - 1))[1].
 210  
                     intValue();
 211  24
             if (mIgnoreComments) {
 212  8
                 final FileContents theFileContents = getFileContents();
 213  8
                 ignore = theFileContents.hasIntersectionWithComment(startLine,
 214  
                     startColumn, endLine, endColumn);
 215  
             }
 216  24
             if (!ignore) {
 217  18
                 mMatchCount++;
 218  18
                 if (mIllegalPattern || (mCheckForDuplicates
 219  
                         && ((mMatchCount - 1) > mDuplicateLimit)))
 220  
                 {
 221  15
                     mErrorCount++;
 222  15
                     logMessage(startLine);
 223  
                 }
 224  
             }
 225  24
             if ((mErrorCount < mErrorLimit)
 226  
                     && (ignore || mIllegalPattern || mCheckForDuplicates))
 227  
             {
 228  22
                 findMatch();
 229  
             }
 230  
         }
 231  44
     }
 232  
 
 233  
     /**
 234  
      * Displays the right message.
 235  
      * @param aLineNumber the line number the message relates to.
 236  
      */
 237  
     private void logMessage(int aLineNumber)
 238  
     {
 239  16
         String message = "".equals(getMessage()) ? getFormat() : mMessage;
 240  16
         if (mErrorCount >= mErrorLimit) {
 241  1
             message = ERROR_LIMIT_EXCEEDED_MESSAGE + message;
 242  
         }
 243  16
         if (mIllegalPattern) {
 244  14
             log(aLineNumber, "illegal.regexp", message);
 245  
         }
 246  
         else {
 247  2
             if (aLineNumber > 0) {
 248  1
                 log(aLineNumber, "duplicate.regexp", message);
 249  
             }
 250  
             else {
 251  1
                 log(aLineNumber, "required.regexp", message);
 252  
             }
 253  
         }
 254  16
     }
 255  
 }
 256