langtools/src/share/classes/com/sun/tools/javac/tree/Pretty.java
changeset 10 06bc494ca11e
child 1260 a772ba9ba43d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1224 @@
+/*
+ * Copyright 1999-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.tree;
+
+import java.io.*;
+import java.util.*;
+
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.code.*;
+
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.tree.JCTree.*;
+
+import static com.sun.tools.javac.code.Flags.*;
+
+/** Prints out a tree as an indented Java source program.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class Pretty extends JCTree.Visitor {
+
+    public Pretty(Writer out, boolean sourceOutput) {
+        this.out = out;
+        this.sourceOutput = sourceOutput;
+    }
+
+    /** Set when we are producing source output.  If we're not
+     *  producing source output, we can sometimes give more detail in
+     *  the output even though that detail would not be valid java
+     *  soruce.
+     */
+    private final boolean sourceOutput;
+
+    /** The output stream on which trees are printed.
+     */
+    Writer out;
+
+    /** Indentation width (can be reassigned from outside).
+     */
+    public int width = 4;
+
+    /** The current left margin.
+     */
+    int lmargin = 0;
+
+    /** The enclosing class name.
+     */
+    Name enclClassName;
+
+    /** A hashtable mapping trees to their documentation comments
+     *  (can be null)
+     */
+    Map<JCTree, String> docComments = null;
+
+    /** Align code to be indented to left margin.
+     */
+    void align() throws IOException {
+        for (int i = 0; i < lmargin; i++) out.write(" ");
+    }
+
+    /** Increase left margin by indentation width.
+     */
+    void indent() {
+        lmargin = lmargin + width;
+    }
+
+    /** Decrease left margin by indentation width.
+     */
+    void undent() {
+        lmargin = lmargin - width;
+    }
+
+    /** Enter a new precedence level. Emit a `(' if new precedence level
+     *  is less than precedence level so far.
+     *  @param contextPrec    The precedence level in force so far.
+     *  @param ownPrec        The new precedence level.
+     */
+    void open(int contextPrec, int ownPrec) throws IOException {
+        if (ownPrec < contextPrec) out.write("(");
+    }
+
+    /** Leave precedence level. Emit a `(' if inner precedence level
+     *  is less than precedence level we revert to.
+     *  @param contextPrec    The precedence level we revert to.
+     *  @param ownPrec        The inner precedence level.
+     */
+    void close(int contextPrec, int ownPrec) throws IOException {
+        if (ownPrec < contextPrec) out.write(")");
+    }
+
+    /** Print string, replacing all non-ascii character with unicode escapes.
+     */
+    public void print(Object s) throws IOException {
+        out.write(Convert.escapeUnicode(s.toString()));
+    }
+
+    /** Print new line.
+     */
+    public void println() throws IOException {
+        out.write(lineSep);
+    }
+
+    String lineSep = System.getProperty("line.separator");
+
+    /**************************************************************************
+     * Traversal methods
+     *************************************************************************/
+
+    /** Exception to propogate IOException through visitXXX methods */
+    private static class UncheckedIOException extends Error {
+        static final long serialVersionUID = -4032692679158424751L;
+        UncheckedIOException(IOException e) {
+            super(e.getMessage(), e);
+        }
+    }
+
+    /** Visitor argument: the current precedence level.
+     */
+    int prec;
+
+    /** Visitor method: print expression tree.
+     *  @param prec  The current precedence level.
+     */
+    public void printExpr(JCTree tree, int prec) throws IOException {
+        int prevPrec = this.prec;
+        try {
+            this.prec = prec;
+            if (tree == null) print("/*missing*/");
+            else {
+                tree.accept(this);
+            }
+        } catch (UncheckedIOException ex) {
+            IOException e = new IOException(ex.getMessage());
+            e.initCause(ex);
+            throw e;
+        } finally {
+            this.prec = prevPrec;
+        }
+    }
+
+    /** Derived visitor method: print expression tree at minimum precedence level
+     *  for expression.
+     */
+    public void printExpr(JCTree tree) throws IOException {
+        printExpr(tree, TreeInfo.noPrec);
+    }
+
+    /** Derived visitor method: print statement tree.
+     */
+    public void printStat(JCTree tree) throws IOException {
+        printExpr(tree, TreeInfo.notExpression);
+    }
+
+    /** Derived visitor method: print list of expression trees, separated by given string.
+     *  @param sep the separator string
+     */
+    public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
+        if (trees.nonEmpty()) {
+            printExpr(trees.head);
+            for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
+                print(sep);
+                printExpr(l.head);
+            }
+        }
+    }
+
+    /** Derived visitor method: print list of expression trees, separated by commas.
+     */
+    public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
+        printExprs(trees, ", ");
+    }
+
+    /** Derived visitor method: print list of statements, each on a separate line.
+     */
+    public void printStats(List<? extends JCTree> trees) throws IOException {
+        for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
+            align();
+            printStat(l.head);
+            println();
+        }
+    }
+
+    /** Print a set of modifiers.
+     */
+    public void printFlags(long flags) throws IOException {
+        if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
+        print(TreeInfo.flagNames(flags));
+        if ((flags & StandardFlags) != 0) print(" ");
+        if ((flags & ANNOTATION) != 0) print("@");
+    }
+
+    public void printAnnotations(List<JCAnnotation> trees) throws IOException {
+        for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
+            printStat(l.head);
+            println();
+            align();
+        }
+    }
+
+    /** Print documentation comment, if it exists
+     *  @param tree    The tree for which a documentation comment should be printed.
+     */
+    public void printDocComment(JCTree tree) throws IOException {
+        if (docComments != null) {
+            String dc = docComments.get(tree);
+            if (dc != null) {
+                print("/**"); println();
+                int pos = 0;
+                int endpos = lineEndPos(dc, pos);
+                while (pos < dc.length()) {
+                    align();
+                    print(" *");
+                    if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
+                    print(dc.substring(pos, endpos)); println();
+                    pos = endpos + 1;
+                    endpos = lineEndPos(dc, pos);
+                }
+                align(); print(" */"); println();
+                align();
+            }
+        }
+    }
+//where
+    static int lineEndPos(String s, int start) {
+        int pos = s.indexOf('\n', start);
+        if (pos < 0) pos = s.length();
+        return pos;
+    }
+
+    /** If type parameter list is non-empty, print it enclosed in "<...>" brackets.
+     */
+    public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
+        if (trees.nonEmpty()) {
+            print("<");
+            printExprs(trees);
+            print(">");
+        }
+    }
+
+    /** Print a block.
+     */
+    public void printBlock(List<? extends JCTree> stats) throws IOException {
+        print("{");
+        println();
+        indent();
+        printStats(stats);
+        undent();
+        align();
+        print("}");
+    }
+
+    /** Print a block.
+     */
+    public void printEnumBody(List<JCTree> stats) throws IOException {
+        print("{");
+        println();
+        indent();
+        boolean first = true;
+        for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
+            if (isEnumerator(l.head)) {
+                if (!first) {
+                    print(",");
+                    println();
+                }
+                align();
+                printStat(l.head);
+                first = false;
+            }
+        }
+        print(";");
+        println();
+        for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
+            if (!isEnumerator(l.head)) {
+                align();
+                printStat(l.head);
+                println();
+            }
+        }
+        undent();
+        align();
+        print("}");
+    }
+
+    /** Is the given tree an enumerator definition? */
+    boolean isEnumerator(JCTree t) {
+        return t.getTag() == JCTree.VARDEF && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
+    }
+
+    /** Print unit consisting of package clause and import statements in toplevel,
+     *  followed by class definition. if class definition == null,
+     *  print all definitions in toplevel.
+     *  @param tree     The toplevel tree
+     *  @param cdef     The class definition, which is assumed to be part of the
+     *                  toplevel tree.
+     */
+    public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
+        docComments = tree.docComments;
+        printDocComment(tree);
+        if (tree.pid != null) {
+            print("package ");
+            printExpr(tree.pid);
+            print(";");
+            println();
+        }
+        boolean firstImport = true;
+        for (List<JCTree> l = tree.defs;
+        l.nonEmpty() && (cdef == null || l.head.getTag() == JCTree.IMPORT);
+        l = l.tail) {
+            if (l.head.getTag() == JCTree.IMPORT) {
+                JCImport imp = (JCImport)l.head;
+                Name name = TreeInfo.name(imp.qualid);
+                if (name == name.table.asterisk ||
+                        cdef == null ||
+                        isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
+                    if (firstImport) {
+                        firstImport = false;
+                        println();
+                    }
+                    printStat(imp);
+                }
+            } else {
+                printStat(l.head);
+            }
+        }
+        if (cdef != null) {
+            printStat(cdef);
+            println();
+        }
+    }
+    // where
+    boolean isUsed(final Symbol t, JCTree cdef) {
+        class UsedVisitor extends TreeScanner {
+            public void scan(JCTree tree) {
+                if (tree!=null && !result) tree.accept(this);
+            }
+            boolean result = false;
+            public void visitIdent(JCIdent tree) {
+                if (tree.sym == t) result = true;
+            }
+        }
+        UsedVisitor v = new UsedVisitor();
+        v.scan(cdef);
+        return v.result;
+    }
+
+    /**************************************************************************
+     * Visitor methods
+     *************************************************************************/
+
+    public void visitTopLevel(JCCompilationUnit tree) {
+        try {
+            printUnit(tree, null);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitImport(JCImport tree) {
+        try {
+            print("import ");
+            if (tree.staticImport) print("static ");
+            printExpr(tree.qualid);
+            print(";");
+            println();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitClassDef(JCClassDecl tree) {
+        try {
+            println(); align();
+            printDocComment(tree);
+            printAnnotations(tree.mods.annotations);
+            printFlags(tree.mods.flags & ~INTERFACE);
+            Name enclClassNamePrev = enclClassName;
+            enclClassName = tree.name;
+            if ((tree.mods.flags & INTERFACE) != 0) {
+                print("interface " + tree.name);
+                printTypeParameters(tree.typarams);
+                if (tree.implementing.nonEmpty()) {
+                    print(" extends ");
+                    printExprs(tree.implementing);
+                }
+            } else {
+                if ((tree.mods.flags & ENUM) != 0)
+                    print("enum " + tree.name);
+                else
+                    print("class " + tree.name);
+                printTypeParameters(tree.typarams);
+                if (tree.extending != null) {
+                    print(" extends ");
+                    printExpr(tree.extending);
+                }
+                if (tree.implementing.nonEmpty()) {
+                    print(" implements ");
+                    printExprs(tree.implementing);
+                }
+            }
+            print(" ");
+            if ((tree.mods.flags & ENUM) != 0) {
+                printEnumBody(tree.defs);
+            } else {
+                printBlock(tree.defs);
+            }
+            enclClassName = enclClassNamePrev;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitMethodDef(JCMethodDecl tree) {
+        try {
+            // when producing source output, omit anonymous constructors
+            if (tree.name == tree.name.table.init &&
+                    enclClassName == null &&
+                    sourceOutput) return;
+            println(); align();
+            printDocComment(tree);
+            printExpr(tree.mods);
+            printTypeParameters(tree.typarams);
+            if (tree.name == tree.name.table.init) {
+                print(enclClassName != null ? enclClassName : tree.name);
+            } else {
+                printExpr(tree.restype);
+                print(" " + tree.name);
+            }
+            print("(");
+            printExprs(tree.params);
+            print(")");
+            if (tree.thrown.nonEmpty()) {
+                print(" throws ");
+                printExprs(tree.thrown);
+            }
+            if (tree.body != null) {
+                print(" ");
+                printStat(tree.body);
+            } else {
+                print(";");
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitVarDef(JCVariableDecl tree) {
+        try {
+            if (docComments != null && docComments.get(tree) != null) {
+                println(); align();
+            }
+            printDocComment(tree);
+            if ((tree.mods.flags & ENUM) != 0) {
+                print("/*public static final*/ ");
+                print(tree.name);
+                if (tree.init != null) {
+                    print(" /* = ");
+                    printExpr(tree.init);
+                    print(" */");
+                }
+            } else {
+                printExpr(tree.mods);
+                if ((tree.mods.flags & VARARGS) != 0) {
+                    printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
+                    print("... " + tree.name);
+                } else {
+                    printExpr(tree.vartype);
+                    print(" " + tree.name);
+                }
+                if (tree.init != null) {
+                    print(" = ");
+                    printExpr(tree.init);
+                }
+                if (prec == TreeInfo.notExpression) print(";");
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitSkip(JCSkip tree) {
+        try {
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitBlock(JCBlock tree) {
+        try {
+            printFlags(tree.flags);
+            printBlock(tree.stats);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitDoLoop(JCDoWhileLoop tree) {
+        try {
+            print("do ");
+            printStat(tree.body);
+            align();
+            print(" while ");
+            if (tree.cond.getTag() == JCTree.PARENS) {
+                printExpr(tree.cond);
+            } else {
+                print("(");
+                printExpr(tree.cond);
+                print(")");
+            }
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitWhileLoop(JCWhileLoop tree) {
+        try {
+            print("while ");
+            if (tree.cond.getTag() == JCTree.PARENS) {
+                printExpr(tree.cond);
+            } else {
+                print("(");
+                printExpr(tree.cond);
+                print(")");
+            }
+            print(" ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitForLoop(JCForLoop tree) {
+        try {
+            print("for (");
+            if (tree.init.nonEmpty()) {
+                if (tree.init.head.getTag() == JCTree.VARDEF) {
+                    printExpr(tree.init.head);
+                    for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
+                        JCVariableDecl vdef = (JCVariableDecl)l.head;
+                        print(", " + vdef.name + " = ");
+                        printExpr(vdef.init);
+                    }
+                } else {
+                    printExprs(tree.init);
+                }
+            }
+            print("; ");
+            if (tree.cond != null) printExpr(tree.cond);
+            print("; ");
+            printExprs(tree.step);
+            print(") ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitForeachLoop(JCEnhancedForLoop tree) {
+        try {
+            print("for (");
+            printExpr(tree.var);
+            print(" : ");
+            printExpr(tree.expr);
+            print(") ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitLabelled(JCLabeledStatement tree) {
+        try {
+            print(tree.label + ": ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitSwitch(JCSwitch tree) {
+        try {
+            print("switch ");
+            if (tree.selector.getTag() == JCTree.PARENS) {
+                printExpr(tree.selector);
+            } else {
+                print("(");
+                printExpr(tree.selector);
+                print(")");
+            }
+            print(" {");
+            println();
+            printStats(tree.cases);
+            align();
+            print("}");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitCase(JCCase tree) {
+        try {
+            if (tree.pat == null) {
+                print("default");
+            } else {
+                print("case ");
+                printExpr(tree.pat);
+            }
+            print(": ");
+            println();
+            indent();
+            printStats(tree.stats);
+            undent();
+            align();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitSynchronized(JCSynchronized tree) {
+        try {
+            print("synchronized ");
+            if (tree.lock.getTag() == JCTree.PARENS) {
+                printExpr(tree.lock);
+            } else {
+                print("(");
+                printExpr(tree.lock);
+                print(")");
+            }
+            print(" ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTry(JCTry tree) {
+        try {
+            print("try ");
+            printStat(tree.body);
+            for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
+                printStat(l.head);
+            }
+            if (tree.finalizer != null) {
+                print(" finally ");
+                printStat(tree.finalizer);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitCatch(JCCatch tree) {
+        try {
+            print(" catch (");
+            printExpr(tree.param);
+            print(") ");
+            printStat(tree.body);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitConditional(JCConditional tree) {
+        try {
+            open(prec, TreeInfo.condPrec);
+            printExpr(tree.cond, TreeInfo.condPrec);
+            print(" ? ");
+            printExpr(tree.truepart, TreeInfo.condPrec);
+            print(" : ");
+            printExpr(tree.falsepart, TreeInfo.condPrec);
+            close(prec, TreeInfo.condPrec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitIf(JCIf tree) {
+        try {
+            print("if ");
+            if (tree.cond.getTag() == JCTree.PARENS) {
+                printExpr(tree.cond);
+            } else {
+                print("(");
+                printExpr(tree.cond);
+                print(")");
+            }
+            print(" ");
+            printStat(tree.thenpart);
+            if (tree.elsepart != null) {
+                print(" else ");
+                printStat(tree.elsepart);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitExec(JCExpressionStatement tree) {
+        try {
+            printExpr(tree.expr);
+            if (prec == TreeInfo.notExpression) print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitBreak(JCBreak tree) {
+        try {
+            print("break");
+            if (tree.label != null) print(" " + tree.label);
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitContinue(JCContinue tree) {
+        try {
+            print("continue");
+            if (tree.label != null) print(" " + tree.label);
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitReturn(JCReturn tree) {
+        try {
+            print("return");
+            if (tree.expr != null) {
+                print(" ");
+                printExpr(tree.expr);
+            }
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitThrow(JCThrow tree) {
+        try {
+            print("throw ");
+            printExpr(tree.expr);
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitAssert(JCAssert tree) {
+        try {
+            print("assert ");
+            printExpr(tree.cond);
+            if (tree.detail != null) {
+                print(" : ");
+                printExpr(tree.detail);
+            }
+            print(";");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitApply(JCMethodInvocation tree) {
+        try {
+            if (!tree.typeargs.isEmpty()) {
+                if (tree.meth.getTag() == JCTree.SELECT) {
+                    JCFieldAccess left = (JCFieldAccess)tree.meth;
+                    printExpr(left.selected);
+                    print(".<");
+                    printExprs(tree.typeargs);
+                    print(">" + left.name);
+                } else {
+                    print("<");
+                    printExprs(tree.typeargs);
+                    print(">");
+                    printExpr(tree.meth);
+                }
+            } else {
+                printExpr(tree.meth);
+            }
+            print("(");
+            printExprs(tree.args);
+            print(")");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitNewClass(JCNewClass tree) {
+        try {
+            if (tree.encl != null) {
+                printExpr(tree.encl);
+                print(".");
+            }
+            print("new ");
+            if (!tree.typeargs.isEmpty()) {
+                print("<");
+                printExprs(tree.typeargs);
+                print(">");
+            }
+            printExpr(tree.clazz);
+            print("(");
+            printExprs(tree.args);
+            print(")");
+            if (tree.def != null) {
+                Name enclClassNamePrev = enclClassName;
+                enclClassName =
+                        tree.def.name != null ? tree.def.name :
+                            tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.empty ? tree.type.tsym.name :
+                                null;
+                if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
+                printBlock(tree.def.defs);
+                enclClassName = enclClassNamePrev;
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitNewArray(JCNewArray tree) {
+        try {
+            if (tree.elemtype != null) {
+                print("new ");
+                JCTree elem = tree.elemtype;
+                if (elem instanceof JCArrayTypeTree)
+                    printBaseElementType((JCArrayTypeTree) elem);
+                else
+                    printExpr(elem);
+                for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
+                    print("[");
+                    printExpr(l.head);
+                    print("]");
+                }
+                if (elem instanceof JCArrayTypeTree)
+                    printBrackets((JCArrayTypeTree) elem);
+            }
+            if (tree.elems != null) {
+                if (tree.elemtype != null) print("[]");
+                print("{");
+                printExprs(tree.elems);
+                print("}");
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitParens(JCParens tree) {
+        try {
+            print("(");
+            printExpr(tree.expr);
+            print(")");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitAssign(JCAssign tree) {
+        try {
+            open(prec, TreeInfo.assignPrec);
+            printExpr(tree.lhs, TreeInfo.assignPrec + 1);
+            print(" = ");
+            printExpr(tree.rhs, TreeInfo.assignPrec);
+            close(prec, TreeInfo.assignPrec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public String operatorName(int tag) {
+        switch(tag) {
+            case JCTree.POS:     return "+";
+            case JCTree.NEG:     return "-";
+            case JCTree.NOT:     return "!";
+            case JCTree.COMPL:   return "~";
+            case JCTree.PREINC:  return "++";
+            case JCTree.PREDEC:  return "--";
+            case JCTree.POSTINC: return "++";
+            case JCTree.POSTDEC: return "--";
+            case JCTree.NULLCHK: return "<*nullchk*>";
+            case JCTree.OR:      return "||";
+            case JCTree.AND:     return "&&";
+            case JCTree.EQ:      return "==";
+            case JCTree.NE:      return "!=";
+            case JCTree.LT:      return "<";
+            case JCTree.GT:      return ">";
+            case JCTree.LE:      return "<=";
+            case JCTree.GE:      return ">=";
+            case JCTree.BITOR:   return "|";
+            case JCTree.BITXOR:  return "^";
+            case JCTree.BITAND:  return "&";
+            case JCTree.SL:      return "<<";
+            case JCTree.SR:      return ">>";
+            case JCTree.USR:     return ">>>";
+            case JCTree.PLUS:    return "+";
+            case JCTree.MINUS:   return "-";
+            case JCTree.MUL:     return "*";
+            case JCTree.DIV:     return "/";
+            case JCTree.MOD:     return "%";
+            default: throw new Error();
+        }
+    }
+
+    public void visitAssignop(JCAssignOp tree) {
+        try {
+            open(prec, TreeInfo.assignopPrec);
+            printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
+            print(" " + operatorName(tree.getTag() - JCTree.ASGOffset) + "= ");
+            printExpr(tree.rhs, TreeInfo.assignopPrec);
+            close(prec, TreeInfo.assignopPrec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitUnary(JCUnary tree) {
+        try {
+            int ownprec = TreeInfo.opPrec(tree.getTag());
+            String opname = operatorName(tree.getTag());
+            open(prec, ownprec);
+            if (tree.getTag() <= JCTree.PREDEC) {
+                print(opname);
+                printExpr(tree.arg, ownprec);
+            } else {
+                printExpr(tree.arg, ownprec);
+                print(opname);
+            }
+            close(prec, ownprec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitBinary(JCBinary tree) {
+        try {
+            int ownprec = TreeInfo.opPrec(tree.getTag());
+            String opname = operatorName(tree.getTag());
+            open(prec, ownprec);
+            printExpr(tree.lhs, ownprec);
+            print(" " + opname + " ");
+            printExpr(tree.rhs, ownprec + 1);
+            close(prec, ownprec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTypeCast(JCTypeCast tree) {
+        try {
+            open(prec, TreeInfo.prefixPrec);
+            print("(");
+            printExpr(tree.clazz);
+            print(")");
+            printExpr(tree.expr, TreeInfo.prefixPrec);
+            close(prec, TreeInfo.prefixPrec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTypeTest(JCInstanceOf tree) {
+        try {
+            open(prec, TreeInfo.ordPrec);
+            printExpr(tree.expr, TreeInfo.ordPrec);
+            print(" instanceof ");
+            printExpr(tree.clazz, TreeInfo.ordPrec + 1);
+            close(prec, TreeInfo.ordPrec);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitIndexed(JCArrayAccess tree) {
+        try {
+            printExpr(tree.indexed, TreeInfo.postfixPrec);
+            print("[");
+            printExpr(tree.index);
+            print("]");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitSelect(JCFieldAccess tree) {
+        try {
+            printExpr(tree.selected, TreeInfo.postfixPrec);
+            print("." + tree.name);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitIdent(JCIdent tree) {
+        try {
+            print(tree.name);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitLiteral(JCLiteral tree) {
+        try {
+            switch (tree.typetag) {
+                case TypeTags.INT:
+                    print(tree.value.toString());
+                    break;
+                case TypeTags.LONG:
+                    print(tree.value + "L");
+                    break;
+                case TypeTags.FLOAT:
+                    print(tree.value + "F");
+                    break;
+                case TypeTags.DOUBLE:
+                    print(tree.value.toString());
+                    break;
+                case TypeTags.CHAR:
+                    print("\'" +
+                            Convert.quote(
+                            String.valueOf((char)((Number)tree.value).intValue())) +
+                            "\'");
+                    break;
+                case TypeTags.BOOLEAN:
+                    print(((Number)tree.value).intValue() == 1 ? "true" : "false");
+                    break;
+                case TypeTags.BOT:
+                    print("null");
+                    break;
+                default:
+                    print("\"" + Convert.quote(tree.value.toString()) + "\"");
+                    break;
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTypeIdent(JCPrimitiveTypeTree tree) {
+        try {
+            switch(tree.typetag) {
+                case TypeTags.BYTE:
+                    print("byte");
+                    break;
+                case TypeTags.CHAR:
+                    print("char");
+                    break;
+                case TypeTags.SHORT:
+                    print("short");
+                    break;
+                case TypeTags.INT:
+                    print("int");
+                    break;
+                case TypeTags.LONG:
+                    print("long");
+                    break;
+                case TypeTags.FLOAT:
+                    print("float");
+                    break;
+                case TypeTags.DOUBLE:
+                    print("double");
+                    break;
+                case TypeTags.BOOLEAN:
+                    print("boolean");
+                    break;
+                case TypeTags.VOID:
+                    print("void");
+                    break;
+                default:
+                    print("error");
+                    break;
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTypeArray(JCArrayTypeTree tree) {
+        try {
+            printBaseElementType(tree);
+            printBrackets(tree);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    // Prints the inner element type of a nested array
+    private void printBaseElementType(JCArrayTypeTree tree) throws IOException {
+        JCTree elem = tree.elemtype;
+        while (elem instanceof JCWildcard)
+            elem = ((JCWildcard) elem).inner;
+        if (elem instanceof JCArrayTypeTree)
+            printBaseElementType((JCArrayTypeTree) elem);
+        else
+            printExpr(elem);
+    }
+
+    // prints the brackets of a nested array in reverse order
+    private void printBrackets(JCArrayTypeTree tree) throws IOException {
+        JCTree elem;
+        while (true) {
+            elem = tree.elemtype;
+            print("[]");
+            if (!(elem instanceof JCArrayTypeTree)) break;
+            tree = (JCArrayTypeTree) elem;
+        }
+    }
+
+    public void visitTypeApply(JCTypeApply tree) {
+        try {
+            printExpr(tree.clazz);
+            print("<");
+            printExprs(tree.arguments);
+            print(">");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTypeParameter(JCTypeParameter tree) {
+        try {
+            print(tree.name);
+            if (tree.bounds.nonEmpty()) {
+                print(" extends ");
+                printExprs(tree.bounds, " & ");
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    @Override
+    public void visitWildcard(JCWildcard tree) {
+        try {
+            print(tree.kind);
+            if (tree.kind.kind != BoundKind.UNBOUND)
+                printExpr(tree.inner);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    @Override
+    public void visitTypeBoundKind(TypeBoundKind tree) {
+        try {
+            print(String.valueOf(tree.kind));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitErroneous(JCErroneous tree) {
+        try {
+            print("(ERROR)");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitLetExpr(LetExpr tree) {
+        try {
+            print("(let " + tree.defs + " in " + tree.expr + ")");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitModifiers(JCModifiers mods) {
+        try {
+            printAnnotations(mods.annotations);
+            printFlags(mods.flags);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitAnnotation(JCAnnotation tree) {
+        try {
+            print("@");
+            printExpr(tree.annotationType);
+            print("(");
+            printExprs(tree.args);
+            print(")");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void visitTree(JCTree tree) {
+        try {
+            print("(UNKNOWN: " + tree + ")");
+            println();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}