lombok.delombok.PrettyPrinter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lombok Show documentation
Show all versions of lombok Show documentation
Spice up your java: Automatic Resource Management, automatic generation of getters, setters, equals, hashCode and toString, and more!
/*
* Copyright (C) 2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.delombok;
import static com.sun.tools.javac.code.Flags.*;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacTreeMaker.TreeTag.treeTag;
import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.sun.tools.javac.tree.DocCommentTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssert;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCErroneous;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCSkip;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Position;
import lombok.javac.CommentInfo;
import lombok.javac.CommentInfo.EndConnection;
import lombok.javac.CommentInfo.StartConnection;
import lombok.javac.JavacTreeMaker.TreeTag;
import lombok.javac.JavacTreeMaker.TypeTag;
public class PrettyPrinter extends JCTree.Visitor {
private static final String LINE_SEP = System.getProperty("line.separator");
private static final Map OPERATORS;
static {
Map map = new HashMap();
map.put(treeTag("POS"), "+");
map.put(treeTag("NEG"), "-");
map.put(treeTag("NOT"), "!");
map.put(treeTag("COMPL"), "~");
map.put(treeTag("PREINC"), "++");
map.put(treeTag("PREDEC"), "--");
map.put(treeTag("POSTINC"), "++");
map.put(treeTag("POSTDEC"), "--");
map.put(treeTag("NULLCHK"), "<*nullchk*>");
map.put(treeTag("OR"), "||");
map.put(treeTag("AND"), "&&");
map.put(treeTag("EQ"), "==");
map.put(treeTag("NE"), "!=");
map.put(treeTag("LT"), "<");
map.put(treeTag("GT"), ">");
map.put(treeTag("LE"), "<=");
map.put(treeTag("GE"), ">=");
map.put(treeTag("BITOR"), "|");
map.put(treeTag("BITXOR"), "^");
map.put(treeTag("BITAND"), "&");
map.put(treeTag("SL"), "<<");
map.put(treeTag("SR"), ">>");
map.put(treeTag("USR"), ">>>");
map.put(treeTag("PLUS"), "+");
map.put(treeTag("MINUS"), "-");
map.put(treeTag("MUL"), "*");
map.put(treeTag("DIV"), "/");
map.put(treeTag("MOD"), "%");
map.put(treeTag("BITOR_ASG"), "|=");
map.put(treeTag("BITXOR_ASG"), "^=");
map.put(treeTag("BITAND_ASG"), "&=");
map.put(treeTag("SL_ASG"), "<<=");
map.put(treeTag("SR_ASG"), ">>=");
map.put(treeTag("USR_ASG"), ">>>=");
map.put(treeTag("PLUS_ASG"), "+=");
map.put(treeTag("MINUS_ASG"), "-=");
map.put(treeTag("MUL_ASG"), "*=");
map.put(treeTag("DIV_ASG"), "/=");
map.put(treeTag("MOD_ASG"), "%=");
OPERATORS = map;
}
private final Writer out;
private final JCCompilationUnit compilationUnit;
private List comments;
private final FormatPreferences formatPreferences;
private final Map docComments;
private final DocCommentTable docTable;
private int indent = 0;
@SuppressWarnings({"unchecked", "rawtypes"})
public PrettyPrinter(Writer out, JCCompilationUnit cu, List comments, FormatPreferences preferences) {
this.out = out;
this.comments = comments;
this.compilationUnit = cu;
this.formatPreferences = preferences;
/* load doc comments */ {
Object dc = getDocComments(compilationUnit);
if (dc instanceof Map, ?>) {
this.docComments = (Map) dc;
this.docTable = null;
} else if (dc instanceof DocCommentTable) {
this.docComments = null;
this.docTable = (DocCommentTable) dc;
} else {
this.docComments = null;
this.docTable = null;
}
}
}
private int endPos(JCTree tree) {
return getEndPosition(tree, compilationUnit);
}
private static int lineEndPos(String s, int start) {
int pos = s.indexOf('\n', start);
if (pos < 0) pos = s.length();
return pos;
}
private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned;
public static final class UncheckedIOException extends RuntimeException {
UncheckedIOException(IOException source) {
super(toMsg(source));
setStackTrace(source.getStackTrace());
}
private static String toMsg(Throwable t) {
String msg = t.getMessage();
String n = t.getClass().getSimpleName();
if (msg == null || msg.isEmpty()) return n;
return n + ": " + msg;
}
}
private void align() {
if (!onNewLine) return;
try {
for (int i = 0; i < indent; i++) out.write(formatPreferences.indent());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
onNewLine = false;
aligned = true;
needsAlign = false;
}
private void print(JCTree tree) {
if (tree == null) {
print("/*missing*/");
return;
}
consumeComments(tree);
tree.accept(this);
consumeTrailingComments(endPos(tree));
}
private void print(List extends JCTree> trees, String infix) {
boolean first = true;
JCTree prev = null;
for (JCTree tree : trees) {
if (suppress(tree)) continue;
if (!first && infix != null && !infix.isEmpty()) {
if ("\n".equals(infix)) println(prev);
else print(infix);
}
first = false;
print(tree);
prev = tree;
}
}
private boolean suppress(JCTree tree) {
if (tree instanceof JCBlock) {
JCBlock block = (JCBlock) tree;
return (Position.NOPOS == block.pos) && block.stats.isEmpty();
}
if (tree instanceof JCExpressionStatement) {
JCExpression expr = ((JCExpressionStatement)tree).expr;
if (expr instanceof JCMethodInvocation) {
JCMethodInvocation inv = (JCMethodInvocation) expr;
if (!inv.typeargs.isEmpty() || !inv.args.isEmpty()) return false;
if (!(inv.meth instanceof JCIdent)) return false;
return ((JCIdent) inv.meth).name.toString().equals("super");
}
}
return false;
}
private void print(CharSequence s) {
boolean align = needsAlign;
if (needsNewLine && !onNewLine) println();
if (align && !aligned) align();
try {
if (needsSpace && !onNewLine && !aligned) out.write(' ');
out.write(s.toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
needsSpace = false;
onNewLine = false;
aligned = false;
}
private void println() {
try {
out.write(LINE_SEP);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
onNewLine = true;
aligned = false;
needsNewLine = false;
}
private void println(JCTree completed) {
if (completed != null) {
int endPos = endPos(completed);
consumeTrailingComments(endPos);
}
try {
out.write(LINE_SEP);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
onNewLine = true;
aligned = false;
needsNewLine = false;
}
private void println(CharSequence s) {
print(s);
println();
}
private void println(CharSequence s, JCTree completed) {
print(s);
println(completed);
}
private void aPrint(CharSequence s) {
align();
print(s);
}
private void aPrintln(CharSequence s) {
align();
print(s);
println();
}
private void aPrintln(CharSequence s, JCTree completed) {
align();
print(s);
println(completed);
}
private void consumeComments(int until) {
CommentInfo head = comments.head;
while (comments.nonEmpty() && head.pos < until) {
printComment(head);
comments = comments.tail;
head = comments.head;
}
}
private void consumeComments(JCTree tree) {
consumeComments(tree.pos);
}
private void consumeTrailingComments(int from) {
boolean prevNewLine = onNewLine;
CommentInfo head = comments.head;
boolean stop = false;
while (comments.nonEmpty() && head.prevEndPos == from && !stop && !(head.start == StartConnection.ON_NEXT_LINE || head.start == StartConnection.START_OF_LINE)) {
from = head.endPos;
printComment(head);
stop = (head.end == EndConnection.ON_NEXT_LINE);
comments = comments.tail;
head = comments.head;
}
if (!onNewLine && prevNewLine) {
println();
}
}
private String getJavadocFor(JCTree node) {
if (docComments != null) return docComments.get(node);
if (docTable != null) return docTable.getCommentText(node);
return null;
}
private int dims(JCExpression vartype) {
if (vartype instanceof JCArrayTypeTree) {
return 1 + dims(((JCArrayTypeTree) vartype).elemtype);
}
return 0;
}
private void printComment(CommentInfo comment) {
switch (comment.start) {
case DIRECT_AFTER_PREVIOUS:
needsSpace = false;
break;
case AFTER_PREVIOUS:
needsSpace = true;
break;
case START_OF_LINE:
needsNewLine = true;
needsAlign = false;
break;
case ON_NEXT_LINE:
if (!onNewLine) {
needsNewLine = true;
needsAlign = true;
} else if (!aligned) {
needsAlign = true;
}
break;
}
if (onNewLine && !aligned && comment.start != StartConnection.START_OF_LINE) needsAlign = true;
print(comment.content);
switch (comment.end) {
case ON_NEXT_LINE:
if (!aligned) {
needsNewLine = true;
needsAlign = true;
}
break;
case AFTER_COMMENT:
needsSpace = true;
break;
case DIRECT_AFTER_COMMENT:
// do nothing
break;
}
}
private void printDocComment(JCTree tree) {
String dc = getJavadocFor(tree);
if (dc == null) return;
aPrintln("/**");
int pos = 0;
int endpos = lineEndPos(dc, pos);
boolean atStart = true;
while (pos < dc.length()) {
String line = dc.substring(pos, endpos);
if (line.trim().isEmpty() && atStart) {
atStart = false;
continue;
}
atStart = false;
aPrint(" *");
if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
println(dc.substring(pos, endpos));
pos = endpos + 1;
endpos = lineEndPos(dc, pos);
}
aPrintln(" */");
}
private Name __INIT__, __VALUE__;
private Name name_init(Name someName) {
if (__INIT__ == null) __INIT__ = someName.table.fromChars("".toCharArray(), 0, 6);
return __INIT__;
}
private Name name_value(Name someName) {
if (__VALUE__ == null) __VALUE__ = someName.table.fromChars("value".toCharArray(), 0, 5);
return __VALUE__;
}
@Override public void visitTopLevel(JCCompilationUnit tree) {
printDocComment(tree);
if (tree.pid != null) {
consumeComments(tree);
aPrint("package ");
print(tree.pid);
println(";", tree.pid);
}
boolean first = true;
for (JCTree child : tree.defs) {
if (!(child instanceof JCImport)) continue;
if (first) println();
first = false;
print(child);
}
for (JCTree child : tree.defs) {
if (child instanceof JCImport) continue;
print(child);
}
consumeComments(Integer.MAX_VALUE);
}
@Override public void visitImport(JCImport tree) {
aPrint("import ");
if (tree.staticImport) print("static ");
print(tree.qualid);
println(";", tree);
}
private Name currentTypeName;
@Override public void visitClassDef(JCClassDecl tree) {
println();
printDocComment(tree);
align();
print(tree.mods);
boolean isInterface = (tree.mods.flags & INTERFACE) != 0;
boolean isAnnotationInterface = isInterface && (tree.mods.flags & ANNOTATION) != 0;
boolean isEnum = (tree.mods.flags & ENUM) != 0;
if (isAnnotationInterface) print("@interface ");
else if (isInterface) print("interface ");
else if (isEnum) print("enum ");
else print("class ");
print(tree.name);
Name prevTypeName = currentTypeName;
currentTypeName = tree.name;
if (tree.typarams.nonEmpty()) {
print("<");
print(tree.typarams, ", ");
print(">");
}
JCTree extendsClause = getExtendsClause(tree);
if (extendsClause != null) {
print(" extends ");
print(extendsClause);
}
if (tree.implementing.nonEmpty()) {
print(isInterface ? " extends " : " implements ");
print(tree.implementing, ", ");
}
println(" {");
indent++;
printClassMembers(tree.defs, isEnum, isInterface);
consumeComments(endPos(tree));
indent--;
aPrintln("}", tree);
currentTypeName = prevTypeName;
}
private void printClassMembers(List members, boolean isEnum, boolean isInterface) {
Class> prefType = null;
int typeOfPrevEnumMember = isEnum ? 3 : 0; // 1 = normal, 2 = with body, 3 = no enum field yet.
boolean prevWasEnumMember = isEnum;
for (JCTree member : members) {
if (typeOfPrevEnumMember == 3 && member instanceof JCMethodDecl && (((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
boolean isEnumVar = isEnum && member instanceof JCVariableDecl && (((JCVariableDecl) member).mods.flags & ENUM) != 0;
if (!isEnumVar && prevWasEnumMember) {
prevWasEnumMember = false;
if (typeOfPrevEnumMember == 3) align();
println(";");
}
if (isEnumVar) {
if (prefType != null && prefType != JCVariableDecl.class) println();
switch (typeOfPrevEnumMember) {
case 1:
print(", ");
break;
case 2:
println(",");
align();
break;
}
print(member);
JCTree init = ((JCVariableDecl) member).init;
typeOfPrevEnumMember = init instanceof JCNewClass && ((JCNewClass) init).def != null ? 2 : 1;
} else if (member instanceof JCVariableDecl) {
if (prefType != null && prefType != JCVariableDecl.class) println();
if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL);
print(member);
} else if (member instanceof JCMethodDecl) {
if ((((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
if (prefType != null) println();
if (isInterface) flagMod = -1L & ~(PUBLIC | ABSTRACT);
print(member);
} else if (member instanceof JCClassDecl) {
if (prefType != null) println();
if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC);
print(member);
} else {
if (prefType != null) println();
print(member);
}
prefType = member.getClass();
}
if (prevWasEnumMember) {
prevWasEnumMember = false;
if (typeOfPrevEnumMember == 3) align();
println(";");
}
}
@Override public void visitTypeParameter(JCTypeParameter tree) {
List annotations = readObject(tree, "annotations", List.nil());
if (!annotations.isEmpty()) {
print(annotations, " ");
print(" ");
}
print(tree.name);
if (tree.bounds.nonEmpty()) {
print(" extends ");
print(tree.bounds, " & ");
}
consumeComments(tree);
}
@Override public void visitVarDef(JCVariableDecl tree) {
printDocComment(tree);
align();
if ((tree.mods.flags & ENUM) != 0) {
printEnumMember(tree);
return;
}
printAnnotations(tree.mods.annotations, true);
printModifierKeywords(tree.mods);
printVarDef0(tree);
println(";", tree);
}
private void printVarDefInline(JCVariableDecl tree) {
printAnnotations(tree.mods.annotations, false);
printModifierKeywords(tree.mods);
printVarDef0(tree);
}
private void printVarDef0(JCVariableDecl tree) {
boolean varargs = (tree.mods.flags & VARARGS) != 0;
if (varargs && tree.vartype instanceof JCArrayTypeTree) {
print(((JCArrayTypeTree) tree.vartype).elemtype);
print("...");
} else {
print(tree.vartype);
}
print(" ");
print(tree.name);
if (tree.init != null) {
print(" = ");
print(tree.init);
}
}
private void printEnumMember(JCVariableDecl tree) {
printAnnotations(tree.mods.annotations, true);
print(tree.name);
if (tree.init instanceof JCNewClass) {
JCNewClass constructor = (JCNewClass) tree.init;
if (constructor.args != null && constructor.args.nonEmpty()) {
print("(");
print(constructor.args, ", ");
print(")");
}
if (constructor.def != null && constructor.def.defs != null) {
println(" {");
indent++;
printClassMembers(constructor.def.defs, false, false);
consumeComments(endPos(tree));
indent--;
aPrint("}");
}
}
}
// TODO: Test postfix syntax for methods (?), for decls. Multiline vardefs, possibly with comments. enums with bodies. constructor-local generics, method-local generics, also do/while, finally, try-with-resources, lambdas, annotations in java8 places...
// TODO: Whatever is JCAnnotatedType? We handle it in the 7+ bucket in the old one...
@Override public void visitTypeApply(JCTypeApply tree) {
print(tree.clazz);
print("<");
print(tree.arguments, ", ");
print(">");
}
@Override public void visitWildcard(JCWildcard tree) {
switch (tree.getKind()) {
default:
case UNBOUNDED_WILDCARD:
print("?");
return;
case EXTENDS_WILDCARD:
print("? extends ");
print(tree.inner);
return;
case SUPER_WILDCARD:
print("? super ");
print(tree.inner);
return;
}
}
@Override public void visitLiteral(JCLiteral tree) {
TypeTag typeTag = typeTag(tree);
if (CTC_INT.equals(typeTag)) print("" + tree.value);
else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
else if (CTC_DOUBLE.equals(typeTag)) print("" + tree.value);
else if (CTC_CHAR.equals(typeTag)) {
print("\'" + quoteChar((char)((Number)tree.value).intValue()) + "\'");
}
else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
else if (CTC_BOT.equals(typeTag)) print("null");
else print("\"" + quoteChars(tree.value.toString()) + "\"");
}
@Override public void visitMethodDef(JCMethodDecl tree) {
boolean isConstructor = tree.name == name_init(tree.name);
if (isConstructor && (tree.mods.flags & GENERATEDCONSTR) != 0) return;
printDocComment(tree);
align();
print(tree.mods);
if (tree.typarams != null && tree.typarams.nonEmpty()) {
print("<");
print(tree.typarams, ", ");
print("> ");
}
if (isConstructor) {
print(currentTypeName == null ? "" : currentTypeName);
} else {
print(tree.restype);
print(" ");
print(tree.name);
}
print("(");
boolean first = true;
for (JCVariableDecl param : tree.params) {
if (!first) print(", ");
first = false;
printVarDefInline(param);
}
print(")");
if (tree.thrown.nonEmpty()) {
print(" throws ");
print(tree.thrown, ", ");
}
if (tree.defaultValue != null) {
print(" default ");
print(tree.defaultValue);
}
if (tree.body != null) {
print(" ");
print(tree.body);
} else println(";", tree);
}
@Override public void visitSkip(JCSkip that) {
if (onNewLine && !aligned) {
align();
}
println(";");
}
@Override public void visitAnnotation(JCAnnotation tree) {
print("@");
print(tree.annotationType);
if (tree.args.isEmpty()) return;
print("(");
boolean done = false;
if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
JCAssign arg1 = (JCAssign) tree.args.get(0);
JCIdent arg1Name = arg1.lhs instanceof JCIdent ? ((JCIdent) arg1.lhs) : null;
if (arg1Name != null && arg1Name.name == name_value(arg1Name.name)) {
print(arg1.rhs);
done = true;
}
}
if (!done) print(tree.args, ", ");
print(")");
}
@Override public void visitTypeArray(JCArrayTypeTree tree) {
JCTree elem = tree.elemtype;
while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner;
print(elem);
print("[]");
}
@Override public void visitNewArray(JCNewArray tree) {
JCTree elem = tree.elemtype;
int dims = 0;
if (elem != null) {
print("new ");
while (elem instanceof JCArrayTypeTree) {
dims++;
elem = ((JCArrayTypeTree) elem).elemtype;
}
print(elem);
for (JCExpression expr : tree.dims) {
print("[");
print(expr);
print("]");
}
}
for (int i = 0; i < dims; i++) print("[]");
if (tree.elems != null) {
if (elem != null) print("[] ");
print("{");
print(tree.elems, ", ");
print("}");
}
}
@Override public void visitNewClass(JCNewClass tree) {
if (tree.encl != null) {
print(tree.encl);
print(".");
}
print("new ");
if (!tree.typeargs.isEmpty()) {
print("<");
print(tree.typeargs, ", ");
print(">");
}
print(tree.clazz);
print("(");
print(tree.args, ", ");
print(")");
if (tree.def != null) {
Name previousTypeName = currentTypeName;
currentTypeName = null;
println(" {");
indent++;
print(tree.def.defs, "");
indent--;
aPrint("}");
currentTypeName = previousTypeName;
}
}
@Override public void visitIndexed(JCArrayAccess tree) {
print(tree.indexed);
print("[");
print(tree.index);
print("]");
}
@Override public void visitTypeIdent(JCPrimitiveTypeTree tree) {
TypeTag typeTag = typeTag(tree);
if (CTC_BYTE.equals(typeTag)) print("byte");
else if (CTC_CHAR.equals(typeTag)) print("char");
else if (CTC_SHORT.equals(typeTag)) print("short");
else if (CTC_INT.equals(typeTag)) print("int");
else if (CTC_LONG.equals(typeTag)) print("long");
else if (CTC_FLOAT.equals(typeTag)) print("float");
else if (CTC_DOUBLE.equals(typeTag)) print("double");
else if (CTC_BOOLEAN.equals(typeTag)) print("boolean");
else if (CTC_VOID.equals(typeTag)) print("void");
else print("error");
}
@Override public void visitLabelled(JCLabeledStatement tree) {
aPrint(tree.label);
print(":");
if (tree.body instanceof JCSkip || suppress(tree)) {
println(" ;", tree);
} else if (tree.body instanceof JCBlock) {
print(" ");
print(tree.body);
} else {
println(tree);
print(tree.body);
}
}
private long flagMod = -1L;
private static final long DEFAULT = 1L<<43;
@Override public void visitModifiers(JCModifiers tree) {
printAnnotations(tree.annotations, true);
printModifierKeywords(tree);
}
private void printAnnotations(List annotations, boolean newlines) {
for (JCAnnotation ann : annotations) {
print(ann);
if (newlines) {
println();
align();
} else print(" ");
}
}
private void printModifierKeywords(JCModifiers tree) {
long v = flagMod & tree.flags;
flagMod = -1L;
if ((v & SYNTHETIC) != 0) print("/* synthetic */ ");
if ((v & PUBLIC) != 0) print("public ");
if ((v & PRIVATE) != 0) print("private ");
if ((v & PROTECTED) != 0) print("protected ");
if ((v & STATIC) != 0) print("static ");
if ((v & FINAL) != 0) print("final ");
if ((v & SYNCHRONIZED) != 0) print("synchronized ");
if ((v & VOLATILE) != 0) print("volatile ");
if ((v & TRANSIENT) != 0) print("transient ");
if ((v & NATIVE) != 0) print("native ");
if ((v & ABSTRACT) != 0) print("abstract ");
if ((v & STRICTFP) != 0) print("strictfp ");
if ((v & DEFAULT) != 0 && (v & INTERFACE) == 0) print("default ");
}
@Override public void visitSelect(JCFieldAccess tree) {
print(tree.selected);
print(".");
print(tree.name);
}
@Override public void visitIdent(JCIdent tree) {
print(tree.name);
}
@Override public void visitApply(JCMethodInvocation tree) {
if (tree.typeargs.nonEmpty()) {
if (tree.meth instanceof JCFieldAccess) {
JCFieldAccess fa = (JCFieldAccess) tree.meth;
print(fa.selected);
print(".<");
print(tree.typeargs, ", ");
print(">");
print(fa.name);
} else {
print("<");
print(tree.typeargs, ", ");
print(">");
print(tree.meth);
}
} else {
print(tree.meth);
}
print("(");
print(tree.args, ", ");
print(")");
}
@Override public void visitAssert(JCAssert tree) {
aPrint("assert ");
print(tree.cond);
if (tree.detail != null) {
print(" : ");
print(tree.detail);
}
println(";", tree);
}
@Override public void visitAssign(JCAssign tree) {
print(tree.lhs);
print(" = ");
print(tree.rhs);
}
@Override public void visitAssignop(JCAssignOp tree) {
print(tree.lhs);
String opname = operator(treeTag(tree));
print(" " + opname + " ");
print(tree.rhs);
}
private static final int PREFIX = 14;
@Override public void visitUnary(JCUnary tree) {
String op = operator(treeTag(tree));
if (treeTag(tree).getOperatorPrecedenceLevel() == PREFIX) {
print(op);
print(tree.arg);
} else {
print(tree.arg);
print(op);
}
}
@Override public void visitBinary(JCBinary tree) {
String op = operator(treeTag(tree));
print(tree.lhs);
print(" ");
print(op);
print(" ");
print(tree.rhs);
}
@Override public void visitTypeTest(JCInstanceOf tree) {
print(tree.expr);
print(" instanceof ");
print(tree.clazz);
}
@Override public void visitTypeCast(JCTypeCast tree) {
print("(");
print(tree.clazz);
print(") ");
print(tree.expr);
}
@Override public void visitBlock(JCBlock tree) {
if (tree.pos == Position.NOPOS && tree.stats.isEmpty()) return;
if (onNewLine) align();
if ((tree.flags & STATIC) != 0) print("static ");
println("{");
indent++;
print(tree.stats, "");
consumeComments(endPos(tree));
indent--;
aPrintln("}", tree);
}
@Override public void visitBreak(JCBreak tree) {
aPrint("break");
if (tree.label != null) {
print(" ");
print(tree.label);
}
println(";", tree);
}
@Override public void visitContinue(JCContinue tree) {
aPrint("continue");
if (tree.label != null) {
print(" ");
print(tree.label);
}
println(";", tree);
}
@Override public void visitConditional(JCConditional tree) {
print(tree.cond);
print(" ? ");
print(tree.truepart);
print(" : ");
print(tree.falsepart);
}
@Override public void visitParens(JCParens tree) {
print("(");
print(tree.expr);
print(")");
}
@Override public void visitReturn(JCReturn tree) {
aPrint("return");
if (tree.expr != null) {
print(" ");
print(tree.expr);
}
println(";", tree);
}
@Override public void visitThrow(JCThrow tree) {
aPrint("throw ");
print(tree.expr);
println(";", tree);
}
@Override public void visitWhileLoop(JCWhileLoop tree) {
aPrint("while ");
if (tree.cond instanceof JCParens) {
print(tree.cond);
} else {
print("(");
print(tree.cond);
print(")");
}
print(" ");
print(tree.body);
// make sure to test while (true) ; and while(true){} and while(true) x = 5;
}
@Override public void visitForLoop(JCForLoop tree) {
aPrint("for (");
if (tree.init.nonEmpty()) {
// ForInit is either a StatementExpressionList or a LocalVariableDeclaration
if (tree.init.head instanceof JCVariableDecl) {
boolean first = true;
int dims = 0;
for (JCStatement i : tree.init) {
JCVariableDecl vd = (JCVariableDecl) i;
if (first) {
printVarDefInline(vd);
dims = dims(vd.vartype);
} else {
print(", ");
print(vd.name);
int dimDiff = dims(vd.vartype) - dims;
for (int j = 0; j < dimDiff; j++) print("[]");
if (vd.init != null) {
print(" = ");
print(vd.init);
}
}
first = false;
}
} else {
boolean first = true;
for (JCStatement exprStatement : tree.init) {
if (!first) print(", ");
first = false;
print(((JCExpressionStatement) exprStatement).expr);
}
}
}
print("; ");
if (tree.cond != null) print(tree.cond);
print("; ");
boolean first = true;
for (JCExpressionStatement exprStatement : tree.step) {
if (!first) print(", ");
first = false;
print(exprStatement.expr);
}
print(") ");
print(tree.body);
}
@Override public void visitForeachLoop(JCEnhancedForLoop tree) {
aPrint("for (");
printVarDefInline(tree.var);
print(" : ");
print(tree.expr);
print(") ");
print(tree.body);
}
@Override public void visitIf(JCIf tree) {
aPrint("if ");
if (tree.cond instanceof JCParens) {
print(tree.cond);
} else {
print("(");
print(tree.cond);
print(")");
}
print(" ");
if (tree.thenpart instanceof JCBlock) {
println("{");
indent++;
print(((JCBlock) tree.thenpart).stats, "");
indent--;
if (tree.elsepart == null) {
aPrintln("}", tree);
} else {
aPrint("}");
}
} else {
print(tree.thenpart);
}
if (tree.elsepart != null) {
aPrint(" else ");
print(tree.elsepart);
}
}
@Override public void visitExec(JCExpressionStatement tree) {
align();
print(tree.expr);
println(";", tree);
}
@Override public void visitDoLoop(JCDoWhileLoop tree) {
aPrint("do ");
if (tree.body instanceof JCBlock) {
println("{");
indent++;
print(((JCBlock) tree.body).stats, "");
indent--;
aPrint("}");
} else print(tree.body);
print(" while ");
if (tree.cond instanceof JCParens) {
print(tree.cond);
} else {
print("(");
print(tree.cond);
print(")");
}
println(";", tree);
}
@Override public void visitSynchronized(JCSynchronized tree) {
aPrint("synchronized ");
if (tree.lock instanceof JCParens) {
print(tree.lock);
} else {
print("(");
print(tree.lock);
print(")");
}
print(" ");
print(tree.body);
}
@Override public void visitCase(JCCase tree) {
if (tree.pat == null) {
aPrint("default");
} else {
aPrint("case ");
print(tree.pat);
}
println(": ");
indent++;
print(tree.stats, "");
indent--;
}
@Override public void visitCatch(JCCatch tree) {
print(" catch (");
print(tree.param);
print(") ");
print(tree.body);
}
@Override public void visitSwitch(JCSwitch tree) {
aPrint("switch ");
if (tree.selector instanceof JCParens) {
print(tree.selector);
} else {
print("(");
print(tree.selector);
print(")");
}
println(" {");
print(tree.cases, "\n");
aPrintln("}", tree);
}
@Override public void visitTry(JCTry tree) {
aPrint("try ");
List> resources = readObject(tree, "resources", List.nil());
int len = resources.length();
switch (len) {
case 0:
break;
case 1:
print("(");
JCVariableDecl decl = (JCVariableDecl) resources.get(0);
flagMod = -1L & ~FINAL;
printVarDefInline(decl);
print(") ");
break;
default:
println("(");
indent++;
int c = 0;
for (Object i : resources) {
align();
flagMod = -1L & ~FINAL;
printVarDefInline((JCVariableDecl) i);
if (++c == len) {
print(") ");
} else {
println(";", (JCTree) i);
}
}
indent--;
}
println("{");
indent++;
for (JCStatement stat : tree.body.stats) print(stat);
indent--;
aPrint("}");
for (JCCatch catchBlock : tree.catchers) {
printCatch(catchBlock);
}
if (tree.finalizer != null) {
println(" finally {");
indent++;
for (JCStatement stat : tree.finalizer.stats) print(stat);
indent--;
aPrint("}");
}
println(tree);
}
private void printCatch(JCCatch catchBlock) {
print(" catch (");
printVarDefInline(catchBlock.param); // ExprType1 | ExprType2 handled via JCTypeUnion.
println(") {");
indent++;
for (JCStatement stat : catchBlock.body.stats) print(stat);
indent--;
aPrint("}");
}
public void visitErroneous(JCErroneous tree) {
print("(ERROR)");
}
private static String operator(TreeTag tag) {
String op = OPERATORS.get(tag);
if (op == null) return "(?op?)";
return op;
}
private static String quoteChars(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) sb.append(quoteChar(s.charAt(i)));
return sb.toString();
}
private static String quoteChar(char ch) {
switch (ch) {
case '\b': return "\\b";
case '\f': return "\\f";
case '\n': return "\\n";
case '\r': return "\\r";
case '\t': return "\\t";
case '\'': return "\\'";
case '\"': return "\\\"";
case '\\': return "\\\\";
default:
if (ch < 32) return String.format("\\%03o", (int) ch);
return String.valueOf(ch);
}
}
private static final Method getExtendsClause, getEndPosition, storeEnd;
static {
getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class>[0]);
getExtendsClause.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class);
} else {
getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable");
Method storeEndMethodTemp;
Class> endPosTable;
try {
endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable");
} catch (ClassNotFoundException ex) {
throw sneakyThrow(ex);
}
try {
storeEndMethodTemp = endPosTable.getMethod("storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException e) {
try {
endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable");
storeEndMethodTemp = endPosTable.getDeclaredMethod("storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException ex) {
throw sneakyThrow(ex);
} catch (ClassNotFoundException ex) {
throw sneakyThrow(ex);
}
}
storeEnd = storeEndMethodTemp;
}
getEndPosition.setAccessible(true);
storeEnd.setAccessible(true);
}
private static Method getMethod(Class> clazz, String name, Class>... paramTypes) {
try {
return clazz.getMethod(name, paramTypes);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
}
}
private static Method getMethod(Class> clazz, String name, String... paramTypes) {
try {
Class>[] c = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
return clazz.getMethod(name, c);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
} catch (ClassNotFoundException e) {
throw sneakyThrow(e);
}
}
public static JCTree getExtendsClause(JCClassDecl decl) {
try {
return (JCTree) getExtendsClause.invoke(decl);
} catch (IllegalAccessException e) {
throw sneakyThrow(e);
} catch (InvocationTargetException e) {
throw sneakyThrow(e.getCause());
}
}
static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
PrettyPrinter.sneakyThrow0(t);
return null;
}
@SuppressWarnings("unchecked")
private static void sneakyThrow0(Throwable t) throws T {
throw (T)t;
}
private static final Map, Map> reflectionCache = new HashMap, Map>();
@SuppressWarnings("unchecked")
private T readObject(JCTree tree, String fieldName, T defaultValue) {
Class> tClass = tree.getClass();
Map c = reflectionCache.get(tClass);
if (c == null) reflectionCache.put(tClass, c = new HashMap());
Field f = c.get(fieldName);
if (f == null) {
try {
f = tClass.getDeclaredField(fieldName);
} catch (Exception e) {
return defaultValue;
}
f.setAccessible(true);
c.put(fieldName, f);
}
try {
return (T) f.get(tree);
} catch (Exception e) {
return defaultValue;
}
}
public void visitTypeBoundKind(TypeBoundKind tree) {
print(String.valueOf(tree.kind));
}
@Override public void visitTree(JCTree tree) {
String simpleName = tree.getClass().getSimpleName();
if ("JCTypeUnion".equals(simpleName)) {
List types = readObject(tree, "alternatives", List.nil());
print(types, " | ");
return;
} else if ("JCTypeIntersection".equals(simpleName)) {
print(readObject(tree, "bounds", List.nil()), " & ");
return;
} else if ("JCMemberReference".equals(simpleName)) {
printMemberReference0(tree);
return;
} else if ("JCLambda".equals(simpleName)) {
printLambda0(tree);
return;
} else if ("JCAnnotatedType".equals(simpleName)) {
printAnnotatedType0(tree);
return;
}
throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree);
}
private void printMemberReference0(JCTree tree) {
print(readObject(tree, "expr", (JCExpression) null));
print("::");
List typeArgs = readObject(tree, "typeargs", List.nil());
if (typeArgs != null && !typeArgs.isEmpty()) {
print("<");
print(typeArgs, ", ");
print(">");
}
print(readObject(tree, "mode", new Object()).toString().equals("INVOKE") ? readObject(tree, "name", (Name) null) : "new");
}
private void printLambda0(JCTree tree) {
List params = readObject(tree, "params", List.nil());
boolean explicit = true;
int paramLength = params.size();
try {
explicit = readObject(tree, "paramKind", new Object()).toString().equals("EXPLICIT");
} catch (Exception e) {}
boolean useParens = paramLength != 1 || explicit;
if (useParens) print("(");
if (explicit) {
boolean first = true;
for (JCVariableDecl vd : params) {
if (!first) print(", ");
first = false;
printVarDefInline(vd);
}
} else {
String sep = "";
for (JCVariableDecl param : params) {
print(sep);
print(param.name);
sep = ", ";
}
}
if (useParens) print(")");
print(" -> ");
JCTree body = readObject(tree, "body", (JCTree) null);
if (body instanceof JCBlock) {
println("{");
indent++;
print(((JCBlock) body).stats, "");
indent--;
aPrint("}");
} else {
print(body);
}
}
private void printAnnotatedType0(JCTree tree) {
JCTree underlyingType = readObject(tree, "underlyingType", (JCTree) null);
if (underlyingType instanceof JCFieldAccess) {
print(((JCFieldAccess) underlyingType).selected);
print(".");
print(readObject(tree, "annotations", List.nil()), " ");
print(" ");
print(((JCFieldAccess) underlyingType).name);
} else {
print(readObject(tree, "annotations", List.nil()), " ");
print(" ");
print(underlyingType);
}
}
}