001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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.util.Locale; 025import java.util.regex.Pattern; 026 027import antlr.RecognitionException; 028import antlr.TokenStreamException; 029import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.FileContents; 032import com.puppycrawl.tools.checkstyle.api.FileText; 033import com.puppycrawl.tools.checkstyle.utils.TokenUtils; 034 035/** 036 * Class for printing AST to String. 037 * @author Vladislav Lisetskii 038 */ 039public final class AstTreeStringPrinter { 040 041 /** Newline pattern. */ 042 private static final Pattern NEWLINE = Pattern.compile("\n"); 043 /** Return pattern. */ 044 private static final Pattern RETURN = Pattern.compile("\r"); 045 /** Tab pattern. */ 046 private static final Pattern TAB = Pattern.compile("\t"); 047 048 /** OS specific line separator. */ 049 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 050 051 /** Prevent instances. */ 052 private AstTreeStringPrinter() { 053 // no code 054 } 055 056 /** 057 * Parse a file and print the parse tree. 058 * @param file the file to print. 059 * @param withComments true to include comments to AST 060 * @return the AST of the file in String form. 061 * @throws IOException if the file could not be read. 062 * @throws CheckstyleException if the file is not a Java source. 063 */ 064 public static String printFileAst(File file, boolean withComments) 065 throws IOException, CheckstyleException { 066 return printTree(parseFile(file, withComments)); 067 } 068 069 /** 070 * Print AST. 071 * @param ast the root AST node. 072 * @return string AST. 073 */ 074 private static String printTree(DetailAST ast) { 075 final StringBuilder messageBuilder = new StringBuilder(); 076 DetailAST node = ast; 077 while (node != null) { 078 messageBuilder.append(getIndentation(node)) 079 .append(TokenUtils.getTokenName(node.getType())).append(" -> ") 080 .append(excapeAllControlChars(node.getText())).append(" [") 081 .append(node.getLineNo()).append(':').append(node.getColumnNo()).append(']') 082 .append(LINE_SEPARATOR) 083 .append(printTree(node.getFirstChild())); 084 node = node.getNextSibling(); 085 } 086 return messageBuilder.toString(); 087 } 088 089 /** 090 * Get indentation for an AST node. 091 * @param ast the AST to get the indentation for. 092 * @return the indentation in String format. 093 */ 094 private static String getIndentation(DetailAST ast) { 095 final boolean isLastChild = ast.getNextSibling() == null; 096 DetailAST node = ast; 097 final StringBuilder indentation = new StringBuilder(); 098 while (node.getParent() != null) { 099 node = node.getParent(); 100 if (node.getParent() == null) { 101 if (isLastChild) { 102 // only ASCII symbols must be used due to 103 // problems with running tests on Windows 104 indentation.append("`--"); 105 } 106 else { 107 indentation.append("|--"); 108 } 109 } 110 else { 111 if (node.getNextSibling() == null) { 112 indentation.insert(0, " "); 113 } 114 else { 115 indentation.insert(0, "| "); 116 } 117 } 118 } 119 return indentation.toString(); 120 } 121 122 /** 123 * Replace all control chars with excaped symbols. 124 * @param text the String to process. 125 * @return the processed String with all control chars excaped. 126 */ 127 private static String excapeAllControlChars(String text) { 128 final String textWithoutNewlines = NEWLINE.matcher(text).replaceAll("\\\\n"); 129 final String textWithoutReturns = RETURN.matcher(textWithoutNewlines).replaceAll("\\\\r"); 130 return TAB.matcher(textWithoutReturns).replaceAll("\\\\t"); 131 } 132 133 /** 134 * Parse a file and return the parse tree. 135 * @param file the file to parse. 136 * @param withComments true to include comment nodes to the tree 137 * @return the root node of the parse tree. 138 * @throws IOException if the file could not be read. 139 * @throws CheckstyleException if the file is not a Java source. 140 */ 141 private static DetailAST parseFile(File file, boolean withComments) 142 throws IOException, CheckstyleException { 143 final FileText text = new FileText(file.getAbsoluteFile(), 144 System.getProperty("file.encoding", "UTF-8")); 145 final FileContents contents = new FileContents(text); 146 final DetailAST result; 147 try { 148 if (withComments) { 149 result = TreeWalker.parseWithComments(contents); 150 } 151 else { 152 result = TreeWalker.parse(contents); 153 } 154 } 155 catch (RecognitionException | TokenStreamException ex) { 156 final String exceptionMsg = String.format(Locale.ROOT, 157 "%s occurred during the analysis of file %s.", 158 ex.getClass().getSimpleName(), file.getPath()); 159 throw new CheckstyleException(exceptionMsg, ex); 160 } 161 162 return result; 163 } 164}