Coverage Report - com.puppycrawl.tools.checkstyle.AstTreeStringPrinter
 
Classes in this File Line Coverage Branch Coverage Complexity
AstTreeStringPrinter
100%
72/72
100%
20/20
1.917
AstTreeStringPrinter$PrintOptions
100%
3/3
N/A
1.917
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 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.nio.charset.StandardCharsets;
 25  
 import java.util.Locale;
 26  
 import java.util.regex.Pattern;
 27  
 
 28  
 import antlr.RecognitionException;
 29  
 import antlr.TokenStreamException;
 30  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 31  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 32  
 import com.puppycrawl.tools.checkstyle.api.DetailNode;
 33  
 import com.puppycrawl.tools.checkstyle.api.FileContents;
 34  
 import com.puppycrawl.tools.checkstyle.api.FileText;
 35  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 36  
 import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;
 37  
 import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
 38  
 
 39  
 /**
 40  
  * Class for printing AST to String.
 41  
  * @author Vladislav Lisetskii
 42  
  */
 43  
 public final class AstTreeStringPrinter {
 44  
 
 45  
     /**
 46  
      * Enum to be used for test if comments should be printed.
 47  
      */
 48  6
     public enum PrintOptions {
 49  
         /**
 50  
          * Comments has to be printed.
 51  
          */
 52  2
         WITH_COMMENTS,
 53  
         /**
 54  
          * Comments has NOT to be printed.
 55  
          */
 56  2
         WITHOUT_COMMENTS
 57  
     }
 58  
 
 59  
     /** Newline pattern. */
 60  2
     private static final Pattern NEWLINE = Pattern.compile("\n");
 61  
     /** Return pattern. */
 62  2
     private static final Pattern RETURN = Pattern.compile("\r");
 63  
     /** Tab pattern. */
 64  2
     private static final Pattern TAB = Pattern.compile("\t");
 65  
 
 66  
     /** OS specific line separator. */
 67  2
     private static final String LINE_SEPARATOR = System.getProperty("line.separator");
 68  
 
 69  
     /** Prevent instances. */
 70  1
     private AstTreeStringPrinter() {
 71  
         // no code
 72  1
     }
 73  
 
 74  
     /**
 75  
      * Parse a file and print the parse tree.
 76  
      * @param file the file to print.
 77  
      * @param withComments true to include comments to AST
 78  
      * @return the AST of the file in String form.
 79  
      * @throws IOException if the file could not be read.
 80  
      * @throws CheckstyleException if the file is not a Java source.
 81  
      */
 82  
     public static String printFileAst(File file, PrintOptions withComments)
 83  
             throws IOException, CheckstyleException {
 84  20
         return printTree(parseFile(file, withComments));
 85  
     }
 86  
 
 87  
     /**
 88  
      * Prints full AST (java + comments + javadoc) of the java file.
 89  
      * @param file java file
 90  
      * @return Full tree
 91  
      * @throws IOException Failed to open a file
 92  
      * @throws CheckstyleException error while parsing the file
 93  
      */
 94  
     public static String printJavaAndJavadocTree(File file)
 95  
             throws IOException, CheckstyleException {
 96  5
         final DetailAST tree = parseFile(file, PrintOptions.WITH_COMMENTS);
 97  5
         return printJavaAndJavadocTree(tree);
 98  
     }
 99  
 
 100  
     /**
 101  
      * Prints full tree (java + comments + javadoc) of the DetailAST.
 102  
      * @param ast root DetailAST
 103  
      * @return Full tree
 104  
      */
 105  
     private static String printJavaAndJavadocTree(DetailAST ast) {
 106  132
         final StringBuilder messageBuilder = new StringBuilder(1024);
 107  132
         DetailAST node = ast;
 108  265
         while (node != null) {
 109  133
             messageBuilder.append(getIndentation(node))
 110  133
                 .append(getNodeInfo(node))
 111  133
                 .append(LINE_SEPARATOR);
 112  133
             if (node.getType() == TokenTypes.COMMENT_CONTENT
 113  9
                     && JavadocUtils.isJavadocComment(node.getParent())) {
 114  6
                 final String javadocTree = parseAndPrintJavadocTree(node);
 115  6
                 messageBuilder.append(javadocTree);
 116  6
             }
 117  
             else {
 118  127
                 messageBuilder.append(printJavaAndJavadocTree(node.getFirstChild()));
 119  
             }
 120  133
             node = node.getNextSibling();
 121  
         }
 122  132
         return messageBuilder.toString();
 123  
     }
 124  
 
 125  
     /**
 126  
      * Parses block comment as javadoc and prints its tree.
 127  
      * @param node block comment begin
 128  
      * @return string javadoc tree
 129  
      */
 130  
     private static String parseAndPrintJavadocTree(DetailAST node) {
 131  6
         final DetailAST javadocBlock = node.getParent();
 132  6
         final DetailNode tree = DetailNodeTreeStringPrinter.parseJavadocAsDetailNode(javadocBlock);
 133  
 
 134  6
         String baseIndentation = getIndentation(node);
 135  6
         baseIndentation = baseIndentation.substring(0, baseIndentation.length() - 2);
 136  6
         final String rootPrefix = baseIndentation + "   `--";
 137  6
         final String prefix = baseIndentation + "       ";
 138  6
         return DetailNodeTreeStringPrinter.printTree(tree, rootPrefix, prefix);
 139  
     }
 140  
 
 141  
     /**
 142  
      * Parse a file and print the parse tree.
 143  
      * @param text the text to parse.
 144  
      * @param withComments true to include comments to AST
 145  
      * @return the AST of the file in String form.
 146  
      * @throws CheckstyleException if the file is not a Java source.
 147  
      */
 148  
     public static String printAst(FileText text,
 149  
                                   PrintOptions withComments) throws CheckstyleException {
 150  13
         return printTree(parseFileText(text, withComments));
 151  
     }
 152  
 
 153  
     /**
 154  
      * Print AST.
 155  
      * @param ast the root AST node.
 156  
      * @return string AST.
 157  
      */
 158  
     private static String printTree(DetailAST ast) {
 159  4854
         final StringBuilder messageBuilder = new StringBuilder(1024);
 160  4854
         DetailAST node = ast;
 161  9676
         while (node != null) {
 162  4822
             messageBuilder.append(getIndentation(node))
 163  4822
                     .append(getNodeInfo(node))
 164  4822
                     .append(LINE_SEPARATOR)
 165  4822
                     .append(printTree(node.getFirstChild()));
 166  4822
             node = node.getNextSibling();
 167  
         }
 168  4854
         return messageBuilder.toString();
 169  
     }
 170  
 
 171  
     /**
 172  
      * Get string representation of the node as token name,
 173  
      * node text, line number and column number.
 174  
      * @param node DetailAST
 175  
      * @return node info
 176  
      */
 177  
     private static String getNodeInfo(DetailAST node) {
 178  9910
         return TokenUtils.getTokenName(node.getType())
 179  4955
                 + " -> " + escapeAllControlChars(node.getText())
 180  4955
                 + " [" + node.getLineNo() + ':' + node.getColumnNo() + ']';
 181  
     }
 182  
 
 183  
     /**
 184  
      * Get indentation for an AST node.
 185  
      * @param ast the AST to get the indentation for.
 186  
      * @return the indentation in String format.
 187  
      */
 188  
     private static String getIndentation(DetailAST ast) {
 189  4961
         final boolean isLastChild = ast.getNextSibling() == null;
 190  4961
         DetailAST node = ast;
 191  4961
         final StringBuilder indentation = new StringBuilder(1024);
 192  28634
         while (node.getParent() != null) {
 193  23673
             node = node.getParent();
 194  23673
             if (node.getParent() == null) {
 195  4864
                 if (isLastChild) {
 196  
                     // only ASCII symbols must be used due to
 197  
                     // problems with running tests on Windows
 198  1960
                     indentation.append("`--");
 199  
                 }
 200  
                 else {
 201  2904
                     indentation.append("|--");
 202  
                 }
 203  
             }
 204  
             else {
 205  18809
                 if (node.getNextSibling() == null) {
 206  9247
                     indentation.insert(0, "    ");
 207  
                 }
 208  
                 else {
 209  9562
                     indentation.insert(0, "|   ");
 210  
                 }
 211  
             }
 212  
         }
 213  4961
         return indentation.toString();
 214  
     }
 215  
 
 216  
     /**
 217  
      * Replace all control chars with escaped symbols.
 218  
      * @param text the String to process.
 219  
      * @return the processed String with all control chars escaped.
 220  
      */
 221  
     private static String escapeAllControlChars(String text) {
 222  4955
         final String textWithoutNewlines = NEWLINE.matcher(text).replaceAll("\\\\n");
 223  4955
         final String textWithoutReturns = RETURN.matcher(textWithoutNewlines).replaceAll("\\\\r");
 224  4955
         return TAB.matcher(textWithoutReturns).replaceAll("\\\\t");
 225  
     }
 226  
 
 227  
     /**
 228  
      * Parse a file and return the parse tree.
 229  
      * @param file the file to parse.
 230  
      * @param withComments true to include comment nodes to the tree
 231  
      * @return the root node of the parse tree.
 232  
      * @throws IOException if the file could not be read.
 233  
      * @throws CheckstyleException if the file is not a Java source.
 234  
      */
 235  
     private static DetailAST parseFile(File file, PrintOptions withComments)
 236  
             throws IOException, CheckstyleException {
 237  25
         final FileText text = new FileText(file.getAbsoluteFile(),
 238  25
             System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
 239  25
         return parseFileText(text, withComments);
 240  
     }
 241  
 
 242  
     /**
 243  
      * Parse a text and return the parse tree.
 244  
      * @param text the text to parse.
 245  
      * @param withComments true to include comment nodes to the tree
 246  
      * @return the root node of the parse tree.
 247  
      * @throws CheckstyleException if the file is not a Java source.
 248  
      */
 249  
     private static DetailAST parseFileText(FileText text, PrintOptions withComments)
 250  
             throws CheckstyleException {
 251  38
         final FileContents contents = new FileContents(text);
 252  
         final DetailAST result;
 253  
         try {
 254  38
             if (withComments == PrintOptions.WITH_COMMENTS) {
 255  19
                 result = TreeWalker.parseWithComments(contents);
 256  
             }
 257  
             else {
 258  19
                 result = TreeWalker.parse(contents);
 259  
             }
 260  
         }
 261  1
         catch (RecognitionException | TokenStreamException ex) {
 262  2
             final String exceptionMsg = String.format(Locale.ROOT,
 263  
                 "%s occurred during the analysis of file %s.",
 264  1
                 ex.getClass().getSimpleName(), text.getFile().getPath());
 265  1
             throw new CheckstyleException(exceptionMsg, ex);
 266  37
         }
 267  
 
 268  37
         return result;
 269  
     }
 270  
 }