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
|
package lombok.eclipse;
import java.lang.reflect.Field;
import lombok.eclipse.EclipseAST.Node;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.parser.Parser;
/**
* Entry point for the Eclipse Parser patch that lets lombok modify the Abstract Syntax Tree as generated by
* eclipse's parser implementations. This class is injected into the appropriate OSGi ClassLoader and can thus
* use any classes that belong to org.eclipse.jdt.(apt.)core.
*
* Note that, for any Method body, if Bit24 is set, the eclipse parser has been patched to never attempt to
* (re)parse it. You should set Bit24 on any MethodDeclaration object you inject into the AST:
*
* <code>methodDeclaration.bits |= ASTNode.Bit24; //0x800000</code>
*
* @author rzwitserloot
* @author rspilker
*/
public class TransformEclipseAST {
private final EclipseAST ast;
//The patcher hacks this field onto CUD. It's public.
private static final Field astCacheField;
private static final HandlerLibrary handlers;
private static boolean disableLombok = false;
static {
Field f = null;
HandlerLibrary l = null;
try {
l = HandlerLibrary.load();
f = CompilationUnitDeclaration.class.getDeclaredField("$lombokAST");
} catch ( Throwable t ) {
Eclipse.error(null, "Problem initializing lombok", t);
disableLombok = true;
}
astCacheField = f;
handlers = l;
}
/**
* This method is called immediately after eclipse finishes building a CompilationUnitDeclaration, which is
* the top-level AST node when eclipse parses a source file. The signature is 'magic' - you should not
* change it!
*
* Eclipse's parsers often operate in diet mode, which means many parts of the AST have been left blank.
* Be ready to deal with just about anything being null, such as the Statement[] arrays of the Method AST nodes.
*
* @param parser The eclipse parser object that generated the AST.
* @param ast The AST node belonging to the compilation unit (java speak for a single source file).
*/
public static void transform(Parser parser, CompilationUnitDeclaration ast) {
if ( disableLombok ) return;
try {
EclipseAST existing = getCache(ast);
if ( existing == null ) {
existing = new EclipseAST(ast);
setCache(ast, existing);
} else existing.reparse();
new TransformEclipseAST(existing).go();
} catch ( Throwable t ) {
try {
String message = "Lombok can't parse this source: " + t.toString();
EclipseAST.addProblemToCompilationResult(ast, false, message, 0, 0);
t.printStackTrace();
} catch ( Throwable t2 ) {
Eclipse.error(ast, "Can't create an error in the problems dialog while adding: " + t.toString(), t2);
}
}
}
private static EclipseAST getCache(CompilationUnitDeclaration ast) {
if ( astCacheField == null ) return null;
try {
return (EclipseAST)astCacheField.get(ast);
} catch ( Exception e ) {
e.printStackTrace();
return null;
}
}
private static void setCache(CompilationUnitDeclaration ast, EclipseAST cache) {
if ( astCacheField != null ) try {
astCacheField.set(ast, cache);
} catch ( Exception ignore ) {
ignore.printStackTrace();
}
}
public TransformEclipseAST(EclipseAST ast) {
this.ast = ast;
}
public void go() {
handlers.skipPrintAST();
ast.traverse(new AnnotationVisitor());
handlers.callASTVisitors(ast);
handlers.skipAllButPrintAST();
ast.traverse(new AnnotationVisitor());
}
private static class AnnotationVisitor extends EclipseASTAdapter {
@Override public void visitAnnotationOnField(FieldDeclaration field, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
boolean handled = handlers.handle(top, annotationNode, annotation);
if ( handled ) annotationNode.setHandled();
}
@Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
boolean handled = handlers.handle(top, annotationNode, annotation);
if ( handled ) annotationNode.setHandled();
}
@Override public void visitAnnotationOnLocal(LocalDeclaration local, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
boolean handled = handlers.handle(top, annotationNode, annotation);
if ( handled ) annotationNode.setHandled();
}
@Override public void visitAnnotationOnMethod(AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
boolean handled = handlers.handle(top, annotationNode, annotation);
if ( handled ) annotationNode.setHandled();
}
@Override public void visitAnnotationOnType(TypeDeclaration type, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
boolean handled = handlers.handle(top, annotationNode, annotation);
if ( handled ) annotationNode.setHandled();
}
}
}
|