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