From a24df47cafeab03fd65cf72b460aad3d2fafce3d Mon Sep 17 00:00:00 2001 From: Baratali Izmailov Date: Sat, 12 Jul 2014 23:56:38 +0400 Subject: [PATCH] Issue #49. Added support of single-line and block comments --- pom.xml | 8 + .../tools/checkstyle/TreeWalker.java | 437 ++++++++++++++---- .../tools/checkstyle/api/Check.java | 8 +- .../tools/checkstyle/api/DetailAST.java | 118 ++++- .../tools/checkstyle/api/TokenTypes.java | 75 ++- .../puppycrawl/tools/checkstyle/gui/Main.java | 13 + .../checkstyle/gui/ParseTreeInfoPanel.java | 23 + .../comments/AllBlockCommentsTest.java | 85 ++++ .../comments/AllSinglelineCommentsTest.java | 85 ++++ .../checkstyle/comments/CommentsTest.java | 413 +++++++++++++++++ .../comments/CompareTreesWithComments.java | 67 +++ .../comments/InputCommentsTest_1.java | 6 + .../comments/InputCommentsTest_2.java | 12 + .../comments/InputFullOfBlockComments.java | 15 + .../InputFullOfSinglelineComments.java | 64 +++ suppressions.xml | 3 + 16 files changed, 1310 insertions(+), 122 deletions(-) mode change 100644 => 100755 src/main/java/com/puppycrawl/tools/checkstyle/TreeWalker.java mode change 100644 => 100755 src/main/java/com/puppycrawl/tools/checkstyle/api/DetailAST.java create mode 100644 src/test/java/com/puppycrawl/tools/checkstyle/comments/AllBlockCommentsTest.java create mode 100644 src/test/java/com/puppycrawl/tools/checkstyle/comments/AllSinglelineCommentsTest.java create mode 100644 src/test/java/com/puppycrawl/tools/checkstyle/comments/CommentsTest.java create mode 100644 src/test/java/com/puppycrawl/tools/checkstyle/comments/CompareTreesWithComments.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_1.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_2.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfBlockComments.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfSinglelineComments.java diff --git a/pom.xml b/pom.xml index 1b723583f..15a8e3d47 100644 --- a/pom.xml +++ b/pom.xml @@ -499,6 +499,14 @@ + + checkstyle_checks.xml + checkstyle.suppressions.file + suppressions.xml + java.header + checkstyle.importcontrol.file=import-control.xml + false + diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalker.java b/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalker.java old mode 100644 new mode 100755 index 69d3c85b3..1cef17906 --- a/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalker.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalker.java @@ -21,15 +21,19 @@ package com.puppycrawl.tools.checkstyle; import java.io.File; import java.io.Reader; import java.io.StringReader; +import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map.Entry; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import antlr.CommonHiddenStreamToken; import antlr.RecognitionException; +import antlr.Token; import antlr.TokenStreamException; import antlr.TokenStreamHiddenTokenFilter; import antlr.TokenStreamRecognitionException; @@ -61,16 +65,42 @@ import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaRecognizer; public final class TreeWalker extends AbstractFileSetCheck { + /** + * State of AST. + * Indicates whether tree contains certain nodes. + */ + private static enum AstState { + /** + * Ordinary tree. + */ + ORDINARY, + + /** + * AST contains comment nodes. + */ + WITH_COMMENTS + } + /** default distance between tab stops */ private static final int DEFAULT_TAB_WIDTH = 8; - /** maps from token name to checks */ - private final Multimap mTokenToChecks = + /** maps from token name to ordinary checks */ + private final Multimap mTokenToOrdinaryChecks = HashMultimap.create(); - /** all the registered checks */ - private final Set mAllChecks = Sets.newHashSet(); + + /** maps from token name to comment checks */ + private final Multimap mTokenToCommentChecks = + HashMultimap.create(); + + /** registered ordinary checks, that don't use comment nodes */ + private final Set mOrdinaryChecks = Sets.newHashSet(); + + /** registered comment checks */ + private final Set mCommentChecks = Sets.newHashSet(); + /** the distance between tab stops */ private int mTabWidth = DEFAULT_TAB_WIDTH; + /** cache file **/ private PropertyCacheFile mCache = new PropertyCacheFile(null, null); @@ -83,11 +113,6 @@ public final class TreeWalker /** a factory for creating submodules (i.e. the Checks) */ private ModuleFactory mModuleFactory; - /** controls whether we should use recursive or iterative - * algorithm for tree processing. - */ - private final boolean mRecursive; - /** logger for debug purpose */ private static final Log LOG = LogFactory.getLog("com.puppycrawl.tools.checkstyle.TreeWalker"); @@ -98,18 +123,6 @@ public final class TreeWalker public TreeWalker() { setFileExtensions(new String[]{"java"}); - // Tree walker can use two possible algorithms for - // tree processing (iterative and recursive. - // Recursive is default for now. - final String recursive = - System.getProperty("checkstyle.use.recursive.algorithm", "false"); - mRecursive = "true".equals(recursive); - if (mRecursive) { - LOG.debug("TreeWalker uses recursive algorithm"); - } - else { - LOG.debug("TreeWalker uses iterative algorithm"); - } } /** @param aTabWidth the distance between tab stops */ @@ -188,7 +201,14 @@ public final class TreeWalker final FileText text = FileText.fromLines(aFile, aLines); final FileContents contents = new FileContents(text); final DetailAST rootAST = TreeWalker.parse(contents); - walk(rootAST, contents); + + getMessageCollector().reset(); + + walk(rootAST, contents, AstState.ORDINARY); + + final DetailAST astWithComments = appendHiddenCommentNodes(rootAST); + + walk(astWithComments, contents, AstState.WITH_COMMENTS); } catch (final RecognitionException re) { Utils.getExceptionLogger() @@ -243,6 +263,7 @@ public final class TreeWalker this.getClass(), null)); } catch (final Throwable err) { + err.printStackTrace(); Utils.getExceptionLogger().debug("Throwable occured.", err); getMessageCollector().add( new LocalizedMessage( @@ -295,7 +316,12 @@ public final class TreeWalker for (int element : tokens) { registerCheck(element, aCheck); } - mAllChecks.add(aCheck); + if (aCheck.isCommentNodesRequired()) { + mCommentChecks.add(aCheck); + } + else { + mOrdinaryChecks.add(aCheck); + } } /** @@ -315,106 +341,126 @@ public final class TreeWalker */ private void registerCheck(String aToken, Check aCheck) { - mTokenToChecks.put(aToken, aCheck); + if (aCheck.isCommentNodesRequired()) { + mTokenToCommentChecks.put(aToken, aCheck); + } + else if (TokenTypes.isCommentType(aToken)) { + LOG.warn("Check '" + + aCheck.getClass().getName() + + "' waits for comment type token ('" + + aToken + + "') and should override 'isCommentNodesRequred()'" + + " method to return 'true'"); + } + else { + mTokenToOrdinaryChecks.put(aToken, aCheck); + } } /** * Initiates the walk of an AST. * @param aAST the root AST - * @param aContents the contents of the file the AST was generated from + * @param aContents the contents of the file the AST was generated from. + * @param aAstState state of AST. */ - private void walk(DetailAST aAST, FileContents aContents) + private void walk(DetailAST aAST, FileContents aContents + , AstState aAstState) { - getMessageCollector().reset(); - notifyBegin(aAST, aContents); + notifyBegin(aAST, aContents, aAstState); // empty files are not flagged by javac, will yield aAST == null if (aAST != null) { - if (useRecursiveAlgorithm()) { - processRec(aAST); - } - else { - processIter(aAST); - } + processIter(aAST, aAstState); } - notifyEnd(aAST); + notifyEnd(aAST, aAstState); } - /** - * Notify interested checks that about to begin walking a tree. - * @param aRootAST the root of the tree - * @param aContents the contents of the file the AST was generated from + * Notify checks that we are about to begin walking a tree. + * @param aRootAST the root of the tree. + * @param aContents the contents of the file the AST was generated from. + * @param aAstState state of AST. */ - private void notifyBegin(DetailAST aRootAST, FileContents aContents) + private void notifyBegin(DetailAST aRootAST, FileContents aContents + , AstState aAstState) { - for (Check ch : mAllChecks) { + Set checks; + + if (aAstState == AstState.WITH_COMMENTS) { + checks = mCommentChecks; + } + else { + checks = mOrdinaryChecks; + } + + for (Check ch : checks) { ch.setFileContents(aContents); ch.beginTree(aRootAST); } } /** - * Notify checks that finished walking a tree. - * @param aRootAST the root of the tree + * Notify checks that we have finished walking a tree. + * @param aRootAST the root of the tree. + * @param aAstState state of AST. */ - private void notifyEnd(DetailAST aRootAST) + private void notifyEnd(DetailAST aRootAST, AstState aAstState) { - for (Check ch : mAllChecks) { + Set checks; + + if (aAstState == AstState.WITH_COMMENTS) { + checks = mCommentChecks; + } + else { + checks = mOrdinaryChecks; + } + + for (Check ch : checks) { ch.finishTree(aRootAST); } } /** - * Recursively processes a node calling interested checks at each node. - * Uses recursive algorithm. - * @param aAST the node to start from + * Notify checks that visiting a node. + * @param aAST the node to notify for. + * @param aAstState state of AST. */ - private void processRec(DetailAST aAST) + private void notifyVisit(DetailAST aAST, AstState aAstState) { - if (aAST == null) { - return; + Collection visitors; + final String tokenType = TokenTypes.getTokenName(aAST.getType()); + + if (aAstState == AstState.WITH_COMMENTS) { + visitors = mTokenToCommentChecks.get(tokenType); + } + else { + visitors = mTokenToOrdinaryChecks.get(tokenType); } - notifyVisit(aAST); - - final DetailAST child = aAST.getFirstChild(); - if (child != null) { - processRec(child); - } - - notifyLeave(aAST); - - final DetailAST sibling = aAST.getNextSibling(); - if (sibling != null) { - processRec(sibling); - } - } - - /** - * Notify interested checks that visiting a node. - * @param aAST the node to notify for - */ - private void notifyVisit(DetailAST aAST) - { - final Collection visitors = - mTokenToChecks.get(TokenTypes.getTokenName(aAST.getType())); for (Check c : visitors) { c.visitToken(aAST); } } /** - * Notify interested checks that leaving a node. - * + * Notify checks that leaving a node. * @param aAST - * the node to notify for + * the node to notify for + * @param aAstState state of AST. */ - private void notifyLeave(DetailAST aAST) + private void notifyLeave(DetailAST aAST, AstState aAstState) { - final Collection visitors = - mTokenToChecks.get(TokenTypes.getTokenName(aAST.getType())); + Collection visitors; + final String tokenType = TokenTypes.getTokenName(aAST.getType()); + + if (aAstState == AstState.WITH_COMMENTS) { + visitors = mTokenToCommentChecks.get(tokenType); + } + else { + visitors = mTokenToOrdinaryChecks.get(tokenType); + } + for (Check ch : visitors) { ch.leaveToken(aAST); } @@ -432,7 +478,7 @@ public final class TreeWalker * @return the root of the AST */ public static DetailAST parse(FileContents aContents) - throws RecognitionException, TokenStreamException + throws RecognitionException, TokenStreamException { final String fullText = aContents.getText().getFullText().toString(); final Reader sr = new StringReader(fullText); @@ -443,13 +489,13 @@ public final class TreeWalker lexer.setTreatEnumAsKeyword(true); lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken"); - TokenStreamHiddenTokenFilter filter = new TokenStreamHiddenTokenFilter( - lexer); + final TokenStreamHiddenTokenFilter filter = + new TokenStreamHiddenTokenFilter(lexer); filter.hide(TokenTypes.SINGLE_LINE_COMMENT); filter.hide(TokenTypes.BLOCK_COMMENT_BEGIN); final GeneratedJavaRecognizer parser = - new GeneratedJavaRecognizer(filter); + new GeneratedJavaRecognizer(filter); parser.setFilename(aContents.getFilename()); parser.setASTNodeClass(DetailAST.class.getName()); parser.compilationUnit(); @@ -460,35 +506,30 @@ public final class TreeWalker @Override public void destroy() { - for (Check c : mAllChecks) { + for (Check c : mOrdinaryChecks) { + c.destroy(); + } + for (Check c : mCommentChecks) { c.destroy(); } mCache.destroy(); super.destroy(); } - /** - * @return true if we should use recursive algorithm - * for tree processing, false for iterative one. - */ - private boolean useRecursiveAlgorithm() - { - return mRecursive; - } - /** * Processes a node calling interested checks at each node. * Uses iterative algorithm. * @param aRoot the root of tree for process + * @param aAstState state of AST. */ - private void processIter(DetailAST aRoot) + private void processIter(DetailAST aRoot, AstState aAstState) { DetailAST curNode = aRoot; while (curNode != null) { - notifyVisit(curNode); + notifyVisit(curNode, aAstState); DetailAST toVisit = curNode.getFirstChild(); while ((curNode != null) && (toVisit == null)) { - notifyLeave(curNode); + notifyLeave(curNode, aAstState); toVisit = curNode.getNextSibling(); if (toVisit == null) { curNode = curNode.getParent(); @@ -497,4 +538,206 @@ public final class TreeWalker curNode = toVisit; } } + + /** + * Appends comment nodes to existing AST. + * It traverses each node in AST, looks for hidden comment tokens + * and appends found comment tokens as nodes in AST. + * @param aRoot + * root of AST. + * @return root of AST with comment nodes. + */ + private static DetailAST appendHiddenCommentNodes(DetailAST aRoot) + { + DetailAST result = aRoot; + DetailAST curNode = aRoot; + DetailAST lastNode = aRoot; + + while (curNode != null) { + if (isPositionGreater(curNode, lastNode)) { + lastNode = curNode; + } + + CommonHiddenStreamToken tokenBefore = curNode.getHiddenBefore(); + DetailAST currentSibling = curNode; + while (tokenBefore != null) { // threat multiple comments + final DetailAST newCommentNode = + createCommentAstFromToken(tokenBefore); + + currentSibling.addPreviousSibling(newCommentNode); + + if (currentSibling == result) { + result = newCommentNode; + } + + currentSibling = newCommentNode; + tokenBefore = tokenBefore.getHiddenBefore(); + } + + DetailAST toVisit = curNode.getFirstChild(); + while ((curNode != null) && (toVisit == null)) { + toVisit = curNode.getNextSibling(); + if (toVisit == null) { + curNode = curNode.getParent(); + } + } + curNode = toVisit; + } + if (lastNode != null) { + CommonHiddenStreamToken tokenAfter = lastNode.getHiddenAfter(); + DetailAST currentSibling = lastNode; + while (tokenAfter != null) { + final DetailAST newCommentNode = + createCommentAstFromToken(tokenAfter); + + currentSibling.addNextSibling(newCommentNode); + + currentSibling = newCommentNode; + tokenAfter = tokenAfter.getHiddenAfter(); + } + } + return result; + } + + /** + * Checks if position of first DetailAST is greater than position of + * second DetailAST. Position is line number and column number in source + * file. + * @param aAST1 + * first DetailAST node. + * @param aAst2 + * second DetailAST node. + * @return true if position of aAst1 is greater than position of aAst2. + */ + private static boolean isPositionGreater(DetailAST aAST1, DetailAST aAst2) + { + if (aAST1.getLineNo() > aAst2.getLineNo()) { + return true; + } + else if (aAST1.getLineNo() < aAst2.getLineNo()) { + return false; + } + else { + if (aAST1.getColumnNo() > aAst2.getColumnNo()) { + return true; + } + } + return false; + } + + /** + * Create comment AST from token. Depending on token type + * SINGLE_LINE_COMMENT or BLOCK_COMMENT_BEGIN is created. + * @param aToken + * Token object. + * @return DetailAST of comment node. + */ + private static DetailAST createCommentAstFromToken(Token aToken) + { + switch (aToken.getType()) { + case TokenTypes.SINGLE_LINE_COMMENT: + return createSlCommentNode(aToken); + case TokenTypes.BLOCK_COMMENT_BEGIN: + return createBlockCommentNode(aToken); + default: + throw new IllegalArgumentException("Unknown comment type"); + } + } + + /** + * Create single-line comment from token. + * @param aToken + * Token object. + * @return DetailAST with SINGLE_LINE_COMMENT type. + */ + private static DetailAST createSlCommentNode(Token aToken) + { + final DetailAST slComment = new DetailAST(); + slComment.setType(TokenTypes.SINGLE_LINE_COMMENT); + slComment.setText("//"); + + // column counting begins from 0 + slComment.setColumnNo(aToken.getColumn() - 1); + slComment.setLineNo(aToken.getLine()); + + final DetailAST slCommentContent = new DetailAST(); + slCommentContent.initialize(aToken); + slCommentContent.setType(TokenTypes.COMMENT_CONTENT); + + // column counting begins from 0 + // plus length of '//' + slCommentContent.setColumnNo(aToken.getColumn() - 1 + 2); + slCommentContent.setLineNo(aToken.getLine()); + slCommentContent.setText(aToken.getText()); + + slComment.addChild(slCommentContent); + return slComment; + } + + /** + * Create block comment from token. + * @param aToken + * Token object. + * @return DetailAST with BLOCK_COMMENT type. + */ + private static DetailAST createBlockCommentNode(Token aToken) + { + final DetailAST blockComment = new DetailAST(); + blockComment.initialize(TokenTypes.BLOCK_COMMENT_BEGIN, "/*"); + + // column counting begins from 0 + blockComment.setColumnNo(aToken.getColumn() - 1); + blockComment.setLineNo(aToken.getLine()); + + final DetailAST blockCommentContent = new DetailAST(); + blockCommentContent.initialize(aToken); + blockCommentContent.setType(TokenTypes.COMMENT_CONTENT); + + // column counting begins from 0 + // plus length of '/*' + blockCommentContent.setColumnNo(aToken.getColumn() - 1 + 2); + blockCommentContent.setLineNo(aToken.getLine()); + blockCommentContent.setText(aToken.getText()); + + final DetailAST blockCommentClose = new DetailAST(); + blockCommentClose.initialize(TokenTypes.BLOCK_COMMENT_END, "*/"); + + final Entry linesColumns = countLinesColumns( + aToken.getText(), aToken.getLine(), aToken.getColumn()); + blockCommentClose.setLineNo(linesColumns.getKey()); + blockCommentClose.setColumnNo(linesColumns.getValue()); + + blockComment.addChild(blockCommentContent); + blockComment.addChild(blockCommentClose); + return blockComment; + } + + /** + * Count lines and columns (in last line) in text. + * @param aText + * String. + * @param aInitialLinesCnt + * initial value of lines counter. + * @param aInitialColumnsCnt + * initial value of columns counter. + * @return entry(pair), first element is lines counter, second - columns + * counter. + */ + private static Entry countLinesColumns( + String aText, int aInitialLinesCnt, int aInitialColumnsCnt) + { + int lines = aInitialLinesCnt; + int columns = aInitialColumnsCnt; + for (char c : aText.toCharArray()) { + switch (c) { + case '\n': + lines++; + columns = 0; + break; + default: + columns++; + } + } + return new SimpleEntry(lines, columns); + } } diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/api/Check.java b/src/main/java/com/puppycrawl/tools/checkstyle/api/Check.java index 0e33c1784..24f118498 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/api/Check.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/api/Check.java @@ -18,9 +18,10 @@ //////////////////////////////////////////////////////////////////////////////// package com.puppycrawl.tools.checkstyle.api; -import com.google.common.collect.Sets; import java.util.Set; +import com.google.common.collect.Sets; + /** * The base class for checks. * @@ -52,6 +53,11 @@ public abstract class Check extends AbstractViolationReporter */ private ClassLoader mLoader; + public boolean isCommentNodesRequired() + { + return false; + } + /** * Returns the default token a check is interested in. Only used if the * configuration for a check does not define the tokens. diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/api/DetailAST.java b/src/main/java/com/puppycrawl/tools/checkstyle/api/DetailAST.java old mode 100644 new mode 100755 index 6b20cdca2..7557bd463 --- a/src/main/java/com/puppycrawl/tools/checkstyle/api/DetailAST.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/api/DetailAST.java @@ -78,6 +78,8 @@ public final class DetailAST extends CommonASTWithHiddenTokens setType(da.getType()); mLineNo = da.getLineNo(); mColumnNo = da.getColumnNo(); + hiddenAfter = da.getHiddenAfter(); + hiddenBefore = da.getHiddenBefore(); } @Override @@ -102,6 +104,51 @@ public final class DetailAST extends CommonASTWithHiddenTokens } } + /** + * Add previous sibling. + * @param aAST + * DetailAST object. + */ + public void addPreviousSibling(DetailAST aAST) + { + if (aAST != null) { + aAST.setParent(mParent); + final DetailAST previousSibling = this.getPreviousSibling(); + + if (previousSibling != null) { + aAST.setPreviousSibling(previousSibling); + previousSibling.setNextSibling(aAST); + } + else if (mParent != null) { + mParent.setFirstChild(aAST); + } + + aAST.setNextSibling(this); + this.setPreviousSibling(aAST); + } + } + + /** + * Add next sibling. + * @param aAST + * DetailAST object. + */ + public void addNextSibling(DetailAST aAST) + { + if (aAST != null) { + aAST.setParent(mParent); + final DetailAST nextSibling = this.getNextSibling(); + + if (nextSibling != null) { + aAST.setNextSibling(nextSibling); + nextSibling.setPreviousSibling(aAST); + } + + aAST.setPreviousSibling(this); + this.setNextSibling(aAST); + } + } + /** * Sets previous sibling. * @param aAST a previous sibling @@ -174,36 +221,82 @@ public final class DetailAST extends CommonASTWithHiddenTokens if (mLineNo == NOT_INITIALIZED) { // an inner AST that has been initialized // with initialize(String text) - final DetailAST child = getFirstChild(); - final DetailAST sibling = getNextSibling(); - if (child != null) { - return child.getLineNo(); + DetailAST child = getFirstChild(); + while (child != null) { + // comment node can't be start of any java statement/definition + if (TokenTypes.isCommentType(child.getType())) { + child = child.getNextSibling(); + } + else { + return child.getLineNo(); + } } - else if (sibling != null) { - return sibling.getLineNo(); + + DetailAST sibling = getNextSibling(); + while (sibling != null) { + // comment node can't be start of any java statement/definition + if (TokenTypes.isCommentType(sibling.getType())) { + sibling = sibling.getNextSibling(); + } + else { + return sibling.getLineNo(); + } } } return mLineNo; } + /** + * Set line number. + * @param aLineNo + * line number. + */ + public void setLineNo(int aLineNo) + { + mLineNo = aLineNo; + } + /** @return the column number **/ public int getColumnNo() { if (mColumnNo == NOT_INITIALIZED) { // an inner AST that has been initialized // with initialize(String text) - final DetailAST child = getFirstChild(); - final DetailAST sibling = getNextSibling(); - if (child != null) { - return child.getColumnNo(); + DetailAST child = getFirstChild(); + while (child != null) { + // comment node can't be start of any java statement/definition + if (TokenTypes.isCommentType(child.getType())) { + child = child.getNextSibling(); + } + else { + return child.getColumnNo(); + } } - else if (sibling != null) { - return sibling.getColumnNo(); + + DetailAST sibling = getNextSibling(); + while (sibling != null) { + // comment node can't be start of any java statement/definition + if (TokenTypes.isCommentType(sibling.getType())) { + sibling = sibling.getNextSibling(); + } + else { + return sibling.getColumnNo(); + } } } return mColumnNo; } + /** + * Set column number. + * @param aColumnNo + * column number. + */ + public void setColumnNo(int aColumnNo) + { + mColumnNo = aColumnNo; + } + /** @return the last child node */ public DetailAST getLastChild() { @@ -308,4 +401,5 @@ public final class DetailAST extends CommonASTWithHiddenTokens { return (DetailAST) super.getFirstChild(); } + } diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java b/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java index 5308e2eff..b4d62e85b 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java @@ -3437,24 +3437,61 @@ public final class TokenTypes public static final int LAMBDA = GeneratedJavaTokenTypes.LAMBDA; /** - * Begining of single line comment: '//' + * Begining of single line comment: '//'. + * + *
+     * +--SINLE_LINE_COMMENT
+     *         |
+     *         +--COMMENT_CONTENT
+     * 
*/ - public static final int SINGLE_LINE_COMMENT = GeneratedJavaTokenTypes.SINGLE_LINE_COMMENT; + public static final int SINGLE_LINE_COMMENT = + GeneratedJavaTokenTypes.SINGLE_LINE_COMMENT; /** - * Begining of block comment: '/*' + * Begining of block comment: '/*'. + * + *
+     * +--BLOCK_COMMENT_BEGIN
+     *         |
+     *         +--COMMENT_CONTENT
+     *         +--BLOCK_COMMENT_END
+     * 
*/ - public static final int BLOCK_COMMENT_BEGIN = GeneratedJavaTokenTypes.BLOCK_COMMENT_BEGIN; + public static final int BLOCK_COMMENT_BEGIN = + GeneratedJavaTokenTypes.BLOCK_COMMENT_BEGIN; /** - * End of block comment: '* /' + * End of block comment: '* /'. + * + *
+     * +--BLOCK_COMMENT_BEGIN
+     *         |
+     *         +--COMMENT_CONTENT
+     *         +--BLOCK_COMMENT_END
+     * 
*/ - public static final int BLOCK_COMMENT_END = GeneratedJavaTokenTypes.BLOCK_COMMENT_END; + public static final int BLOCK_COMMENT_END = + GeneratedJavaTokenTypes.BLOCK_COMMENT_END; /** * Text of single-line or block comment. + * + *
+     * +--SINLE_LINE_COMMENT
+     *         |
+     *         +--COMMENT_CONTENT
+     * 
+ * + *
+     * +--BLOCK_COMMENT_BEGIN
+     *         |
+     *         +--COMMENT_CONTENT
+     *         +--BLOCK_COMMENT_END
+     * 
*/ - public static final int COMMENT_CONTENT = GeneratedJavaTokenTypes.COMMENT_CONTENT; + public static final int COMMENT_CONTENT = + GeneratedJavaTokenTypes.COMMENT_CONTENT; //////////////////////////////////////////////////////////////////////// // The interesting code goes here @@ -3552,15 +3589,29 @@ public final class TokenTypes } /** - * Check that argument is SINGLE_LINE_COMMENT or BLOCK_COMMENT_BEGIN - * or BLOCK_COMMENT_END or COMMENT_CONTENT. - * @param aType token type - * @return true if aType is comment token type + * Is argument comment-related type (SINGLE_LINE_COMMENT, + * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT). + * @param aType + * token type. + * @return true if aType is comment-related type. */ - public static boolean isCommentType(int aType) { + public static boolean isCommentType(int aType) + { return aType == TokenTypes.SINGLE_LINE_COMMENT || aType == TokenTypes.BLOCK_COMMENT_BEGIN || aType == TokenTypes.BLOCK_COMMENT_END || aType == TokenTypes.COMMENT_CONTENT; } + + /** + * Is argument comment-related type name (SINGLE_LINE_COMMENT, + * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT). + * @param aType + * token type name. + * @return true if aType is comment-related type name. + */ + public static boolean isCommentType(String aType) + { + return isCommentType(getTokenId(aType)); + } } diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/gui/Main.java b/src/main/java/com/puppycrawl/tools/checkstyle/gui/Main.java index 83a3ffbed..5f04b9e59 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/gui/Main.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/gui/Main.java @@ -23,6 +23,8 @@ import java.io.File; import javax.swing.JFrame; +import com.puppycrawl.tools.checkstyle.api.DetailAST; + /** * Entry point for starting the checkstyle GUI. */ @@ -43,4 +45,15 @@ public class Main frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } + + public static void displayAst(DetailAST ast) + { + JFrame frame = new JFrame("CheckStyle"); + final ParseTreeInfoPanel panel = new ParseTreeInfoPanel(); + frame.getContentPane().add(panel); + panel.openAst(ast, frame); + frame.setSize(1500, 800); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } } diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/gui/ParseTreeInfoPanel.java b/src/main/java/com/puppycrawl/tools/checkstyle/gui/ParseTreeInfoPanel.java index fcfc7fa3f..3269a3398 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/gui/ParseTreeInfoPanel.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/gui/ParseTreeInfoPanel.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.TooManyListenersException; + import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; @@ -39,7 +40,9 @@ import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; + import antlr.ANTLRException; + import com.puppycrawl.tools.checkstyle.TreeWalker; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.FileContents; @@ -82,6 +85,26 @@ public class ParseTreeInfoPanel extends JPanel } } + public void openAst(DetailAST parseTree, final Component aParent) + { + mParseTreeModel.setParseTree(parseTree); + reloadAction.setEnabled(true); + + // clear for each new file + getLines2position().clear(); + // starts line counting at 1 + getLines2position().add(0); + // insert the contents of the file to the text area + + // clean the text area before inserting the lines of the new file + if (mJTextArea.getText().length() != 0) { + mJTextArea.replaceRange("", 0, mJTextArea.getText().length()); + } + + // move back to the top of the file + mJTextArea.moveCaretPosition(0); + } + private class FileSelectionAction extends AbstractAction { /** diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllBlockCommentsTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllBlockCommentsTest.java new file mode 100644 index 000000000..49c6fcd37 --- /dev/null +++ b/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllBlockCommentsTest.java @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.comments; + +import java.io.File; +import java.util.Arrays; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.collect.Sets; +import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport; +import com.puppycrawl.tools.checkstyle.DefaultConfiguration; +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +public class AllBlockCommentsTest extends BaseCheckTestSupport +{ + protected static final Set allComments = Sets.newLinkedHashSet(); + + public static class BlockCommentListenerCheck extends Check + { + @Override + public boolean isCommentNodesRequired() + { + return true; + } + + @Override + public int[] getDefaultTokens() + { + return new int[] {TokenTypes.BLOCK_COMMENT_BEGIN}; + } + + @Override + public void init() + { + allComments.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "\n21\n", "22", "23", "24", "25", "26", + "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", + "37", "38", " 39 ", "40", "41", "42", "43", "44", "45", "46", + "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", + "57", "58", "59", "60", "61")); + } + + @Override + public void visitToken(DetailAST aAST) + { + String commentContent = aAST.getFirstChild().getText(); + if (!allComments.remove(commentContent)) { + Assert.fail("Unexpected comment: " + commentContent); + } + } + + } + + @Test + public void testAllBlockComments() throws Exception + { + DefaultConfiguration checkConfig = createCheckConfig(BlockCommentListenerCheck.class); + final String[] expected = {}; + verify(checkConfig, getPath("comments" + File.separator + + "InputFullOfBlockComments.java"), expected); + Assert.assertTrue(allComments.isEmpty()); + } +} diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllSinglelineCommentsTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllSinglelineCommentsTest.java new file mode 100644 index 000000000..b9039e7bb --- /dev/null +++ b/src/test/java/com/puppycrawl/tools/checkstyle/comments/AllSinglelineCommentsTest.java @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.comments; + +import java.io.File; +import java.util.Arrays; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.collect.Sets; +import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport; +import com.puppycrawl.tools.checkstyle.DefaultConfiguration; +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +public class AllSinglelineCommentsTest extends BaseCheckTestSupport +{ + protected static final Set allComments = Sets.newLinkedHashSet(); + + public static class SinglelineCommentListenerCheck extends Check + { + @Override + public boolean isCommentNodesRequired() + { + return true; + } + + @Override + public int[] getDefaultTokens() + { + return new int[] {TokenTypes.SINGLE_LINE_COMMENT}; + } + + @Override + public void init() + { + allComments.addAll(Arrays.asList("0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", + "7\n", "8\n", "9\n", "10\n", "11\n", "12\n", "13\n", "14\n", "15\n", "16\n", + "17\n", "18\n", "19\n", "20\n", "21\n", "22\n", "23\n", "24\n", "25\n", "26\n", + "27\n", "28\n", "29\n", "30\n", "31\n", "32\n", "33\n", "34\n", "35\n", "36\n", + "37\n", "38\n", "39\n", "40\n", "41\n", "42\n", "43\n", "44\n", "45\n", "46\n", + "47\n", "48\n", "49\n", "50\n", "51\n", "52\n", "53\n", "54\n", "55\n", "56\n", + "57\n", "58\n", "59\n", "60\n", "61\n", "62\n", "63")); + } + + @Override + public void visitToken(DetailAST aAST) + { + String commentContent = aAST.getFirstChild().getText(); + if (!allComments.remove(commentContent)) { + Assert.fail("Unexpected comment: " + commentContent); + } + } + + } + + @Test + public void testAllBlockComments() throws Exception + { + DefaultConfiguration checkConfig = createCheckConfig(SinglelineCommentListenerCheck.class); + final String[] expected = {}; + verify(checkConfig, getPath("comments" + File.separator + + "InputFullOfSinglelineComments.java"), expected); + Assert.assertTrue(allComments.isEmpty()); + } +} diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/comments/CommentsTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/comments/CommentsTest.java new file mode 100644 index 000000000..4a0be90ed --- /dev/null +++ b/src/test/java/com/puppycrawl/tools/checkstyle/comments/CommentsTest.java @@ -0,0 +1,413 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.comments; + +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.BLOCK_COMMENT_BEGIN; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.BLOCK_COMMENT_END; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.CLASS_DEF; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.COMMENT_CONTENT; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.IDENT; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LCURLY; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LITERAL_CLASS; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LITERAL_PROTECTED; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LITERAL_PUBLIC; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LPAREN; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.METHOD_DEF; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.MODIFIERS; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.OBJBLOCK; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.PARAMETERS; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.RCURLY; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.RPAREN; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.SINGLE_LINE_COMMENT; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.SLIST; +import static com.puppycrawl.tools.checkstyle.api.TokenTypes.TYPE; + +import java.io.File; + +import org.junit.Test; + +import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport; +import com.puppycrawl.tools.checkstyle.DefaultConfiguration; +import com.puppycrawl.tools.checkstyle.api.DetailAST; + +public class CommentsTest extends BaseCheckTestSupport +{ + /* + * +--CLASS_DEF [1,0] + * | + * +--MODIFIERS [1,0] + * | + * +--LITERAL_PUBLIC [1,0] + * +--LITERAL_CLASS [1,7] + * +--BLOCK_COMMENT_BEGIN [1,13] + * | + * +--COMMENT_CONTENT [1,15] + * +--BLOCK_COMMENT_END [3,4] + * +--IDENT [4,0] + * +--OBJBLOCK [5,0] + * | + * +--LCURLY [5,0] + * | + * +--SINGLE_LINE_COMMENT [5,2] + * | + * +--COMMENT_CONTENT [5,4] + * +--RCURLY [6,0] + */ + private static DetailAST buildInput_1() + { + DetailAST classDef = new DetailAST(); + classDef.setType(CLASS_DEF); + classDef.setText("CLASS_DEF"); + classDef.setLineNo(1); + classDef.setColumnNo(0); + + DetailAST modifiers = new DetailAST(); + modifiers.setType(MODIFIERS); + modifiers.setText("MODIFIERS"); + modifiers.setLineNo(1); + modifiers.setColumnNo(0); + + DetailAST literalPublic = new DetailAST(); + literalPublic.setType(LITERAL_PUBLIC); + literalPublic.setText("public"); + literalPublic.setLineNo(1); + literalPublic.setColumnNo(0); + + DetailAST literalClass = new DetailAST(); + literalClass.setType(LITERAL_CLASS); + literalClass.setText("class"); + literalClass.setLineNo(1); + literalClass.setColumnNo(7); + + DetailAST blockCommentStart = new DetailAST(); + blockCommentStart.setType(BLOCK_COMMENT_BEGIN); + blockCommentStart.setText("/*"); + blockCommentStart.setLineNo(1); + blockCommentStart.setColumnNo(13); + + DetailAST blockCommentContent = new DetailAST(); + blockCommentContent.setType(COMMENT_CONTENT); + blockCommentContent.setText("\n i'mcomment567\n "); + blockCommentContent.setLineNo(1); + blockCommentContent.setColumnNo(15); + + DetailAST blockCommentEnd = new DetailAST(); + blockCommentEnd.setType(BLOCK_COMMENT_END); + blockCommentEnd.setText("*/"); + blockCommentEnd.setLineNo(3); + blockCommentEnd.setColumnNo(4); + + DetailAST ident = new DetailAST(); + ident.setType(IDENT); + ident.setText("InputCommentsTest"); + ident.setLineNo(4); + ident.setColumnNo(0); + + DetailAST objBlock = new DetailAST(); + objBlock.setType(OBJBLOCK); + objBlock.setText("OBJBLOCK"); + objBlock.setLineNo(5); + objBlock.setColumnNo(0); + + DetailAST lcurly = new DetailAST(); + lcurly.setType(LCURLY); + lcurly.setText("{"); + lcurly.setLineNo(5); + lcurly.setColumnNo(0); + + DetailAST slComment = new DetailAST(); + slComment.setType(SINGLE_LINE_COMMENT); + slComment.setText("//"); + slComment.setLineNo(5); + slComment.setColumnNo(2); + + DetailAST slCommentContent = new DetailAST(); + slCommentContent.setType(COMMENT_CONTENT); + slCommentContent.setText(" comment to left curly brace\n"); + slCommentContent.setLineNo(5); + slCommentContent.setColumnNo(4); + + DetailAST rcurly = new DetailAST(); + rcurly.setType(RCURLY); + rcurly.setText("}"); + rcurly.setLineNo(6); + rcurly.setColumnNo(0); + + classDef.setFirstChild(modifiers); + modifiers.setNextSibling(literalClass); + literalClass.setNextSibling(blockCommentStart); + blockCommentStart.setNextSibling(ident); + ident.setNextSibling(objBlock); + + modifiers.setFirstChild(literalPublic); + + blockCommentStart.setFirstChild(blockCommentContent); + blockCommentContent.setNextSibling(blockCommentEnd); + + objBlock.setFirstChild(lcurly); + lcurly.setNextSibling(slComment); + slComment.setNextSibling(rcurly); + + slComment.setFirstChild(slCommentContent); + + return classDef; + } + + /* + * +--CLASS_DEF [2,0] + * | + * +--MODIFIERS [2,0] + * +--SINGLE_LINE_COMMENT [1,0] + * | + * +--COMMENT_CONTENT [1,2] + * +--LITERAL_CLASS [2,0] + * +--IDENT [2,6] + * +--OBJBLOCK [3,0] + * | + * +--LCURLY [3,0] + * +--METHOD_DEF [9,4] + * | + * +--MODIFIERS [9,4] + * | + * +--BLOCK_COMMENT_BEGIN [4,4] + * | + * +--COMMENT_CONTENT [4,6] + * +--BLOCK_COMMENT_END [8,5] + * +--LITERAL_PROTECTED [9,4] + * +--TYPE [9,14] + * | + * +--IDENT [9,14] + * +--IDENT [9,21] + * +--LPAREN [9,25] + * +--PARAMETERS [9,26] + * +--RPAREN [9,26] + * +--SLIST [10,4] + * | + * +--RCURLY [11,4] + * +--RCURLY [12,0] + */ + private static DetailAST buildInput_2() + { + DetailAST classDef = new DetailAST(); + classDef.setType(CLASS_DEF); + classDef.setText("CLASS_DEF"); + classDef.setLineNo(2); + classDef.setColumnNo(0); + + DetailAST modifiers = new DetailAST(); + modifiers.setType(MODIFIERS); + modifiers.setText("MODIFIERS"); + modifiers.setLineNo(2); + modifiers.setColumnNo(0); + + classDef.setFirstChild(modifiers); + + DetailAST slComment = new DetailAST(); + slComment.setType(SINGLE_LINE_COMMENT); + slComment.setText("//"); + slComment.setLineNo(1); + slComment.setColumnNo(0); + + DetailAST slCommentContent = new DetailAST(); + slCommentContent.setType(COMMENT_CONTENT); + slCommentContent.setText(" my class\n"); + slCommentContent.setLineNo(1); + slCommentContent.setColumnNo(2); + + slComment.setFirstChild(slCommentContent); + modifiers.setNextSibling(slComment); + + DetailAST literalClass = new DetailAST(); + literalClass.setType(LITERAL_CLASS); + literalClass.setText("class"); + literalClass.setLineNo(2); + literalClass.setColumnNo(0); + + slComment.setNextSibling(literalClass); + + DetailAST identClassName = new DetailAST(); + identClassName.setType(IDENT); + identClassName.setText("A"); + identClassName.setLineNo(2); + identClassName.setColumnNo(6); + + literalClass.setNextSibling(identClassName); + + DetailAST objBlock = new DetailAST(); + objBlock.setType(OBJBLOCK); + objBlock.setText("OBJBLOCK"); + objBlock.setLineNo(3); + objBlock.setColumnNo(0); + + identClassName.setNextSibling(objBlock); + + DetailAST lcurly = new DetailAST(); + lcurly.setType(LCURLY); + lcurly.setText("{"); + lcurly.setLineNo(3); + lcurly.setColumnNo(0); + + objBlock.setFirstChild(lcurly); + + DetailAST methodDef = new DetailAST(); + methodDef.setType(METHOD_DEF); + methodDef.setText("METHOD_DEF"); + methodDef.setLineNo(9); + methodDef.setColumnNo(4); + + lcurly.setNextSibling(methodDef); + + DetailAST rcurly = new DetailAST(); + rcurly.setType(RCURLY); + rcurly.setText("}"); + rcurly.setLineNo(12); + rcurly.setColumnNo(0); + + methodDef.setNextSibling(rcurly); + + DetailAST methodModifiers = new DetailAST(); + methodModifiers.setType(MODIFIERS); + methodModifiers.setText("MODIFIERS"); + methodModifiers.setLineNo(9); + methodModifiers.setColumnNo(4); + + methodDef.setFirstChild(methodModifiers); + + DetailAST methodType = new DetailAST(); + methodType.setType(TYPE); + methodType.setText("TYPE"); + methodType.setLineNo(9); + methodType.setColumnNo(14); + + methodModifiers.setNextSibling(methodType); + + DetailAST identMethodType = new DetailAST(); + identMethodType.setType(IDENT); + identMethodType.setText("String"); + identMethodType.setLineNo(9); + identMethodType.setColumnNo(14); + + methodType.setFirstChild(identMethodType); + + DetailAST identMethodName = new DetailAST(); + identMethodName.setType(IDENT); + identMethodName.setText("line"); + identMethodName.setLineNo(9); + identMethodName.setColumnNo(21); + + methodType.setNextSibling(identMethodName); + + DetailAST lparen = new DetailAST(); + lparen.setType(LPAREN); + lparen.setText("("); + lparen.setLineNo(9); + lparen.setColumnNo(25); + + identMethodName.setNextSibling(lparen); + + DetailAST parameters = new DetailAST(); + parameters.setType(PARAMETERS); + parameters.setText("PARAMETERS"); + parameters.setLineNo(9); + parameters.setColumnNo(26); + + lparen.setNextSibling(parameters); + + DetailAST rparen = new DetailAST(); + rparen.setType(RPAREN); + rparen.setText(")"); + rparen.setLineNo(9); + rparen.setColumnNo(26); + + parameters.setNextSibling(rparen); + + DetailAST slist = new DetailAST(); + slist.setType(SLIST); + slist.setText("{"); + slist.setLineNo(10); + slist.setColumnNo(4); + + rparen.setNextSibling(slist); + + DetailAST methodRcurly = new DetailAST(); + methodRcurly.setType(RCURLY); + methodRcurly.setText("}"); + methodRcurly.setLineNo(11); + methodRcurly.setColumnNo(4); + + slist.setFirstChild(methodRcurly); + + DetailAST blockCommentStart = new DetailAST(); + blockCommentStart.setType(BLOCK_COMMENT_BEGIN); + blockCommentStart.setText("/*"); + blockCommentStart.setLineNo(4); + blockCommentStart.setColumnNo(4); + + DetailAST blockCommentContent = new DetailAST(); + blockCommentContent.setType(COMMENT_CONTENT); + blockCommentContent.setText("*\n" + + " * Lines method.\n" + + " * \n" + + " * @return string.\n" + + " "); + blockCommentContent.setLineNo(4); + blockCommentContent.setColumnNo(6); + + blockCommentStart.setFirstChild(blockCommentContent); + + DetailAST blockCommentEnd = new DetailAST(); + blockCommentEnd.setType(BLOCK_COMMENT_END); + blockCommentEnd.setText("*/"); + blockCommentEnd.setLineNo(8); + blockCommentEnd.setColumnNo(5); + + blockCommentContent.setNextSibling(blockCommentEnd); + methodModifiers.setFirstChild(blockCommentStart); + + DetailAST literalProtected = new DetailAST(); + literalProtected.setType(LITERAL_PROTECTED); + literalProtected.setText("protected"); + literalProtected.setLineNo(9); + literalProtected.setColumnNo(4); + + blockCommentStart.setNextSibling(literalProtected); + + return classDef; + } + + @Test + public void testCompareExpectedTreeWithInput_1() throws Exception + { + DefaultConfiguration checkConfig = createCheckConfig(CompareTreesWithComments.class); + CompareTreesWithComments.expectedTree = buildInput_1(); + final String[] expected = {}; + verify(checkConfig, getPath("comments" + File.separator + + "InputCommentsTest_1.java"), expected); + } + + @Test + public void testCompareExpectedTreeWithInput_2() throws Exception + { + DefaultConfiguration checkConfig = createCheckConfig(CompareTreesWithComments.class); + CompareTreesWithComments.expectedTree = buildInput_2(); + final String[] expected = {}; + verify(checkConfig, getPath("comments" + File.separator + + "InputCommentsTest_2.java"), expected); + } +} diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/comments/CompareTreesWithComments.java b/src/test/java/com/puppycrawl/tools/checkstyle/comments/CompareTreesWithComments.java new file mode 100644 index 000000000..dbac9ab67 --- /dev/null +++ b/src/test/java/com/puppycrawl/tools/checkstyle/comments/CompareTreesWithComments.java @@ -0,0 +1,67 @@ +package com.puppycrawl.tools.checkstyle.comments; + +import org.junit.Assert; + +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.DetailAST; + +public class CompareTreesWithComments extends Check +{ + protected static DetailAST expectedTree; + + @Override + public boolean isCommentNodesRequired() + { + return true; + } + + @Override + public int[] getDefaultTokens() + { + return new int[]{}; + } + + @Override + public void beginTree(DetailAST aRootAST) + { + Assert.assertTrue(isAstEquals(expectedTree, aRootAST)); + } + + private boolean isAstEquals(DetailAST expected, DetailAST actual) + { + boolean result = false; + if (expected == actual) { + result = true; + } + else if (actual == null || expected == null) { + result = false; + } else { + if (expected.getType() == actual.getType() + && expected.getLineNo() == actual.getLineNo() + && expected.getColumnNo() == actual.getColumnNo()) + { + if (expected.getText() == null) { + result = actual.getText() == null; + } + else if (expected.getText().equals(actual.getText())) { + result = true; + } + } + + if (result) { + DetailAST childExpected = expected.getFirstChild(); + DetailAST childActual = actual.getFirstChild(); + result = isAstEquals(childExpected, childActual); + if (result) { + DetailAST nextSiblingExpected = expected.getNextSibling(); + DetailAST nextSiblingActual = actual.getNextSibling(); + result = isAstEquals(nextSiblingExpected, nextSiblingActual); + } + } + } + if (!result) { + System.out.println("Expected: " + expected + " | Actual: " + actual); + } + return result; + } +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_1.java b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_1.java new file mode 100644 index 000000000..e8969001f --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_1.java @@ -0,0 +1,6 @@ +public class /* + i'mcomment567 + */ +InputCommentsTest +{ // comment to left curly brace +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_2.java b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_2.java new file mode 100644 index 000000000..6e88f3b7d --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputCommentsTest_2.java @@ -0,0 +1,12 @@ +// my class +class A +{ + /** + * Lines method. + * + * @return string. + */ + protected String line() + { + } +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfBlockComments.java b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfBlockComments.java new file mode 100644 index 000000000..72e1e3227 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfBlockComments.java @@ -0,0 +1,15 @@ +/*0*//*1*/package/*2*/ com/*3*/./*4*/puppycrawl/*5*/./*6*/tools/*7*/./*8*/checkstyle/*9*/./*10*/javadoc/*11*/;/*12*/ +/*13*/ +/*14*/public/*15*/ class /*16*/InputFullOfBlockComments /*49*/{/*17*/ + /*18*/ + /*19*/public/*20*/ static/* +21 +*/ String/*22*/ main/*23*/(/*24*/String/*25*/[/*26*/]/*27*/ args/*28*/)/*29*/ {/*30*/ + /*31*/String /*32*/line /*33*/= /*34*/"/*I'm NOT comment*/blabla"/*35*/;/*36*/ + /*37*/System/*38*/./* 39 */out/*40*/./*41*/println/*42*/(/*43*/line/*44*/)/*45*/;/*46*/ + /*50*/for/*51*/ (/*52*/Integer/*53*/ i/*54*/:/*55*/ array/*56*/)/*57*/ {/*58*/ + /*59*/ + }/*60*/ + }/*47*/ +}/*48*/ +/*61*/ \ No newline at end of file diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfSinglelineComments.java b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfSinglelineComments.java new file mode 100644 index 000000000..68b1b5db1 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/comments/InputFullOfSinglelineComments.java @@ -0,0 +1,64 @@ +//0 +package//1 +com//2 +.//3 +puppycrawl//4 +.//5 +tools//6 +.//7 +checkstyle//8 +.//9 +checks//10 +.//11 +javadoc//12 +;//13 +//14 +public//15 +class//16 +InputFullOfSinglelineComments//17 +{//18 +public//19 +static//20 +void//21 +main//22 +(//23 +String//24 +[//25 +]//26 +args//27 +)//28 +{//29 +for//30 +(//31 +int//32 +i//33 +=//34 +0//35 +;//36 +i//37 +<=//38 +10//39 +;//40 +i++//41 +)//42 +{//43 +String//44 +line//45 +=//46 +i//47 ++//48 +""//49 +;//50 +System//51 +.//52 +out//53 +.//54 +println//55 +(//56 +line//57 +)//58 +;//59 +}//60 +}//61 +}//62 +//63 \ No newline at end of file diff --git a/suppressions.xml b/suppressions.xml index 0409f7900..0b308956c 100644 --- a/suppressions.xml +++ b/suppressions.xml @@ -50,4 +50,7 @@ + + +