blob: e4a98d366c2330ba3d0f9f0873007cd2ad8823c7 [file] [log] [blame]
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.tree;
import java.io.*;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
/** Prints out a tree as an indented Java source program.
*
* <p><b>This is NOT part of any supported API.
* 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
* source.
*/
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 table mapping trees to their documentation comments
* (can be null)
*/
DocCommentTable docComments = null;
/**
* A string sequence to be used when Pretty output should be constrained
* to fit into a given size
*/
private final static String trimSequence = "[...]";
/**
* Max number of chars to be generated when output should fit into a single line
*/
private final static int PREFERRED_LENGTH = 20;
/** 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);
}
public static String toSimpleString(JCTree tree) {
return toSimpleString(tree, PREFERRED_LENGTH);
}
public static String toSimpleString(JCTree tree, int maxLength) {
StringWriter s = new StringWriter();
try {
new Pretty(s, false).printExpr(tree);
}
catch (IOException e) {
// should never happen, because StringWriter is defined
// never to throw any IOExceptions
throw new AssertionError(e);
}
//we need to (i) replace all line terminators with a space and (ii) remove
//occurrences of 'missing' in the Pretty output (generated when types are missing)
String res = s.toString().trim().replaceAll("\\s+", " ").replaceAll("/\\*missing\\*/", "");
if (res.length() < maxLength) {
return res;
} else {
int head = (maxLength - trimSequence.length()) * 2 / 3;
int tail = maxLength - trimSequence.length() - head;
return res.substring(0, head) + trimSequence + res.substring(res.length() - tail);
}
}
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 & ExtendedStandardFlags) != 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();
}
}
public void printTypeAnnotations(List<JCAnnotation> trees) throws IOException {
for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
printExpr(l.head);
print(" ");
}
}
/** 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.getCommentText(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
* {@literal "<...>"} 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.hasTag(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);
boolean firstImport = true;
for (List<JCTree> l = tree.defs;
l.nonEmpty() &&
(cdef == null ||
l.head.hasTag(IMPORT) || l.head.hasTag(PACKAGEDEF));
l = l.tail) {
if (l.head.hasTag(IMPORT)) {
JCImport imp = (JCImport)l.head;
Name name = TreeInfo.name(imp.qualid);
if (name == name.table.names.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 visitPackageDef(JCPackageDecl tree) {
try {
printDocComment(tree);
printAnnotations(tree.annotations);
if (tree.pid != null) {
print("package ");
printExpr(tree.pid);
print(";");
println();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitModuleDef(JCModuleDecl tree) {
try {
print("module ");
printExpr(tree.qualId);
if (tree.directives == null) {
print(";");
} else {
printBlock(tree.directives);
}
println();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitExports(JCExports tree) {
try {
print("exports ");
printExpr(tree.qualid);
if (tree.moduleNames != null) {
print(" to ");
printExprs(tree.moduleNames);
}
print(";");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitProvides(JCProvides tree) {
try {
print("provides ");
printExpr(tree.serviceName);
print(" with ");
printExpr(tree.implName);
print(";");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitRequires(JCRequires tree) {
try {
print("requires ");
if (tree.isPublic)
print("public ");
printExpr(tree.moduleName);
print(";");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitUses(JCUses tree) {
try {
print("uses ");
printExpr(tree.qualid);
print(";");
} 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.names.init &&
enclClassName == null &&
sourceOutput) return;
println(); align();
printDocComment(tree);
printExpr(tree.mods);
printTypeParameters(tree.typarams);
if (tree.name == tree.name.table.names.init) {
print(enclClassName != null ? enclClassName : tree.name);
} else {
printExpr(tree.restype);
print(" " + tree.name);
}
print("(");
if (tree.recvparam!=null) {
printExpr(tree.recvparam);
if (tree.params.size() > 0) {
print(", ");
}
}
printExprs(tree.params);
print(")");
if (tree.thrown.nonEmpty()) {
print(" throws ");
printExprs(tree.thrown);
}
if (tree.defaultValue != null) {
print(" default ");
printExpr(tree.defaultValue);
}
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.hasComment(tree)) {
println(); align();
}
printDocComment(tree);
if ((tree.mods.flags & ENUM) != 0) {
print("/*public static final*/ ");
print(tree.name);
if (tree.init != null) {
if (sourceOutput && tree.init.hasTag(NEWCLASS)) {
print(" /*enum*/ ");
JCNewClass init = (JCNewClass) tree.init;
if (init.args != null && init.args.nonEmpty()) {
print("(");
print(init.args);
print(")");
}
if (init.def != null && init.def.defs != null) {
print(" ");
printBlock(init.def.defs);
}
return;
}
print(" /* = ");
printExpr(tree.init);
print(" */");
}
} else {
printExpr(tree.mods);
if ((tree.mods.flags & VARARGS) != 0) {
JCTree vartype = tree.vartype;
List<JCAnnotation> tas = null;
if (vartype instanceof JCAnnotatedType) {
tas = ((JCAnnotatedType)vartype).annotations;
vartype = ((JCAnnotatedType)vartype).underlyingType;
}
printExpr(((JCArrayTypeTree) vartype).elemtype);
if (tas != null) {
print(' ');
printTypeAnnotations(tas);
}
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.hasTag(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.hasTag(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.hasTag(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);
if (vdef.init != null) {
print(" = ");
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.hasTag(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.hasTag(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 ");
if (tree.resources.nonEmpty()) {
print("(");
boolean first = true;
for (JCTree var : tree.resources) {
if (!first) {
println();
indent();
}
printStat(var);
first = false;
}
print(") ");
}
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 + 1);
print(" ? ");
printExpr(tree.truepart);
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.hasTag(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.hasTag(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(">");
}
if (tree.def != null && tree.def.mods.annotations.nonEmpty()) {
printTypeAnnotations(tree.def.mods.annotations);
}
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.names.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;
printBaseElementType(elem);
if (!tree.annotations.isEmpty()) {
print(' ');
printTypeAnnotations(tree.annotations);
}
if (tree.elems != null) {
print("[]");
}
int i = 0;
List<List<JCAnnotation>> da = tree.dimAnnotations;
for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
if (da.size() > i && !da.get(i).isEmpty()) {
print(' ');
printTypeAnnotations(da.get(i));
}
print("[");
i++;
printExpr(l.head);
print("]");
}
printBrackets(elem);
}
if (tree.elems != null) {
print("{");
printExprs(tree.elems);
print("}");
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitLambda(JCLambda tree) {
try {
print("(");
if (tree.paramKind == JCLambda.ParameterKind.EXPLICIT) {
printExprs(tree.params);
} else {
String sep = "";
for (JCVariableDecl param : tree.params) {
print(sep);
print(param.name);
sep = ",";
}
}
print(")->");
printExpr(tree.body);
} 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(JCTree.Tag tag) {
switch(tag) {
case POS: return "+";
case NEG: return "-";
case NOT: return "!";
case COMPL: return "~";
case PREINC: return "++";
case PREDEC: return "--";
case POSTINC: return "++";
case POSTDEC: return "--";
case NULLCHK: return "<*nullchk*>";
case OR: return "||";
case AND: return "&&";
case EQ: return "==";
case NE: return "!=";
case LT: return "<";
case GT: return ">";
case LE: return "<=";
case GE: return ">=";
case BITOR: return "|";
case BITXOR: return "^";
case BITAND: return "&";
case SL: return "<<";
case SR: return ">>";
case USR: return ">>>";
case PLUS: return "+";
case MINUS: return "-";
case MUL: return "*";
case DIV: return "/";
case 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().noAssignOp()) + "= ");
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().isPostUnaryOp()) {
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 visitReference(JCMemberReference tree) {
try {
printExpr(tree.expr);
print("::");
if (tree.typeargs != null) {
print("<");
printExprs(tree.typeargs);
print(">");
}
print(tree.getMode() == ReferenceMode.INVOKE ? tree.name : "new");
} 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 INT:
print(tree.value.toString());
break;
case LONG:
print(tree.value + "L");
break;
case FLOAT:
print(tree.value + "F");
break;
case DOUBLE:
print(tree.value.toString());
break;
case CHAR:
print("\'" +
Convert.quote(
String.valueOf((char)((Number)tree.value).intValue())) +
"\'");
break;
case BOOLEAN:
print(((Number)tree.value).intValue() == 1 ? "true" : "false");
break;
case 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 BYTE:
print("byte");
break;
case CHAR:
print("char");
break;
case SHORT:
print("short");
break;
case INT:
print("int");
break;
case LONG:
print("long");
break;
case FLOAT:
print("float");
break;
case DOUBLE:
print("double");
break;
case BOOLEAN:
print("boolean");
break;
case 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(JCTree tree) throws IOException {
printExpr(TreeInfo.innermostType(tree));
}
// prints the brackets of a nested array in reverse order
// tree is either JCArrayTypeTree or JCAnnotatedTypeTree
private void printBrackets(JCTree tree) throws IOException {
JCTree elem = tree;
while (true) {
if (elem.hasTag(ANNOTATED_TYPE)) {
JCAnnotatedType atype = (JCAnnotatedType) elem;
elem = atype.underlyingType;
if (elem.hasTag(TYPEARRAY)) {
print(' ');
printTypeAnnotations(atype.annotations);
}
}
if (elem.hasTag(TYPEARRAY)) {
print("[]");
elem = ((JCArrayTypeTree)elem).elemtype;
} else {
break;
}
}
}
public void visitTypeApply(JCTypeApply tree) {
try {
printExpr(tree.clazz);
print("<");
printExprs(tree.arguments);
print(">");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitTypeUnion(JCTypeUnion tree) {
try {
printExprs(tree.alternatives, " | ");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitTypeIntersection(JCTypeIntersection tree) {
try {
printExprs(tree.bounds, " & ");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitTypeParameter(JCTypeParameter tree) {
try {
if (tree.annotations.nonEmpty()) {
this.printTypeAnnotations(tree.annotations);
}
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 visitAnnotatedType(JCAnnotatedType tree) {
try {
if (tree.underlyingType.hasTag(SELECT)) {
JCFieldAccess access = (JCFieldAccess) tree.underlyingType;
printExpr(access.selected, TreeInfo.postfixPrec);
print(".");
printTypeAnnotations(tree.annotations);
print(access.name);
} else if (tree.underlyingType.hasTag(TYPEARRAY)) {
printBaseElementType(tree);
printBrackets(tree);
} else {
printTypeAnnotations(tree.annotations);
printExpr(tree.underlyingType);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitTree(JCTree tree) {
try {
print("(UNKNOWN: " + tree + ")");
println();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}