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}