Java tutorial
/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.hql.internal.ast.util; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.hibernate.hql.internal.ast.tree.DisplayableNode; import org.hibernate.internal.util.StringHelper; import antlr.collections.AST; /** * Utility for generating pretty "ASCII art" representations of syntax trees. * * This class is threadsafe: reuse instances as needed. * * @author Joshua Davis * @author Steve Ebersole */ public class ASTPrinter { // This is a map: array index is the ANTLR Token ID, array value is the name of that token. // There might be gaps in the array (null values) but it's generally quite compact. private final String[] tokenTypeNameCache; /** * Constructs a printer. Package protected: use the constants from {TokenPrinters} * @see TokenPrinters * * @param tokenTypeConstants The token types to use during printing; typically the {vocabulary}TokenTypes.java * interface generated by ANTLR. */ ASTPrinter(Class tokenTypeConstants) { this.tokenTypeNameCache = ASTUtil.generateTokenNameCache(tokenTypeConstants); } /** * Renders the AST into 'ASCII art' form and returns that string representation. * * @param ast The AST to display. * @param header The header for the display. * * @return The AST in 'ASCII art' form, as a string. */ public String showAsString(AST ast, String header) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); ps.println(header); showAst(ast, ps); ps.flush(); return new String(baos.toByteArray()); } /** * Prints the AST in 'ASCII art' form to the specified print stream. * * @param ast The AST to print. * @param out The print stream to which the AST should be printed. */ public void showAst(AST ast, PrintStream out) { showAst(ast, new PrintWriter(out)); } /** * Prints the AST in 'ASCII art' tree form to the specified print writer. * * @param ast The AST to print. * @param pw The print writer to which the AST should be written. */ public void showAst(AST ast, PrintWriter pw) { ArrayList<AST> parents = new ArrayList<AST>(); showAst(parents, pw, ast); pw.flush(); } /** * Returns the token type name for the given token type. * * @param type The token type. * * @return String - The token type name from the token type constant class, * or just the integer as a string if none exists. */ public String getTokenTypeName(int type) { String value = tokenTypeNameCache[type]; if (value == null) { value = Integer.toString(type); } return value; } private void showAst(ArrayList<AST> parents, PrintWriter pw, AST ast) { if (ast == null) { pw.println("AST is null!"); return; } indentLine(parents, pw); if (ast.getNextSibling() == null) { pw.print(" \\-"); } else { pw.print(" +-"); } showNode(pw, ast); showNodeProperties(parents, pw, ast); ArrayList<AST> newParents = new ArrayList<AST>(parents); newParents.add(ast); for (AST child = ast.getFirstChild(); child != null; child = child.getNextSibling()) { showAst(newParents, pw, child); } newParents.clear(); } private void indentLine(List<AST> parents, PrintWriter pw) { for (AST parent : parents) { if (parent.getNextSibling() == null) { pw.print(" "); } else { pw.print(" | "); } } } private void showNode(PrintWriter pw, AST ast) { String s = nodeToString(ast); pw.println(s); } public String nodeToString(AST ast) { if (ast == null) { return "{node:null}"; } StringBuilder buf = new StringBuilder(); buf.append("[").append(getTokenTypeName(ast.getType())).append("] "); buf.append(StringHelper.unqualify(ast.getClass().getName())).append(": "); buf.append("'"); String text = ast.getText(); if (text == null) { text = "{text:null}"; } appendEscapedMultibyteChars(text, buf); buf.append("'"); if (ast instanceof DisplayableNode) { DisplayableNode displayableNode = (DisplayableNode) ast; // Add a space before the display text. buf.append(" ").append(displayableNode.getDisplayText()); } return buf.toString(); } private void showNodeProperties(ArrayList<AST> parents, PrintWriter pw, AST ast) { Map<String, Object> nodeProperties = createNodeProperties(ast); ArrayList<AST> parentsAndNode = new ArrayList<>(parents); parentsAndNode.add(ast); for (String propertyName : nodeProperties.keySet()) { indentLine(parentsAndNode, pw); pw.println(propertyToString(propertyName, nodeProperties.get(propertyName), ast)); } } public LinkedHashMap<String, Object> createNodeProperties(AST ast) { return new LinkedHashMap<>(); } public String propertyToString(String label, Object value, AST ast) { return String.format("%s: %s", label, value); } public static void appendEscapedMultibyteChars(String text, StringBuilder buf) { char[] chars = text.toCharArray(); for (char aChar : chars) { if (aChar > 256) { buf.append("\\u"); buf.append(Integer.toHexString(aChar)); } else { buf.append(aChar); } } } public static String escapeMultibyteChars(String text) { StringBuilder buf = new StringBuilder(); appendEscapedMultibyteChars(text, buf); return buf.toString(); } }