View Javadoc
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.util.regex.Pattern;
25  
26  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.DetailNode;
29  import com.puppycrawl.tools.checkstyle.api.FileText;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;
32  import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
33  
34  /**
35   * Class for printing AST to String.
36   * @author Vladislav Lisetskii
37   */
38  public final class AstTreeStringPrinter {
39  
40      /** Newline pattern. */
41      private static final Pattern NEWLINE = Pattern.compile("\n");
42      /** Return pattern. */
43      private static final Pattern RETURN = Pattern.compile("\r");
44      /** Tab pattern. */
45      private static final Pattern TAB = Pattern.compile("\t");
46  
47      /** OS specific line separator. */
48      private static final String LINE_SEPARATOR = System.getProperty("line.separator");
49  
50      /** Prevent instances. */
51      private AstTreeStringPrinter() {
52          // no code
53      }
54  
55      /**
56       * Parse a file and print the parse tree.
57       * @param file the file to print.
58       * @param options {@link JavaParser.Options} to control the inclusion of comment nodes.
59       * @return the AST of the file in String form.
60       * @throws IOException if the file could not be read.
61       * @throws CheckstyleException if the file is not a Java source.
62       */
63      public static String printFileAst(File file, JavaParser.Options options)
64              throws IOException, CheckstyleException {
65          return printTree(JavaParser.parseFile(file, options));
66      }
67  
68      /**
69       * Prints full AST (java + comments + javadoc) of the java file.
70       * @param file java file
71       * @return Full tree
72       * @throws IOException Failed to open a file
73       * @throws CheckstyleException error while parsing the file
74       */
75      public static String printJavaAndJavadocTree(File file)
76              throws IOException, CheckstyleException {
77          final DetailAST tree = JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS);
78          return printJavaAndJavadocTree(tree);
79      }
80  
81      /**
82       * Prints full tree (java + comments + javadoc) of the DetailAST.
83       * @param ast root DetailAST
84       * @return Full tree
85       */
86      private static String printJavaAndJavadocTree(DetailAST ast) {
87          final StringBuilder messageBuilder = new StringBuilder(1024);
88          DetailAST node = ast;
89          while (node != null) {
90              messageBuilder.append(getIndentation(node))
91                  .append(getNodeInfo(node))
92                  .append(LINE_SEPARATOR);
93              if (node.getType() == TokenTypes.COMMENT_CONTENT
94                      && JavadocUtils.isJavadocComment(node.getParent())) {
95                  final String javadocTree = parseAndPrintJavadocTree(node);
96                  messageBuilder.append(javadocTree);
97              }
98              else {
99                  messageBuilder.append(printJavaAndJavadocTree(node.getFirstChild()));
100             }
101             node = node.getNextSibling();
102         }
103         return messageBuilder.toString();
104     }
105 
106     /**
107      * Parses block comment as javadoc and prints its tree.
108      * @param node block comment begin
109      * @return string javadoc tree
110      */
111     private static String parseAndPrintJavadocTree(DetailAST node) {
112         final DetailAST javadocBlock = node.getParent();
113         final DetailNode tree = DetailNodeTreeStringPrinter.parseJavadocAsDetailNode(javadocBlock);
114 
115         String baseIndentation = getIndentation(node);
116         baseIndentation = baseIndentation.substring(0, baseIndentation.length() - 2);
117         final String rootPrefix = baseIndentation + "   `--";
118         final String prefix = baseIndentation + "       ";
119         return DetailNodeTreeStringPrinter.printTree(tree, rootPrefix, prefix);
120     }
121 
122     /**
123      * Parse a file and print the parse tree.
124      * @param text the text to parse.
125      * @param options {@link JavaParser.Options} to control the inclusion of comment nodes.
126      * @return the AST of the file in String form.
127      * @throws CheckstyleException if the file is not a Java source.
128      */
129     public static String printAst(FileText text, JavaParser.Options options)
130             throws CheckstyleException {
131         final DetailAST ast = JavaParser.parseFileText(text, options);
132         return printTree(ast);
133     }
134 
135     /**
136      * Print AST.
137      * @param ast the root AST node.
138      * @return string AST.
139      */
140     private static String printTree(DetailAST ast) {
141         final StringBuilder messageBuilder = new StringBuilder(1024);
142         DetailAST node = ast;
143         while (node != null) {
144             messageBuilder.append(getIndentation(node))
145                     .append(getNodeInfo(node))
146                     .append(LINE_SEPARATOR)
147                     .append(printTree(node.getFirstChild()));
148             node = node.getNextSibling();
149         }
150         return messageBuilder.toString();
151     }
152 
153     /**
154      * Get string representation of the node as token name,
155      * node text, line number and column number.
156      * @param node DetailAST
157      * @return node info
158      */
159     private static String getNodeInfo(DetailAST node) {
160         return TokenUtils.getTokenName(node.getType())
161                 + " -> " + escapeAllControlChars(node.getText())
162                 + " [" + node.getLineNo() + ':' + node.getColumnNo() + ']';
163     }
164 
165     /**
166      * Get indentation for an AST node.
167      * @param ast the AST to get the indentation for.
168      * @return the indentation in String format.
169      */
170     private static String getIndentation(DetailAST ast) {
171         final boolean isLastChild = ast.getNextSibling() == null;
172         DetailAST node = ast;
173         final StringBuilder indentation = new StringBuilder(1024);
174         while (node.getParent() != null) {
175             node = node.getParent();
176             if (node.getParent() == null) {
177                 if (isLastChild) {
178                     // only ASCII symbols must be used due to
179                     // problems with running tests on Windows
180                     indentation.append("`--");
181                 }
182                 else {
183                     indentation.append("|--");
184                 }
185             }
186             else {
187                 if (node.getNextSibling() == null) {
188                     indentation.insert(0, "    ");
189                 }
190                 else {
191                     indentation.insert(0, "|   ");
192                 }
193             }
194         }
195         return indentation.toString();
196     }
197 
198     /**
199      * Replace all control chars with escaped symbols.
200      * @param text the String to process.
201      * @return the processed String with all control chars escaped.
202      */
203     private static String escapeAllControlChars(String text) {
204         final String textWithoutNewlines = NEWLINE.matcher(text).replaceAll("\\\\n");
205         final String textWithoutReturns = RETURN.matcher(textWithoutNewlines).replaceAll("\\\\r");
206         return TAB.matcher(textWithoutReturns).replaceAll("\\\\t");
207     }
208 
209 }