From 59222296258fecc02db44eeae1cb65b44b68c267 Mon Sep 17 00:00:00 2001 From: Oliver Burn Date: Thu, 24 Oct 2002 14:33:31 +0000 Subject: [PATCH] Implemented the first Javadoc test. Still need to add some Javadoc (no pun intended) and also refactor the utility methods. --- .../puppycrawl/tools/checkstyle/Verifier.java | 43 +---- .../tools/checkstyle/api/FileContents.java | 53 ++++++ .../tools/checkstyle/api/Utils.java | 23 +++ .../checks/JavadocVariableCheck.java | 169 ++++++++++++++++++ .../tools/checkstyle/CheckerTest.java | 19 -- .../checkstyle/JavadocVariableCheckTest.java | 86 +++++++++ 6 files changed, 338 insertions(+), 55 deletions(-) create mode 100644 src/checkstyle/com/puppycrawl/tools/checkstyle/checks/JavadocVariableCheck.java create mode 100644 src/tests/com/puppycrawl/tools/checkstyle/JavadocVariableCheckTest.java diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/Verifier.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/Verifier.java index 2489c5a2b..67408a837 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/Verifier.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/Verifier.java @@ -50,7 +50,7 @@ class Verifier private static final String MATCH_JAVADOC_ARG_PAT = "@(throws|exception|param)\\s+(\\S+)\\s+\\S"; /** compiled regexp to match Javadoc tags that take an argument **/ - private static final RE MATCH_JAVADOC_ARG = createRE(MATCH_JAVADOC_ARG_PAT); + private static final RE MATCH_JAVADOC_ARG = Utils.createRE(MATCH_JAVADOC_ARG_PAT); /** * the pattern to match a single line comment containing only the comment @@ -60,7 +60,7 @@ class Verifier = "^\\s*//.*$"; /** compiled regexp to match a single-line comment line **/ private static final RE MATCH_SINGLELINE_COMMENT = - createRE(MATCH_SINGLELINE_COMMENT_PAT); + Utils.createRE(MATCH_SINGLELINE_COMMENT_PAT); /** * the pattern to match the first line of a multi-line Javadoc @@ -71,14 +71,14 @@ class Verifier = "@(throws|exception|param)\\s+(\\S+)\\s*$"; /** compiled regexp to match first part of multilineJavadoc tags **/ private static final RE MATCH_JAVADOC_MULTILINE_START = - createRE(MATCH_JAVADOC_MULTILINE_START_PAT); + Utils.createRE(MATCH_JAVADOC_MULTILINE_START_PAT); /** the pattern that looks for a continuation of the comment **/ private static final String MATCH_JAVADOC_MULTILINE_CONT_PAT = "(\\*/|@|[^\\s\\*])"; /** compiled regexp to look for a continuation of the comment **/ private static final RE MATCH_JAVADOC_MULTILINE_CONT = - createRE(MATCH_JAVADOC_MULTILINE_CONT_PAT); + Utils.createRE(MATCH_JAVADOC_MULTILINE_CONT_PAT); /** Multiline finished at end of comment **/ private static final String END_JAVADOC = "*/"; /** Multiline finished at next Javadoc **/ @@ -89,19 +89,19 @@ class Verifier = "@(return|see|author)\\s+\\S"; /** compiled regexp to match Javadoc tags with no argument **/ private static final RE MATCH_JAVADOC_NOARG - = createRE(MATCH_JAVADOC_NOARG_PAT); + = Utils.createRE(MATCH_JAVADOC_NOARG_PAT); /** the pattern to match author tag **/ private static final String MATCH_JAVADOC_AUTHOR_PAT = "@author\\s+\\S"; /** compiled regexp to match author tag **/ private static final RE MATCH_JAVADOC_AUTHOR - = createRE(MATCH_JAVADOC_AUTHOR_PAT); + = Utils.createRE(MATCH_JAVADOC_AUTHOR_PAT); /** the pattern to match version tag **/ private static final String MATCH_JAVADOC_VERSION_PAT = "@version\\s+\\S"; /** compiled regexp to match version tag **/ private static final RE MATCH_JAVADOC_VERSION - = createRE(MATCH_JAVADOC_VERSION_PAT); + = Utils.createRE(MATCH_JAVADOC_VERSION_PAT); //////////////////////////////////////////////////////////////////////////// // Member variables @@ -315,13 +315,6 @@ class Verifier final Scope variableScope = inInterfaceBlock() ? Scope.PUBLIC : declaredScope; - if (inCheckScope(variableScope) - && (getJavadocBefore(aVar.getStartLineNo() - 1) == null)) - { - mMessages.add(aVar.getLineNo(), aVar.getColumnNo() - 1, - "javadoc.missing"); - } - // Check correct format if (inInterfaceBlock()) { // The only declarations allowed in interfaces are all static final, @@ -883,28 +876,6 @@ class Verifier } - - /** - * Helper method to create a regular expression. Will exit if unable to - * create the object. - * @param aPattern the pattern to match - * @return a created regexp object - **/ - private static RE createRE(String aPattern) - { - RE retVal = null; - try { - retVal = Utils.getRE(aPattern); - } - catch (RESyntaxException e) { - System.out.println("Failed to initialise regexp expression " - + aPattern); - e.printStackTrace(System.out); - System.exit(1); - } - return retVal; - } - /** * Checks that a variable confirms to a specified regular expression. Logs * a message if it does not. diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/api/FileContents.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/api/FileContents.java index ad017b072..439385b63 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/api/FileContents.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/api/FileContents.java @@ -1,10 +1,22 @@ package com.puppycrawl.tools.checkstyle.api; +import org.apache.regexp.RE; + import java.util.Map; import java.util.HashMap; public class FileContents { + /** + * the pattern to match a single line comment containing only the comment + * itself -- no code. + */ + private static final String MATCH_SINGLELINE_COMMENT_PAT + = "^\\s*//.*$"; + /** compiled regexp to match a single-line comment line */ + private static final RE MATCH_SINGLELINE_COMMENT = + Utils.createRE(MATCH_SINGLELINE_COMMENT_PAT); + /** the file name */ private final String mFilename; @@ -84,6 +96,24 @@ public class FileContents return retVal; } + /** + * Returns the Javadoc comment before the specified line. null is none. + * @return the Javadoc comment, or null if none + * @param aLineNo the line number to check before + **/ + public String[] getJavadocBefore(int aLineNo) + { + // Lines start at 1 to the callers perspective, so nee to take off 2 + int lineNo = aLineNo - 2; + + // skip blank lines + while ((lineNo > 0) && (lineIsBlank(lineNo) || lineIsComment(lineNo))) { + lineNo--; + } + + return (String[]) mJavadocComments.get(new Integer(lineNo)); + } + public String[] getLines() { return mLines; @@ -93,4 +123,27 @@ public class FileContents { return mFilename; } + + /** + * Checks if the specified line is blank. + * @param aLineNo the line number to check + * @return if the specified line consists only of tabs and spaces. + **/ + private boolean lineIsBlank(int aLineNo) + { + // possible improvement: avoid garbage creation in trim() + return "".equals(mLines[aLineNo].trim()); + } + + /** + * Checks if the specified line is a single-line comment without code. + * @param aLineNo the line number to check + * @return if the specified line consists of only a single line comment + * without code. + **/ + private boolean lineIsComment(int aLineNo) + { + return MATCH_SINGLELINE_COMMENT.match(mLines[aLineNo]); + } + } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/api/Utils.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/api/Utils.java index a14a80110..386720c65 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/api/Utils.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/api/Utils.java @@ -30,6 +30,7 @@ import java.io.FileReader; import org.apache.regexp.RE; import org.apache.regexp.RESyntaxException; +import org.apache.commons.beanutils.ConversionException; import antlr.collections.AST; /** @@ -237,4 +238,26 @@ public final class Utils } return count; } + + /** + * Helper method to create a regular expression. + * @param aPattern the pattern to match + * @return a created regexp object + * @throws ConversionException if unable to create RE object. + **/ + public static RE createRE(String aPattern) + { + RE retVal = null; + try { + retVal = getRE(aPattern); + } + catch (RESyntaxException e) { + System.out.println("Failed to initialise regexp expression " + + aPattern); + e.printStackTrace(System.out); + throw new ConversionException( + "Failed to initialise regexp expression " + aPattern, e); + } + return retVal; + } } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/JavadocVariableCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/JavadocVariableCheck.java new file mode 100644 index 000000000..15e390513 --- /dev/null +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/JavadocVariableCheck.java @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2002 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.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.Utils; +import com.puppycrawl.tools.checkstyle.api.FileContents; +import com.puppycrawl.tools.checkstyle.Scope; +import antlr.collections.AST; + +/** + * Checks that a variable has Javadoc comment + * + * @author Oliver Burn + * @version 1.0 + */ +public class JavadocVariableCheck + extends Check +{ + private Scope mScope = Scope.PRIVATE; + + public void setScope(String aFrom) + { + mScope = Scope.getInstance(aFrom); + } + + /** @see com.puppycrawl.tools.checkstyle.api.Check */ + public int[] getDefaultTokens() + { + return new int[] {TokenTypes.VARIABLE_DEF}; + } + + /** @see com.puppycrawl.tools.checkstyle.api.Check */ + public void visitToken(DetailAST aAST) + { + if (!inCodeBlock(aAST)) { + final DetailAST mods = + Utils.findFirstToken(aAST.getFirstChild(), + TokenTypes.MODIFIERS); + final Scope declaredScope = getScopeFromMods(mods); + final Scope variableScope = + inInterfaceBlock(aAST) ? Scope.PUBLIC : declaredScope; + if (variableScope.isIn(mScope)) { + final Scope surroundingScope = getSurroundingScope(aAST); + if (surroundingScope.isIn(mScope)) { + final FileContents contents = getFileContents(); + final String[] cmt = + contents.getJavadocBefore(aAST.getLineNo()); + if (cmt == null) { + log(aAST.getLineNo(), aAST.getColumnNo(), + "javadoc.missing"); + } + } + } + } + } + + private boolean inCodeBlock(DetailAST aAST) + { + boolean retVal = false; + + // Loop up looking for a containing code block + for (DetailAST token = aAST.getParent(); + token != null; + token = token.getParent()) + { + final int type = token.getType(); + if ((type == TokenTypes.METHOD_DEF) + || (type == TokenTypes.CTOR_DEF) + || (type == TokenTypes.INSTANCE_INIT) + || (type == TokenTypes.STATIC_INIT)) + { + retVal = true; + break; + } + } + + return retVal; + } + + private boolean inInterfaceBlock(DetailAST aAST) + { + boolean retVal = false; + + // Loop up looking for a containing interface block + for (DetailAST token = aAST.getParent(); + token != null; + token = token.getParent()) + { + final int type = token.getType(); + if (type == TokenTypes.CLASS_DEF) { + break; // in a class + } + else if (type == TokenTypes.INTERFACE_DEF) { + retVal = true; + break; + } + } + + return retVal; + } + + private static Scope getSurroundingScope(DetailAST aAST) + { + Scope retVal = null; + for (DetailAST token = aAST.getParent(); + token != null; + token = token.getParent()) + { + final int type = token.getType(); + if ((type == TokenTypes.CLASS_DEF) + || (type == TokenTypes.INTERFACE_DEF)) + { + final DetailAST mods = + Utils.findFirstToken(token.getFirstChild(), + TokenTypes.MODIFIERS); + final Scope modScope = getScopeFromMods(mods); + if ((retVal == null) || (retVal.isIn(modScope))) { + retVal = modScope; + } + } + } + + return retVal; + } + + + private static Scope getScopeFromMods(DetailAST aMods) + { + Scope retVal = Scope.PACKAGE; // default scope + for (AST token = aMods.getFirstChild(); + token != null; + token = token.getNextSibling()) + { + if ("public".equals(token.getText())) { + retVal = Scope.PUBLIC; + break; + } + else if ("protected".equals(token.getText())) { + retVal = Scope.PROTECTED; + break; + } + else if ("private".equals(token.getText())) { + retVal = Scope.PRIVATE; + break; + } + } + + return retVal; + } +} diff --git a/src/tests/com/puppycrawl/tools/checkstyle/CheckerTest.java b/src/tests/com/puppycrawl/tools/checkstyle/CheckerTest.java index cffc3441e..83d5e556a 100644 --- a/src/tests/com/puppycrawl/tools/checkstyle/CheckerTest.java +++ b/src/tests/com/puppycrawl/tools/checkstyle/CheckerTest.java @@ -225,7 +225,6 @@ public class CheckerTest assertNotNull(c); final String[] expected = { filepath + ":8: Missing a Javadoc comment.", - filepath + ":11:17: Missing a Javadoc comment.", filepath + ":14:5: Missing a Javadoc comment.", filepath + ":18: Unused @param tag for 'unused'.", filepath + ":24: Expected an @return tag.", @@ -262,7 +261,6 @@ public class CheckerTest assertNotNull(c); final String[] expected = { filepath + ":8: Missing a Javadoc comment.", - filepath + ":11:17: Missing a Javadoc comment.", filepath + ":14:5: Missing a Javadoc comment.", filepath + ":18: Unused @param tag for 'unused'.", filepath + ":24: Expected an @return tag.", @@ -297,12 +295,9 @@ public class CheckerTest assertNotNull(c); final String[] expected = { filepath + ":14: Missing a Javadoc comment.", - filepath + ":17:20: Missing a Javadoc comment.", filepath + ":21: Missing a Javadoc comment.", - filepath + ":24:16: Missing a Javadoc comment.", filepath + ":24:16: Name 'data' must match pattern '^[A-Z](_?[A-Z0-9]+)*$'.", filepath + ":27: Missing a Javadoc comment.", - filepath + ":30:24: Missing a Javadoc comment.", }; verify(c, filepath, expected); } @@ -317,12 +312,9 @@ public class CheckerTest assertNotNull(c); final String[] expected = { filepath + ":14: Missing a Javadoc comment.", - filepath + ":17:20: Missing a Javadoc comment.", filepath + ":21: Missing a Javadoc comment.", - filepath + ":24:16: Missing a Javadoc comment.", filepath + ":24:16: Name 'data' must match pattern '^[A-Z](_?[A-Z0-9]+)*$'.", filepath + ":27: Missing a Javadoc comment.", - filepath + ":30:24: Missing a Javadoc comment.", }; verify(c, filepath, expected); } @@ -369,19 +361,12 @@ public class CheckerTest final String[] expected = { filepath + ":7: Missing a Javadoc comment.", filepath + ":9: Missing a Javadoc comment.", - filepath + ":11:16: Missing a Javadoc comment.", filepath + ":12:9: Missing a Javadoc comment.", filepath + ":14: Missing a Javadoc comment.", - filepath + ":16:25: Missing a Javadoc comment.", filepath + ":18:13: Missing a Javadoc comment.", filepath + ":25:13: Missing a Javadoc comment.", filepath + ":34: Missing a Javadoc comment.", - filepath + ":36:21: Missing a Javadoc comment.", filepath + ":38:9: Missing a Javadoc comment.", - filepath + ":43:17: Missing a Javadoc comment.", - filepath + ":44:9: Missing a Javadoc comment.", - filepath + ":45:19: Missing a Javadoc comment.", - filepath + ":46:16: Missing a Javadoc comment.", filepath + ":49:5: Missing a Javadoc comment.", filepath + ":54:5: Missing a Javadoc comment.", filepath + ":59:5: Missing a Javadoc comment.", @@ -421,8 +406,6 @@ public class CheckerTest assertNotNull(c); final String[] expected = { filepath + ":7: Missing a Javadoc comment.", - filepath + ":45:19: Missing a Javadoc comment.", - filepath + ":46:16: Missing a Javadoc comment.", filepath + ":59:5: Missing a Javadoc comment.", filepath + ":64:5: Missing a Javadoc comment.", filepath + ":79:5: Missing a Javadoc comment.", @@ -442,8 +425,6 @@ public class CheckerTest final String[] expected = { filepath + ":7: Missing a Javadoc comment.", filepath + ":38: Missing a Javadoc comment.", - filepath + ":40:23: Missing a Javadoc comment.", - filepath + ":41:16: Missing a Javadoc comment.", filepath + ":43:9: Missing a Javadoc comment.", filepath + ":44:9: Missing a Javadoc comment." }; diff --git a/src/tests/com/puppycrawl/tools/checkstyle/JavadocVariableCheckTest.java b/src/tests/com/puppycrawl/tools/checkstyle/JavadocVariableCheckTest.java new file mode 100644 index 000000000..8b0f990f4 --- /dev/null +++ b/src/tests/com/puppycrawl/tools/checkstyle/JavadocVariableCheckTest.java @@ -0,0 +1,86 @@ +package com.puppycrawl.tools.checkstyle; + +import com.puppycrawl.tools.checkstyle.checks.JavadocVariableCheck; + + +public class JavadocVariableCheckTest + extends BaseCheckTestCase +{ + public JavadocVariableCheckTest(String aName) + { + super(aName); + } + + public void testDefault() + throws Exception + { + final CheckConfiguration checkConfig = new CheckConfiguration(); + checkConfig.setClassname(JavadocVariableCheck.class.getName()); + final Checker c = createChecker(checkConfig); + final String fname = getPath("InputTags.java"); + final String[] expected = { + "11:5: Missing a Javadoc comment.", + }; + verify(c, fname, expected); + } + + public void testAnother() + throws Exception + { + final CheckConfiguration checkConfig = new CheckConfiguration(); + checkConfig.setClassname(JavadocVariableCheck.class.getName()); + final Checker c = createChecker(checkConfig); + final String fname = getPath("InputInner.java"); + final String[] expected = { + "17:9: Missing a Javadoc comment.", + "24:9: Missing a Javadoc comment.", + "30:13: Missing a Javadoc comment.", + }; + verify(c, fname, expected); + } + + public void testAnother2() + throws Exception + { + final CheckConfiguration checkConfig = new CheckConfiguration(); + checkConfig.setClassname(JavadocVariableCheck.class.getName()); + checkConfig.addProperty("scope", Scope.PUBLIC.getName()); + final Checker c = createChecker(checkConfig); + final String fname = getPath("InputInner.java"); + final String[] expected = { + }; + verify(c, fname, expected); + } + + public void testAnother3() + throws Exception + { + final CheckConfiguration checkConfig = new CheckConfiguration(); + checkConfig.setClassname(JavadocVariableCheck.class.getName()); + final Checker c = createChecker(checkConfig); + final String fname = getPath("InputPublicOnly.java"); + final String[] expected = { + "11:9: Missing a Javadoc comment.", + "16:13: Missing a Javadoc comment.", + "36:9: Missing a Javadoc comment.", + "43:5: Missing a Javadoc comment.", + "44:5: Missing a Javadoc comment.", + "45:5: Missing a Javadoc comment.", + "46:5: Missing a Javadoc comment.", + }; + verify(c, fname, expected); + } + public void testAnother4() + throws Exception + { + final CheckConfiguration checkConfig = new CheckConfiguration(); + checkConfig.setClassname(JavadocVariableCheck.class.getName()); + checkConfig.addProperty("scope", Scope.PUBLIC.getName()); + final Checker c = createChecker(checkConfig); + final String fname = getPath("InputPublicOnly.java"); + final String[] expected = { + "46:5: Missing a Javadoc comment.", + }; + verify(c, fname, expected); + } +}