-
Notifications
You must be signed in to change notification settings - Fork 1
/
JavaSourcePrettyPrinter.java
185 lines (178 loc) · 7.18 KB
/
JavaSourcePrettyPrinter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.*;
import java.io.IOException;
import java.io.Writer;
public class JavaSourcePrettyPrinter extends com.sun.tools.javac.tree.Pretty {
private boolean inEnum = false;
public JavaSourcePrettyPrinter(Writer out, boolean sourceOutput) {
super(out, sourceOutput);
}
@Override
public void visitClassDef(final JCClassDecl classDeclaration) {
final boolean oldInEnum = this.inEnum;
this.inEnum = (classDeclaration.mods.flags & Flags.ENUM) > 0;
boolean isRecord = (classDeclaration.mods.flags & Flags.RECORD) != 0;
if (isRecord) { // FIXME: hacky workaround to get (static) records pretty printed as records instead of classes
boolean first;
try {
println();
super.visitModifiers(classDeclaration.mods);
print("record " + classDeclaration.name);
if (classDeclaration.typarams.nonEmpty()) {
print("<");
print(classDeclaration.typarams);
print(">");
}
StringBuilder recordConstructorParameterTypes = new StringBuilder();
first = true;
print("(");
for (JCTree member : classDeclaration.defs) {
if (member instanceof JCVariableDecl variableDeclaration && (variableDeclaration.mods.flags & Flags.GENERATED_MEMBER) != 0) {
if (!first) {
print(", ");
recordConstructorParameterTypes.append(",");
}
first = false;
super.printAnnotations(variableDeclaration.mods.annotations);
print(variableDeclaration.vartype + " " + variableDeclaration.name);
recordConstructorParameterTypes.append(variableDeclaration.vartype);
}
}
print(")");
if (classDeclaration.implementing != null && classDeclaration.implementing.nonEmpty()) { // records can implement, but never extend
print(" implements " + classDeclaration.implementing);
}
print(" {");
println();
for (JCTree member : classDeclaration.defs) {
if (member instanceof JCVariableDecl variableDeclaration && (variableDeclaration.mods.flags & Flags.GENERATED_MEMBER) == 0) {
super.visitVarDef(variableDeclaration);
} else if (member instanceof JCMethodDecl methodDeclaration && (methodDeclaration.mods.flags & Flags.GENERATED_MEMBER) == 0) {
if (methodDeclaration.getReturnType() != null) { // regular method
super.visitMethodDef(methodDeclaration);
} else if (!methodDeclaration.sym.toString().equals(classDeclaration.name + "(" + recordConstructorParameterTypes + ")")) { // not the main record constructor
// FIXME: if the main record constructor is explicitly implemented, it will be removed here (the PrettyPrinter doesn't see it at all?)!
String s = methodDeclaration.toString().replace("<init>", classDeclaration.name);
print(s);
}
} else { // FIXME: missing anything else here?
print("// missing something? skipped: " + member.getTag() + " / " + member.getKind());
}
println();
}
print("}");
} catch (IOException e) {
throw new Error("something failed while pretty printing: " + e);
}
} else {
super.visitClassDef(classDeclaration);
}
this.inEnum = oldInEnum;
}
@Override
public void visitApply(final JCMethodInvocation methodInvocation) {
if (inEnum && methodInvocation.meth instanceof JCIdent ident) {
if (ident.name == ident.name.table.names._super) {
return;
}
}
super.visitApply(methodInvocation);
}
@Override
public void visitVarDef(JCVariableDecl variableDeclaration) {
// FIXME: hacky workaround to get rid of "/*missing*/" printed instead of "var"
if (variableDeclaration.declaredUsingVar()) {
try {
super.printDocComment(variableDeclaration);
super.printAnnotations(variableDeclaration.mods.annotations);
super.visitModifiers(variableDeclaration.mods);
print("var ");
print(variableDeclaration.name);
if (variableDeclaration.init != null) {
print(" = ");
print(variableDeclaration.init);
print(";");
}
} catch (IOException e) {
throw new Error("something failed while pretty printing: " + e);
}
} else {
super.visitVarDef(variableDeclaration);
}
}
@Override
public void visitForLoop(JCForLoop forLoop) {
// FIXME: hacky workaround to get rid of "/*missing*/" printed instead of "var"
try {
print("for (");
boolean first = true;
for (JCStatement statement : forLoop.init) {
if (!first) print(", ");
if (statement instanceof JCVariableDecl variableDeclaration) {
if (first) {
if (variableDeclaration.declaredUsingVar()) {
print("var");
} else {
print(variableDeclaration.getType());
}
print(" ");
}
print(variableDeclaration.getName());
if (variableDeclaration.init != null) {
print(" = ");
print(variableDeclaration.init);
}
} else {
print(((JCExpressionStatement) statement).expr);
}
first = false;
}
print("; ");
if (forLoop.cond != null) print(forLoop.cond);
print("; ");
first = true;
for (JCExpressionStatement statement : forLoop.step) {
if (!first) print(", ");
first = false;
print(statement.expr);
}
print(") ");
if (forLoop.body instanceof JCAssert body) super.visitAssert(body);
else if (forLoop.body instanceof JCBlock body) super.visitBlock(body);
else if (forLoop.body instanceof JCBreak body) super.visitBreak(body);
else if (forLoop.body instanceof JCCase body) super.visitCase(body);
else if (forLoop.body instanceof JCClassDecl body) super.visitClassDef(body);
else if (forLoop.body instanceof JCContinue body) super.visitContinue(body);
else if (forLoop.body instanceof JCDoWhileLoop body) super.visitDoLoop(body);
else if (forLoop.body instanceof JCEnhancedForLoop body) super.visitForeachLoop(body);
else if (forLoop.body instanceof JCExpressionStatement body) super.visitExec(body);
else if (forLoop.body instanceof JCForLoop body) super.visitForLoop(body);
else if (forLoop.body instanceof JCIf body) super.visitIf(body);
else if (forLoop.body instanceof JCLabeledStatement body) super.visitLabelled(body);
else if (forLoop.body instanceof JCReturn body) super.visitReturn(body);
else if (forLoop.body instanceof JCSkip body) super.visitSkip(body);
else if (forLoop.body instanceof JCSwitch body) super.visitSwitch(body);
else if (forLoop.body instanceof JCSynchronized body) super.visitSynchronized(body);
else if (forLoop.body instanceof JCThrow body) super.visitThrow(body);
else if (forLoop.body instanceof JCTry body) super.visitTry(body);
else if (forLoop.body instanceof JCVariableDecl body) super.visitVarDef(body);
else if (forLoop.body instanceof JCWhileLoop body) super.visitWhileLoop(body);
else if (forLoop.body instanceof JCYield body) super.visitYield(body);
else print(forLoop.body); // FIXME: missing anything else here? try best effort if so...
} catch (IOException e) {
throw new Error("something failed while pretty printing: " + e);
}
}
@Override
public void visitYield(JCYield tree) {
// FIXME: hacky workaround to get rid of "yield" printed without block parenthesis "{yield ...;}"
try {
print("{");
super.visitYield(tree);
print("}");
} catch (IOException e) {
throw new Error("something failed while pretty printing: " + e);
}
}
}