org.hibernate.hql.ast.util.ASTPrinter Maven / Gradle / Ivy
The newest version!
// $Id: ASTPrinter.java 7460 2005-07-12 20:27:29Z steveebersole $
package org.hibernate.hql.ast.util;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.hql.ast.tree.DisplayableNode;
import org.hibernate.util.StringHelper;
import antlr.collections.AST;
/**
* An 'ASCII art' AST printer for debugging ANTLR grammars.
*
* @author Joshua Davis ([email protected])
*/
public class ASTPrinter {
private Map tokenTypeNamesByTokenType;
private Class tokenTypeConstants;
private boolean showClassNames = true;
/**
* Constructs an org.hibernate.hql.antlr.ASTPrinter, given the class that contains the token type
* constants (typically the '{grammar}TokenTypes' interface generated by
* ANTLR).
*
* @param tokenTypeConstants The class with token type constants in it.
*/
public ASTPrinter(Class tokenTypeConstants) {
this.tokenTypeConstants = tokenTypeConstants;
}
/**
* Returns true if the node class names will be displayed.
*
* @return true if the node class names will be displayed.
*/
public boolean isShowClassNames() {
return showClassNames;
}
/**
* Enables or disables AST node class name display.
*
* @param showClassNames true to enable class name display, false to disable
*/
public void setShowClassNames(boolean showClassNames) {
this.showClassNames = showClassNames;
}
/**
* Prints the AST in 'ASCII art' tree form to the specified print stream.
*
* @param ast The AST to print.
* @param out The print stream.
*/
private 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.
*/
public void showAst(AST ast, PrintWriter pw) {
ArrayList parents = new ArrayList();
showAst( parents, pw, ast );
pw.flush();
}
/**
* Prints the AST in 'ASCII art' tree form into a string.
*
* @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() );
}
/**
* Get a single token type name in the specified set of token type constants (interface).
*
* @param tokenTypeConstants Token type constants interface (e.g. HqlSqlTokenTypes.class).
* @param type The token type ( typically from ast.getType() ).
* @return The token type name, *or* the integer value if the name could not be found for some reason.
*/
public static String getConstantName(Class tokenTypeConstants, int type) {
String tokenTypeName = null;
if ( tokenTypeConstants != null ) {
Field[] fields = tokenTypeConstants.getFields();
for ( int i = 0; i < fields.length; i++ ) {
Field field = fields[i];
tokenTypeName = getTokenTypeName( field, type, true );
if ( tokenTypeName != null ) {
break; // Stop if found.
}
} // for
} // if type constants were provided
// Use the integer value if no token type name was found
if ( tokenTypeName == null ) {
tokenTypeName = Integer.toString( type );
}
return tokenTypeName;
}
private static String getTokenTypeName(Field field, int type, boolean checkType) {
if ( Modifier.isStatic( field.getModifiers() ) ) {
try {
Object value = field.get( null );
if ( !checkType ) {
return field.getName();
}
else if ( value instanceof Integer ) {
Integer integer = ( Integer ) value;
if ( integer.intValue() == type ) {
return field.getName();
}
} // if value is an integer
} // try
catch ( IllegalArgumentException ignore ) {
}
catch ( IllegalAccessException ignore ) {
}
} // if the field is static
return null;
}
/**
* 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.
*/
private String getTokenTypeName(int type) {
// If the class with the constants in it was not supplied, just
// use the integer token type as the token type name.
if ( tokenTypeConstants == null ) {
return Integer.toString( type );
}
// Otherwise, create a type id -> name map from the class if it
// hasn't already been created.
if ( tokenTypeNamesByTokenType == null ) {
Field[] fields = tokenTypeConstants.getFields();
tokenTypeNamesByTokenType = new HashMap();
String tokenTypeName = null;
for ( int i = 0; i < fields.length; i++ ) {
Field field = fields[i];
tokenTypeName = getTokenTypeName( field, type, false );
if ( tokenTypeName != null ) {
try {
tokenTypeNamesByTokenType.put( field.get( null ), field.getName() );
}
catch ( IllegalAccessException ignore ) {
}
}
} // for
} // if the map hasn't been created.
return ( String ) tokenTypeNamesByTokenType.get( new Integer( type ) );
}
private void showAst(ArrayList parents, PrintWriter pw, AST ast) {
if ( ast == null ) {
pw.println( "AST is null!" );
return;
}
for ( int i = 0; i < parents.size(); i++ ) {
AST parent = ( AST ) parents.get( i );
if ( parent.getNextSibling() == null ) {
pw.print( " " );
}
else {
pw.print( " | " );
}
}
if ( ast.getNextSibling() == null ) {
pw.print( " \\-" );
}
else {
pw.print( " +-" );
}
showNode( pw, ast );
ArrayList newParents = new ArrayList( parents );
newParents.add( ast );
for ( AST child = ast.getFirstChild(); child != null; child = child.getNextSibling() ) {
showAst( newParents, pw, child );
}
newParents.clear();
}
private void showNode(PrintWriter pw, AST ast) {
String s = nodeToString( ast, isShowClassNames() );
pw.println( s );
}
public String nodeToString(AST ast, boolean showClassName) {
if ( ast == null ) {
return "{null}";
}
StringBuffer buf = new StringBuffer();
buf.append( "[" ).append( getTokenTypeName( ast.getType() ) ).append( "] " );
if ( showClassName ) {
buf.append( StringHelper.unqualify( ast.getClass().getName() ) ).append( ": " );
}
buf.append( "'" );
String text = ast.getText();
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() );
}
String s = buf.toString();
return s;
}
public static void appendEscapedMultibyteChars(String text, StringBuffer buf) {
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++) {
char aChar = chars[i];
if (aChar > 256) {
buf.append("\\u");
buf.append(Integer.toHexString(aChar));
}
else
buf.append(aChar);
}
}
public static String escapeMultibyteChars(String text)
{
StringBuffer buf = new StringBuffer();
appendEscapedMultibyteChars(text,buf);
return buf.toString();
}
}