001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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; 024import java.nio.charset.Charset; 025 026import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseErrorMessage; 027import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseStatus; 028import com.puppycrawl.tools.checkstyle.api.DetailAST; 029import com.puppycrawl.tools.checkstyle.api.DetailNode; 030import com.puppycrawl.tools.checkstyle.api.FileText; 031import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes; 032import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 033import com.puppycrawl.tools.checkstyle.utils.ParserUtil; 034 035/** 036 * Parses file as javadoc DetailNode tree and prints to system output stream. 037 */ 038public final class DetailNodeTreeStringPrinter { 039 040 /** OS specific line separator. */ 041 private static final String LINE_SEPARATOR = System.lineSeparator(); 042 043 /** Prevent instances. */ 044 private DetailNodeTreeStringPrinter() { 045 // no code 046 } 047 048 /** 049 * Parse a file and print the parse tree. 050 * 051 * @param file the file to print. 052 * @return parse tree as a string 053 * @throws IOException if the file could not be read. 054 */ 055 public static String printFileAst(File file) throws IOException { 056 return printTree(parseFile(file), "", ""); 057 } 058 059 /** 060 * Parse block comment DetailAST as Javadoc DetailNode tree. 061 * 062 * @param blockComment DetailAST 063 * @return DetailNode tree 064 * @throws IllegalArgumentException if there is an error parsing the Javadoc. 065 */ 066 public static DetailNode parseJavadocAsDetailNode(DetailAST blockComment) { 067 final JavadocDetailNodeParser parser = new JavadocDetailNodeParser(); 068 final ParseStatus status = parser.parseJavadocComment(blockComment); 069 final ParseErrorMessage parseErrorMessage = status.getParseErrorMessage(); 070 if (parseErrorMessage != null) { 071 throw new IllegalArgumentException(getParseErrorMessage(parseErrorMessage)); 072 } 073 return status.getTree(); 074 } 075 076 /** 077 * Builds violation base on ParseErrorMessage's violation key, its arguments, etc. 078 * 079 * @param parseErrorMessage ParseErrorMessage 080 * @return error violation 081 */ 082 private static String getParseErrorMessage(ParseErrorMessage parseErrorMessage) { 083 final LocalizedMessage message = new LocalizedMessage( 084 "com.puppycrawl.tools.checkstyle.checks.javadoc.messages", 085 DetailNodeTreeStringPrinter.class, 086 parseErrorMessage.getMessageKey(), 087 parseErrorMessage.getMessageArguments()); 088 return "[ERROR:" + parseErrorMessage.getLineNumber() + "] " + message.getMessage(); 089 } 090 091 /** 092 * Print AST. 093 * 094 * @param ast the root AST node. 095 * @param rootPrefix prefix for the root node 096 * @param prefix prefix for other nodes 097 * @return string AST. 098 */ 099 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}