////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2014 Oliver Burn
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks;
import com.google.common.collect.Lists;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>
* A check that makes sure that a specified pattern exists (or not) in the file.
* </p>
* <p>
* An example of how to configure the check to make sure a copyright statement
* is included in the file (but without requirements on where in the file
* it should be):
* </p>
* <pre>
* <module name="RequiredRegexp">
* <property name="format" value="This code is copyrighted"/>
* </module>
* </pre>
* <p>
* And to make sure the same statement appears at the beginning of the file.
* </p>
* <pre>
* <module name="RequiredRegexp">
* <property name="format" value="\AThis code is copyrighted"/>
* </module>
* </pre>
* @author Stan Quinn
*/
public class RegexpCheck extends AbstractFormatCheck
{
/** Default duplicate limit */
private static final int DEFAULT_DUPLICATE_LIMIT = -1;
/** Default error report limit */
private static final int DEFAULT_ERROR_LIMIT = 100;
/** Error count exceeded message */
private static final String ERROR_LIMIT_EXCEEDED_MESSAGE =
"The error limit has been exceeded, "
+ "the check is aborting, there may be more unreported errors.";
/** Custom message for report. */
private String mMessage = "";
/** Ignore matches within comments? **/
private boolean mIgnoreComments;
/** Pattern illegal? */
private boolean mIllegalPattern;
/** Error report limit */
private int mErrorLimit = DEFAULT_ERROR_LIMIT;
/** Disallow more than x duplicates? */
private int mDuplicateLimit;
/** Boolean to say if we should check for duplicates. */
private boolean mCheckForDuplicates;
/** Tracks number of matches made */
private int mMatchCount;
/** Tracks number of errors */
private int mErrorCount;
/** Relates StringBuffer positions to line # and column */
private final List<Integer[]> mCharacters = Lists.newArrayList();
/** The mMatcher */
private Matcher mMatcher;
/**
* Instantiates an new RegexpCheck.
*/
public RegexpCheck()
{
super("$^", Pattern.MULTILINE); // the empty language
}
/**
* Setter for message property.
* @param aMessage custom message which should be used in report.
*/
public void setMessage(String aMessage)
{
mMessage = (aMessage == null) ? "" : aMessage;
}
/**
* Getter for message property.
* I'm not sure if this gets used by anything outside,
* I just included it because GenericIllegalRegexp had it,
* it's being used in logMessage() so it's covered in EMMA.
* @return custom message to be used in report.
*/
public String getMessage()
{
return mMessage;
}
/**
* Sets if matches within comments should be ignored.
* @param aIgnoreComments True if comments should be ignored.
*/
public void setIgnoreComments(boolean aIgnoreComments)
{
mIgnoreComments = aIgnoreComments;
}
/**
* Sets if pattern is illegal, otherwise pattern is required.
* @param aIllegalPattern True if pattern is not allowed.
*/
public void setIllegalPattern(boolean aIllegalPattern)
{
mIllegalPattern = aIllegalPattern;
}
/**
* Sets the limit on the number of errors to report.
* @param aErrorLimit the number of errors to report.
*/
public void setErrorLimit(int aErrorLimit)
{
mErrorLimit = aErrorLimit;
}
/**
* Sets the maximum number of instances of required pattern allowed.
* @param aDuplicateLimit negative values mean no duplicate checking,
* any positive value is used as the limit.
*/
public void setDuplicateLimit(int aDuplicateLimit)
{
mDuplicateLimit = aDuplicateLimit;
mCheckForDuplicates = (mDuplicateLimit > DEFAULT_DUPLICATE_LIMIT);
}
@Override
public int[] getDefaultTokens()
{
return new int[0];
}
@Override
public void beginTree(DetailAST aRootAST)
{
mCharacters.clear();
final Pattern pattern = getRegexp();
final String[] lines = getLines();
final StringBuffer sb = new StringBuffer();
for (int i = 0; i < lines.length; i++) {
sb.append(lines[i]);
sb.append('\n');
for (int j = 0; j < (lines[i].length() + 1); j++) {
mCharacters.add(new Integer[] {i + 1, j});
}
}
mMatcher = pattern.matcher(sb.toString());
mMatchCount = 0;
mErrorCount = 0;
findMatch();
}
/** recursive method that finds the matches. */
private void findMatch()
{
int startLine;
int startColumn;
int endLine;
int endColumn;
boolean foundMatch;
boolean ignore = false;
foundMatch = mMatcher.find();
if (!foundMatch && !mIllegalPattern && (mMatchCount == 0)) {
logMessage(0);
}
else if (foundMatch) {
startLine = (mCharacters.get(mMatcher.start()))[0].
intValue();
startColumn = (mCharacters.get(mMatcher.start()))[1].
intValue();
endLine = (mCharacters.get(mMatcher.end() - 1))[0].
intValue();
endColumn = (mCharacters.get(mMatcher.end() - 1))[1].
intValue();
if (mIgnoreComments) {
final FileContents theFileContents = getFileContents();
ignore = theFileContents.hasIntersectionWithComment(startLine,
startColumn, endLine, endColumn);
}
if (!ignore) {
mMatchCount++;
if (mIllegalPattern || (mCheckForDuplicates
&& ((mMatchCount - 1) > mDuplicateLimit)))
{
mErrorCount++;
logMessage(startLine);
}
}
if ((mErrorCount < mErrorLimit)
&& (ignore || mIllegalPattern || mCheckForDuplicates))
{
findMatch();
}
}
}
/**
* Displays the right message.
* @param aLineNumber the line number the message relates to.
*/
private void logMessage(int aLineNumber)
{
String message = "".equals(getMessage()) ? getFormat() : mMessage;
if (mErrorCount >= mErrorLimit) {
message = ERROR_LIMIT_EXCEEDED_MESSAGE + message;
}
if (mIllegalPattern) {
log(aLineNumber, "illegal.regexp", message);
}
else {
if (aLineNumber > 0) {
log(aLineNumber, "duplicate.regexp", message);
}
else {
log(aLineNumber, "required.regexp", message);
}
}
}
}