View Javadoc
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  
26  import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseErrorMessage;
27  import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseStatus;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.DetailNode;
30  import com.puppycrawl.tools.checkstyle.api.FileText;
31  import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
32  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
33  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
34  import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;
35  
36  /**
37   * Parses file as javadoc DetailNode tree and prints to system output stream.
38   * @author bizmailov
39   */
40  public final class DetailNodeTreeStringPrinter {
41  
42      /** OS specific line separator. */
43      private static final String LINE_SEPARATOR = System.getProperty("line.separator");
44  
45      /** Prevent instances. */
46      private DetailNodeTreeStringPrinter() {
47          // no code
48      }
49  
50      /**
51       * Parse a file and print the parse tree.
52       * @param file the file to print.
53       * @return parse tree as a string
54       * @throws IOException if the file could not be read.
55       */
56      public static String printFileAst(File file) throws IOException {
57          return printTree(parseFile(file), "", "");
58      }
59  
60      /**
61       * Parse block comment DetailAST as Javadoc DetailNode tree.
62       * @param blockComment DetailAST
63       * @return DetailNode tree
64       */
65      public static DetailNode parseJavadocAsDetailNode(DetailAST blockComment) {
66          final JavadocDetailNodeParser parser = new JavadocDetailNodeParser();
67          final ParseStatus status = parser.parseJavadocAsDetailNode(blockComment);
68          if (status.getParseErrorMessage() != null) {
69              throw new IllegalArgumentException(getParseErrorMessage(status.getParseErrorMessage()));
70          }
71          return status.getTree();
72      }
73  
74      /**
75       * Parse javadoc comment to DetailNode tree.
76       * @param javadocComment javadoc comment content
77       * @return tree
78       */
79      private static DetailNode parseJavadocAsDetailNode(String javadocComment) {
80          final DetailAST blockComment = CommonUtils.createBlockCommentNode(javadocComment);
81          return parseJavadocAsDetailNode(blockComment);
82      }
83  
84      /**
85       * Builds error message base on ParseErrorMessage's message key, its arguments, etc.
86       * @param parseErrorMessage ParseErrorMessage
87       * @return error message
88       */
89      private static String getParseErrorMessage(ParseErrorMessage parseErrorMessage) {
90          final LocalizedMessage lmessage = new LocalizedMessage(
91                  parseErrorMessage.getLineNumber(),
92                  "com.puppycrawl.tools.checkstyle.checks.javadoc.messages",
93                  parseErrorMessage.getMessageKey(),
94                  parseErrorMessage.getMessageArguments(),
95                  "",
96                  DetailNodeTreeStringPrinter.class,
97                  null);
98          return "[ERROR:" + parseErrorMessage.getLineNumber() + "] " + lmessage.getMessage();
99      }
100 
101     /**
102      * Print AST.
103      * @param ast the root AST node.
104      * @param rootPrefix prefix for the root node
105      * @param prefix prefix for other nodes
106      * @return string AST.
107      */
108     public static String printTree(DetailNode ast, String rootPrefix, String prefix) {
109         final StringBuilder messageBuilder = new StringBuilder(1024);
110         DetailNode node = ast;
111         while (node != null) {
112             if (node.getType() == JavadocTokenTypes.JAVADOC) {
113                 messageBuilder.append(rootPrefix);
114             }
115             else {
116                 messageBuilder.append(prefix);
117             }
118             messageBuilder.append(getIndentation(node))
119                     .append(JavadocUtils.getTokenName(node.getType())).append(" -> ")
120                     .append(JavadocUtils.escapeAllControlChars(node.getText())).append(" [")
121                     .append(node.getLineNumber()).append(':').append(node.getColumnNumber())
122                     .append(']').append(LINE_SEPARATOR)
123                     .append(printTree(JavadocUtils.getFirstChild(node), rootPrefix, prefix));
124             node = JavadocUtils.getNextSibling(node);
125         }
126         return messageBuilder.toString();
127     }
128 
129     /**
130      * Get indentation for a node.
131      * @param node the DetailNode to get the indentation for.
132      * @return the indentation in String format.
133      */
134     private static String getIndentation(DetailNode node) {
135         final boolean isLastChild = JavadocUtils.getNextSibling(node) == null;
136         DetailNode currentNode = node;
137         final StringBuilder indentation = new StringBuilder(1024);
138         while (currentNode.getParent() != null) {
139             currentNode = currentNode.getParent();
140             if (currentNode.getParent() == null) {
141                 if (isLastChild) {
142                     // only ASCII symbols must be used due to
143                     // problems with running tests on Windows
144                     indentation.append("`--");
145                 }
146                 else {
147                     indentation.append("|--");
148                 }
149             }
150             else {
151                 if (JavadocUtils.getNextSibling(currentNode) == null) {
152                     indentation.insert(0, "    ");
153                 }
154                 else {
155                     indentation.insert(0, "|   ");
156                 }
157             }
158         }
159         return indentation.toString();
160     }
161 
162     /**
163      * Parse a file and return the parse tree.
164      * @param file the file to parse.
165      * @return the root node of the parse tree.
166      * @throws IOException if the file could not be read.
167      */
168     private static DetailNode parseFile(File file) throws IOException {
169         final FileText text = new FileText(file.getAbsoluteFile(),
170             System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
171         return parseJavadocAsDetailNode(text.getFullText().toString());
172     }
173 
174 }