blob: f08a2bd5a905c5496cdafaf1b536f94a36e8b722 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
// //////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2005 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.google.gwt.checkstyle;
import com.puppycrawl.tools.checkstyle.checks.header.RegexpHeaderCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.Utils;
import org.apache.commons.beanutils.ConversionException;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Custom version of {@link RegexpHeaderCheck} that has hooks for a custom log handler (see
* {@link CustomLogHandler}).
* <p>
* This is an exact copy of {@link RegexpHeaderCheck} with three exceptions:
* <ol>
* <li>{@link CustomLogHandler} has been added for custom log callbacks.</li>
* <li>{@link #doChecks(CustomLogHandler)} has been added for custom checks. This method is an exact
* copy of {@link RegexpHeaderCheck#beginTree(DetailAST)} except all log calls have been replaced
* with a call to a custom log handler.</li>
* <li>{@link #beginTree(DetailAST)} has been refactored to call
* {@link #doChecks(CustomLogHandler)}.
* </ol>
*/
public class CustomRegexpHeaderCheck extends RegexpHeaderCheck {
/**
* Custom log handler callback.
*/
abstract static class CustomLogHandler {
abstract void log(int aLine, String aKey);
abstract void log(int aLine, String aKey, Object aObject);
}
// empty array to avoid instantiations.
private static final int[] EMPTY_INT_ARRAY = new int[0];
// the header lines to repeat (0 or more) in the check, sorted.
private int[] mMultiLines = EMPTY_INT_ARRAY;
// the compiled regular expressions
private Pattern[] mHeaderRegexps;
/**
* {@inheritDoc}
*/
public void beginTree(DetailAST aRootAST) {
doChecks(new CustomLogHandler() {
@Override
void log(int aLine, String aKey) {
CustomRegexpHeaderCheck.this.log(aLine, aKey);
}
@Override
void log(int aLine, String aKey, Object aObject) {
CustomRegexpHeaderCheck.this.log(aLine, aKey, aObject);
}
});
}
/**
* Check the current file using the same method as {@link RegexpHeaderCheck#beginTree(DetailAST)}
* but pass all logging calls through a custom log handler (@see {@link CustomLogHandler}).
*
* @param logHandler the custom log handler, or <code>null</code> to suppress logging.
*/
public void doChecks(CustomLogHandler logHandler) {
// With the exception of the logging hooks, the following is copied from
// RegexpHeaderCheck.beginTree().
final int headerSize = getHeaderLines().length;
final int fileSize = getLines().length;
if (headerSize - mMultiLines.length > fileSize) {
if (logHandler != null) {
logHandler.log(1, "gwtheader.missing", null);
}
} else {
int headerLineNo = 0;
int i;
for (i = 0; (headerLineNo < headerSize) && (i < fileSize); i++) {
boolean isMatch = isMatch(i, headerLineNo);
while (!isMatch && isMultiLine(headerLineNo)) {
headerLineNo++;
isMatch = (headerLineNo == headerSize) || isMatch(i, headerLineNo);
}
if (!isMatch) {
if (logHandler != null) {
logHandler.log(i + 1, "gwtheader.mismatch", getHeaderLines()[headerLineNo]);
}
break; // stop checking
}
if (!isMultiLine(headerLineNo)) {
headerLineNo++;
}
}
if (i == fileSize) {
// if file finished, but we have at least one non-multi-line
// header isn't completed
for (; headerLineNo < headerSize; headerLineNo++) {
if (!isMultiLine(headerLineNo)) {
if (logHandler != null) {
logHandler.log(1, "gwtheader.missing");
}
break;
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void setHeader(String aHeader) {
super.setHeader(aHeader);
initHeaderRegexps();
}
/**
* {@inheritDoc}
*/
@Override
public void setHeaderFile(String aFileName) throws ConversionException {
super.setHeaderFile(aFileName);
initHeaderRegexps();
}
/**
* {@inheritDoc}
*/
@Override
public void setMultiLines(int[] aList) {
if ((aList == null) || (aList.length == 0)) {
mMultiLines = EMPTY_INT_ARRAY;
return;
}
mMultiLines = new int[aList.length];
System.arraycopy(aList, 0, mMultiLines, 0, aList.length);
Arrays.sort(mMultiLines);
}
/**
* Initializes {@link #mHeaderRegexps} from {@link AbstractHeaderCheck#getHeaderLines()}.
*/
private void initHeaderRegexps() {
final String[] headerLines = getHeaderLines();
if (headerLines != null) {
mHeaderRegexps = new Pattern[headerLines.length];
for (int i = 0; i < headerLines.length; i++) {
try {
// todo: Not sure if chache in Utils is still necessary
mHeaderRegexps[i] = Utils.getPattern(headerLines[i]);
} catch (final PatternSyntaxException ex) {
throw new ConversionException("line " + i + " in header specification"
+ " is not a regular expression");
}
}
}
}
/**
* Checks if a code line matches the required header line.
*
* @param aLineNo the line number to check against the header
* @param aHeaderLineNo the header line number.
* @return true if and only if the line matches the required header line.
*/
private boolean isMatch(int aLineNo, int aHeaderLineNo) {
final String line = getLines()[aLineNo];
return mHeaderRegexps[aHeaderLineNo].matcher(line).find();
}
/**
* @param aLineNo a line number
* @return if <code>aLineNo</code> is one of the repeat header lines.
*/
private boolean isMultiLine(int aLineNo) {
return (Arrays.binarySearch(mMultiLines, aLineNo + 1) >= 0);
}
}