Coverage Report - com.puppycrawl.tools.checkstyle.JavaParser
 
Classes in this File Line Coverage Branch Coverage Complexity
JavaParser
100%
83/83
100%
30/30
2.875
JavaParser$Options
100%
3/3
N/A
2.875
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2018 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.io.Reader;
 25  
 import java.io.StringReader;
 26  
 import java.nio.charset.StandardCharsets;
 27  
 import java.util.Locale;
 28  
 
 29  
 import antlr.CommonHiddenStreamToken;
 30  
 import antlr.RecognitionException;
 31  
 import antlr.Token;
 32  
 import antlr.TokenStreamException;
 33  
 import antlr.TokenStreamHiddenTokenFilter;
 34  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 35  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 36  
 import com.puppycrawl.tools.checkstyle.api.FileContents;
 37  
 import com.puppycrawl.tools.checkstyle.api.FileText;
 38  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 39  
 import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaLexer;
 40  
 import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaRecognizer;
 41  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 42  
 
 43  
 /**
 44  
  * Helper methods to parse java source files.
 45  
  *
 46  
  * @author Oliver Burn
 47  
  * @author Pavel Bludov
 48  
  */
 49  
 public final class JavaParser {
 50  
 
 51  
     /**
 52  
      * Enum to be used for test if comments should be used.
 53  
      */
 54  9
     public enum Options {
 55  
 
 56  
         /**
 57  
          * Comments nodes should be processed.
 58  
          */
 59  3
         WITH_COMMENTS,
 60  
 
 61  
         /**
 62  
          * Comments nodes should be ignored.
 63  
          */
 64  3
         WITHOUT_COMMENTS
 65  
 
 66  
     }
 67  
 
 68  
     /** Stop instances being created. **/
 69  1
     private JavaParser() {
 70  1
     }
 71  
 
 72  
     /**
 73  
      * Static helper method to parses a Java source file.
 74  
      * @param contents contains the contents of the file
 75  
      * @return the root of the AST
 76  
      * @throws CheckstyleException if the contents is not a valid Java source
 77  
      */
 78  
     public static DetailAST parse(FileContents contents)
 79  
             throws CheckstyleException {
 80  2454
         final String fullText = contents.getText().getFullText().toString();
 81  2454
         final Reader reader = new StringReader(fullText);
 82  2454
         final GeneratedJavaLexer lexer = new GeneratedJavaLexer(reader);
 83  2454
         lexer.setCommentListener(contents);
 84  2454
         lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
 85  
 
 86  2454
         final TokenStreamHiddenTokenFilter filter = new TokenStreamHiddenTokenFilter(lexer);
 87  2454
         filter.hide(TokenTypes.SINGLE_LINE_COMMENT);
 88  2454
         filter.hide(TokenTypes.BLOCK_COMMENT_BEGIN);
 89  
 
 90  2454
         final GeneratedJavaRecognizer parser = new GeneratedJavaRecognizer(filter);
 91  2454
         parser.setFilename(contents.getFileName());
 92  2454
         parser.setASTNodeClass(DetailAST.class.getName());
 93  
         try {
 94  2454
             parser.compilationUnit();
 95  
         }
 96  9
         catch (RecognitionException | TokenStreamException ex) {
 97  18
             final String exceptionMsg = String.format(Locale.ROOT,
 98  
                 "%s occurred while parsing file %s.",
 99  9
                 ex.getClass().getSimpleName(), contents.getFileName());
 100  9
             throw new CheckstyleException(exceptionMsg, ex);
 101  2445
         }
 102  
 
 103  2445
         return (DetailAST) parser.getAST();
 104  
     }
 105  
 
 106  
     /**
 107  
      * Parse a text and return the parse tree.
 108  
      * @param text the text to parse
 109  
      * @param options {@link Options} to control inclusion of comment nodes
 110  
      * @return the root node of the parse tree
 111  
      * @throws CheckstyleException if the text is not a valid Java source
 112  
      */
 113  
     public static DetailAST parseFileText(FileText text, Options options)
 114  
             throws CheckstyleException {
 115  1068
         final FileContents contents = new FileContents(text);
 116  1068
         DetailAST ast = parse(contents);
 117  1066
         if (options == Options.WITH_COMMENTS) {
 118  88
             ast = appendHiddenCommentNodes(ast);
 119  
         }
 120  1066
         return ast;
 121  
     }
 122  
 
 123  
     /**
 124  
      * Parses Java source file.
 125  
      * @param file the file to parse
 126  
      * @param options {@link Options} to control inclusion of comment nodes
 127  
      * @return DetailAST tree
 128  
      * @throws IOException if the file could not be read
 129  
      * @throws CheckstyleException if the file is not a valid Java source file
 130  
      */
 131  
     public static DetailAST parseFile(File file, Options options)
 132  
             throws IOException, CheckstyleException {
 133  1056
         final FileText text = new FileText(file.getAbsoluteFile(),
 134  1056
             System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
 135  1055
         return parseFileText(text, options);
 136  
     }
 137  
 
 138  
     /**
 139  
      * Appends comment nodes to existing AST.
 140  
      * It traverses each node in AST, looks for hidden comment tokens
 141  
      * and appends found comment tokens as nodes in AST.
 142  
      * @param root of AST
 143  
      * @return root of AST with comment nodes
 144  
      */
 145  
     public static DetailAST appendHiddenCommentNodes(DetailAST root) {
 146  239
         DetailAST result = root;
 147  239
         DetailAST curNode = root;
 148  239
         DetailAST lastNode = root;
 149  
 
 150  54962
         while (curNode != null) {
 151  54723
             if (isPositionGreater(curNode, lastNode)) {
 152  31005
                 lastNode = curNode;
 153  
             }
 154  
 
 155  54723
             CommonHiddenStreamToken tokenBefore = curNode.getHiddenBefore();
 156  54723
             DetailAST currentSibling = curNode;
 157  88475
             while (tokenBefore != null) {
 158  33752
                 final DetailAST newCommentNode =
 159  33752
                          createCommentAstFromToken(tokenBefore);
 160  
 
 161  33752
                 currentSibling.addPreviousSibling(newCommentNode);
 162  
 
 163  33752
                 if (currentSibling == result) {
 164  234
                     result = newCommentNode;
 165  
                 }
 166  
 
 167  33752
                 currentSibling = newCommentNode;
 168  33752
                 tokenBefore = tokenBefore.getHiddenBefore();
 169  33752
             }
 170  
 
 171  54723
             DetailAST toVisit = curNode.getFirstChild();
 172  109446
             while (curNode != null && toVisit == null) {
 173  54723
                 toVisit = curNode.getNextSibling();
 174  54723
                 if (toVisit == null) {
 175  22890
                     curNode = curNode.getParent();
 176  
                 }
 177  
             }
 178  54723
             curNode = toVisit;
 179  54723
         }
 180  239
         if (lastNode != null) {
 181  233
             CommonHiddenStreamToken tokenAfter = lastNode.getHiddenAfter();
 182  233
             DetailAST currentSibling = lastNode;
 183  254
             while (tokenAfter != null) {
 184  21
                 final DetailAST newCommentNode =
 185  21
                         createCommentAstFromToken(tokenAfter);
 186  
 
 187  21
                 currentSibling.addNextSibling(newCommentNode);
 188  
 
 189  21
                 currentSibling = newCommentNode;
 190  21
                 tokenAfter = tokenAfter.getHiddenAfter();
 191  21
             }
 192  
         }
 193  239
         return result;
 194  
     }
 195  
 
 196  
     /**
 197  
      * Checks if position of first DetailAST is greater than position of
 198  
      * second DetailAST. Position is line number and column number in source file.
 199  
      * @param ast1 first DetailAST node
 200  
      * @param ast2 second DetailAST node
 201  
      * @return true if position of ast1 is greater than position of ast2
 202  
      */
 203  
     private static boolean isPositionGreater(DetailAST ast1, DetailAST ast2) {
 204  54726
         boolean isGreater = ast1.getLineNo() > ast2.getLineNo();
 205  54726
         if (!isGreater && ast1.getLineNo() == ast2.getLineNo()) {
 206  46959
             isGreater = ast1.getColumnNo() > ast2.getColumnNo();
 207  
         }
 208  54726
         return isGreater;
 209  
     }
 210  
 
 211  
     /**
 212  
      * Create comment AST from token. Depending on token type
 213  
      * SINGLE_LINE_COMMENT or BLOCK_COMMENT_BEGIN is created.
 214  
      * @param token to create the AST
 215  
      * @return DetailAST of comment node
 216  
      */
 217  
     private static DetailAST createCommentAstFromToken(Token token) {
 218  
         final DetailAST commentAst;
 219  33773
         if (token.getType() == TokenTypes.SINGLE_LINE_COMMENT) {
 220  31129
             commentAst = createSlCommentNode(token);
 221  
         }
 222  
         else {
 223  2644
             commentAst = CommonUtils.createBlockCommentNode(token);
 224  
         }
 225  33773
         return commentAst;
 226  
     }
 227  
 
 228  
     /**
 229  
      * Create single-line comment from token.
 230  
      * @param token to create the AST
 231  
      * @return DetailAST with SINGLE_LINE_COMMENT type
 232  
      */
 233  
     private static DetailAST createSlCommentNode(Token token) {
 234  31129
         final DetailAST slComment = new DetailAST();
 235  31129
         slComment.setType(TokenTypes.SINGLE_LINE_COMMENT);
 236  31129
         slComment.setText("//");
 237  
 
 238  
         // column counting begins from 0
 239  31129
         slComment.setColumnNo(token.getColumn() - 1);
 240  31129
         slComment.setLineNo(token.getLine());
 241  
 
 242  31129
         final DetailAST slCommentContent = new DetailAST();
 243  31129
         slCommentContent.setType(TokenTypes.COMMENT_CONTENT);
 244  
 
 245  
         // column counting begins from 0
 246  
         // plus length of '//'
 247  31129
         slCommentContent.setColumnNo(token.getColumn() - 1 + 2);
 248  31129
         slCommentContent.setLineNo(token.getLine());
 249  31129
         slCommentContent.setText(token.getText());
 250  
 
 251  31129
         slComment.addChild(slCommentContent);
 252  31129
         return slComment;
 253  
     }
 254  
 
 255  
 }