diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-07-27 23:25:37 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-11-16 22:18:29 +0100 |
commit | 9cfac4131ff33b247652ed3622491cbf8bf76721 (patch) | |
tree | c1f31d3577a308ea7b76bc847c73b2c40fc11333 /src | |
parent | ff4f995507d23c1c9172de2f5fffaf0bfb6cf1ec (diff) | |
download | lombok-9cfac4131ff33b247652ed3622491cbf8bf76721.tar.gz lombok-9cfac4131ff33b247652ed3622491cbf8bf76721.tar.bz2 lombok-9cfac4131ff33b247652ed3622491cbf8bf76721.zip |
The bulk of the effort for a new pretty printer.
Diffstat (limited to 'src')
-rw-r--r-- | src/delombok/lombok/delombok/DelombokResult.java | 3 | ||||
-rw-r--r-- | src/delombok/lombok/delombok/PrettyPrinter.java | 1458 |
2 files changed, 1460 insertions, 1 deletions
diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java index 84aeb68b..8985b257 100644 --- a/src/delombok/lombok/delombok/DelombokResult.java +++ b/src/delombok/lombok/delombok/DelombokResult.java @@ -65,7 +65,8 @@ public class DelombokResult { else comments_ = com.sun.tools.javac.util.List.from(comments.toArray(new CommentInfo[0])); FormatPreferences preferences = new FormatPreferenceScanner().scan(formatPreferences, getContent()); - compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences)); + //compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences)); + compilationUnit.accept(new PrettyPrinter(out, compilationUnit, comments_, preferences)); } private CharSequence getContent() throws IOException { diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java new file mode 100644 index 00000000..0b248c52 --- /dev/null +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -0,0 +1,1458 @@ +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.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<TreeTag, String> OPERATORS; + + static { + Map<TreeTag, String> map = new HashMap<TreeTag, String>(); + + 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<CommentInfo> comments; + private final FormatPreferences formatPreferences; + + private final Map<JCTree, String> docComments; + private final DocCommentTable docTable; + private int indent = 0; + + @SuppressWarnings({"unchecked", "rawtypes"}) + public PrettyPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> 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 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; + } + + 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("<init>".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<JCTree> 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<JCExpression> annotations = readObject(tree, "annotations", List.<JCExpression>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 ? "<init>" : 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) { + print(";"); + } + + @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<JCAnnotation> 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()) { + if (tree.init.head instanceof JCVariableDecl) { + boolean first = true; + for (JCStatement i : tree.init) { + JCVariableDecl vd = (JCVariableDecl) i; + if (first) { + printVarDefInline(vd); + } else { + print(", "); + print(vd.name); + if (vd.init != null) { + print(" = "); + print(vd.init); + } + } + first = false; + } + } else { + print(tree.init, ", "); + } + } + 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 "); + 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, "\n"); + 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); + } + } + } + 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.<RuntimeException>sneakyThrow0(t); + return null; + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + throw (T)t; + } + + private static final Map<Class<?>, Map<String, Field>> reflectionCache = new HashMap<Class<?>, Map<String, Field>>(); + + @SuppressWarnings("unchecked") + private <T> T readObject(JCTree tree, String fieldName, T defaultValue) { + Class<?> tClass = tree.getClass(); + Map<String, Field> c = reflectionCache.get(tClass); + if (c == null) reflectionCache.put(tClass, c = new HashMap<String, Field>()); + 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; + } + } + + @Override public void visitTree(JCTree tree) { + String simpleName = tree.getClass().getSimpleName(); + if ("JCTypeUnion".equals(simpleName)) { + List<JCExpression> types = readObject(tree, "alternatives", List.<JCExpression>nil()); + print(types, " | "); + return; + } else if ("JCTypeIntersection".equals(simpleName)) { + print(readObject(tree, "bounds", List.<JCExpression>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<JCExpression> typeArgs = readObject(tree, "typeargs", List.<JCExpression>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<JCVariableDecl> params = readObject(tree, "params", List.<JCVariableDecl>nil()); + boolean explicit = true; + int paramLength = params.size(); + if (paramLength != 1) print("("); + try { + explicit = readObject(tree, "paramKind", new Object()).toString().equals("EXPLICIT"); + } catch (Exception e) {} + 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 (paramLength != 1) 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.<JCExpression>nil()), " "); + print(" "); + print(((JCFieldAccess) underlyingType).name); + } else { + print(readObject(tree, "annotations", List.<JCExpression>nil()), " "); + print(" "); + print(underlyingType); + } + } +} |