| /* |
| * 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); |
| } |
| } |