001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle;
021
022import java.io.File;
023import java.io.IOException;
024
025import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseErrorMessage;
026import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseStatus;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.DetailNode;
029import com.puppycrawl.tools.checkstyle.api.FileText;
030import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
032import com.puppycrawl.tools.checkstyle.utils.ParserUtil;
033
034/**
035 * Parses file as javadoc DetailNode tree and prints to system output stream.
036 */
037public final class DetailNodeTreeStringPrinter {
038
039 /** OS specific line separator. */
040 private static final String LINE_SEPARATOR = System.getProperty("line.separator");
041
042 /** Prevent instances. */
043 private DetailNodeTreeStringPrinter() {
044 // no code
045 }
046
047 /**
048 * Parse a file and print the parse tree.
049 *
050 * @param file the file to print.
051 * @return parse tree as a string
052 * @throws IOException if the file could not be read.
053 */
054 public static String printFileAst(File file) throws IOException {
055 return printTree(parseFile(file), "", "");
056 }
057
058 /**
059 * Parse block comment DetailAST as Javadoc DetailNode tree.
060 *
061 * @param blockComment DetailAST
062 * @return DetailNode tree
063 * @throws IllegalArgumentException if there is an error parsing the Javadoc.
064 */
065 public static DetailNode parseJavadocAsDetailNode(DetailAST blockComment) {
066 final JavadocDetailNodeParser parser = new JavadocDetailNodeParser();
067 final ParseStatus status = parser.parseJavadocAsDetailNode(blockComment);
068 if (status.getParseErrorMessage() != null) {
069 throw new IllegalArgumentException(getParseErrorMessage(status.getParseErrorMessage()));
070 }
071 return status.getTree();
072 }
073
074 /**
075 * Parse javadoc comment to DetailNode tree.
076 *
077 * @param javadocComment javadoc comment content
078 * @return tree
079 */
080 private static DetailNode parseJavadocAsDetailNode(String javadocComment) {
081 final DetailAST blockComment = ParserUtil.createBlockCommentNode(javadocComment);
082 return parseJavadocAsDetailNode(blockComment);
083 }
084
085 /**
086 * Builds violation base on ParseErrorMessage's violation key, its arguments, etc.
087 *
088 * @param parseErrorMessage ParseErrorMessage
089 * @return error violation
090 */
091 private static String getParseErrorMessage(ParseErrorMessage parseErrorMessage) {
092 final LocalizedMessage message = new LocalizedMessage(
093 "com.puppycrawl.tools.checkstyle.checks.javadoc.messages",
094 DetailNodeTreeStringPrinter.class,
095 parseErrorMessage.getMessageKey(),
096 parseErrorMessage.getMessageArguments());
097 return "[ERROR:" + parseErrorMessage.getLineNumber() + "] " + message.getMessage();
098 }
099
100 /**
101 * Print AST.
102 *
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(JavadocUtil.getTokenName(node.getType())).append(" -> ")
120 .append(JavadocUtil.escapeAllControlChars(node.getText())).append(" [")
121 .append(node.getLineNumber()).append(':').append(node.getColumnNumber())
122 .append(']').append(LINE_SEPARATOR)
123 .append(printTree(JavadocUtil.getFirstChild(node), rootPrefix, prefix));
124 node = JavadocUtil.getNextSibling(node);
125 }
126 return messageBuilder.toString();
127 }
128
129 /**
130 * Get indentation for a node.
131 *
132 * @param node the DetailNode to get the indentation for.
133 * @return the indentation in String format.
134 */
135 private static String getIndentation(DetailNode node) {
136 final boolean isLastChild = JavadocUtil.getNextSibling(node) == null;
137 DetailNode currentNode = node;
138 final StringBuilder indentation = new StringBuilder(1024);
139 while (currentNode.getParent() != null) {
140 currentNode = currentNode.getParent();
141 if (currentNode.getParent() == null) {
142 if (isLastChild) {
143 // only ASCII symbols must be used due to
144 // problems with running tests on Windows
145 indentation.append("`--");
146 }
147 else {
148 indentation.append("|--");
149 }
150 }
151 else {
152 if (JavadocUtil.getNextSibling(currentNode) == null) {
153 indentation.insert(0, " ");
154 }
155 else {
156 indentation.insert(0, "| ");
157 }
158 }
159 }
160 return indentation.toString();
161 }
162
163 /**
164 * Parse a file and return the parse tree.
165 *
166 * @param file the file to parse.
167 * @return the root node of the parse tree.
168 * @throws IOException if the file could not be read.
169 */
170 private static DetailNode parseFile(File file) throws IOException {
171 final FileText text = new FileText(file, System.getProperty("file.encoding"));
172 return parseJavadocAsDetailNode(text.getFullText().toString());
173 }
174
175}