aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@tipit.to>2009-06-12 03:40:07 +0200
committerReinier Zwitserloot <reinier@tipit.to>2009-06-12 03:40:07 +0200
commite84bdd32e71b029d8d1f2fa7849e998953984f47 (patch)
treed3e7ff61b9d1b84de36b5d41496f5436d853d114 /src
parent4c18c774fc60eca7e104b3eaa5535ce94ec66198 (diff)
downloadlombok-e84bdd32e71b029d8d1f2fa7849e998953984f47.tar.gz
lombok-e84bdd32e71b029d8d1f2fa7849e998953984f47.tar.bz2
lombok-e84bdd32e71b029d8d1f2fa7849e998953984f47.zip
Singularly massive code change, too hard to document. Basically, hooking now occurs in the two most sane places:
- After the parser is done building a first rendition of the AST. (Usually lightweight and missing method bodies etc) - After the parser is done taking such a lightweight AST and filling in the gaps. Lombok then builts its own bidirectional and somewhat saner AST out of this, and hands this saner AST off for treatment. Things in the AST can be marked as 'handled'. This seems to work swimmingly and should allow us to easily identify the annotations that are for us, and work our magic, no matter where they appear or on what, including stuff inside method bodies.
Diffstat (limited to 'src')
-rw-r--r--src/lombok/eclipse/EclipseAST.java337
-rw-r--r--src/lombok/eclipse/EclipseASTAdapter.java25
-rw-r--r--src/lombok/eclipse/EclipseASTVisitor.java159
-rw-r--r--src/lombok/eclipse/HandleGetter_ecj.java26
-rw-r--r--src/lombok/eclipse/TransformEclipseAST.java46
5 files changed, 563 insertions, 30 deletions
diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java
new file mode 100644
index 00000000..63815a99
--- /dev/null
+++ b/src/lombok/eclipse/EclipseAST.java
@@ -0,0 +1,337 @@
+package lombok.eclipse;
+
+import static lombok.Lombok.sneakyThrow;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+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.Initializer;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+
+public class EclipseAST {
+ public void traverse(EclipseASTVisitor visitor) {
+ Node current = top();
+ visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.node);
+ traverseChildren(visitor, current);
+ visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.node);
+ }
+
+ private void traverseChildren(EclipseASTVisitor visitor, Node node) {
+ for ( Node child : node.children ) {
+ ASTNode n = child.node;
+ if ( n instanceof TypeDeclaration ) {
+ visitor.visitType(child, (TypeDeclaration)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitType(child, (TypeDeclaration)n);
+ } else if ( n instanceof Initializer ) {
+ visitor.visitInitializer(child, (Initializer)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitInitializer(child, (Initializer)n);
+ } else if ( n instanceof FieldDeclaration ) {
+ visitor.visitField(child, (FieldDeclaration)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitField(child, (FieldDeclaration)n);
+ } else if ( n instanceof AbstractMethodDeclaration ) {
+ visitor.visitMethod(child, (AbstractMethodDeclaration)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitMethod(child, (AbstractMethodDeclaration)n);
+ } else if ( n instanceof LocalDeclaration ) {
+ visitor.visitLocal(child, (LocalDeclaration)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitLocal(child, (LocalDeclaration)n);
+ } else throw new AssertionError("Can't be reached");
+ }
+ }
+
+ public boolean isCompleteParse() {
+ return completeParse;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public Node top() {
+ return top;
+ }
+
+ public Node get(ASTNode node) {
+ return nodeMap.get(node);
+ }
+
+ public final class Node {
+ final ASTNode node;
+ Node parent;
+ final Collection<Node> children;
+ boolean handled;
+
+ Node(ASTNode node, Collection<Node> children) {
+ this.node = node;
+ this.children = children == null ? Collections.<Node>emptyList() : children;
+ }
+
+ public ASTNode getEclipseNode() {
+ return node;
+ }
+
+ public Node up() {
+ return parent;
+ }
+
+ public Collection<Node> down() {
+ return children;
+ }
+
+ public boolean isHandled() {
+ return handled;
+ }
+
+ public Node setHandled() {
+ this.handled = true;
+ return this;
+ }
+
+ public Node top() {
+ return top;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public boolean isCompleteParse() {
+ return completeParse;
+ }
+ }
+
+ private final Map<ASTNode, Void> identityDetector = new IdentityHashMap<ASTNode, Void>();
+ private Map<ASTNode, Node> nodeMap = new HashMap<ASTNode, Node>();
+ private final CompilationUnitDeclaration compilationUnitDeclaration;
+ private final String fileName;
+ private Node top;
+ private boolean completeParse;
+
+ public EclipseAST(CompilationUnitDeclaration ast) {
+ this.compilationUnitDeclaration = ast;
+ this.fileName = ast.compilationResult.fileName == null ? "(unknown).java" : new String(ast.compilationResult.fileName);
+ this.top = buildTree(ast);
+ this.completeParse = isComplete(ast);
+ }
+
+ public void reparse() {
+ if ( completeParse ) return;
+ boolean newCompleteParse = isComplete(compilationUnitDeclaration);
+ if ( !newCompleteParse ) return;
+ Map<ASTNode, Node> oldMap = nodeMap;
+ nodeMap = new HashMap<ASTNode, Node>();
+ this.top = buildTree(compilationUnitDeclaration);
+
+ //Retain 'handled' flags.
+ for ( Map.Entry<ASTNode, Node> e : nodeMap.entrySet() ) {
+ Node oldEntry = oldMap.get(e.getKey());
+ if ( oldEntry != null && oldEntry.handled ) e.getValue().handled = true;
+ }
+
+ this.completeParse = true;
+ }
+
+ private static boolean isComplete(CompilationUnitDeclaration unit) {
+ return (unit.bits & ASTNode.HasAllMethodBodies) > 0;
+ }
+
+ private Node putInMap(Node parent) {
+ for ( Node child : parent.children ) child.parent = parent;
+ nodeMap.put(parent.node, parent);
+ identityDetector.put(parent.node, null);
+ return parent;
+ }
+
+ private Node buildTree(CompilationUnitDeclaration top) {
+ identityDetector.clear();
+ Collection<Node> children = buildTree(top.types);
+ return putInMap(new Node(top, children));
+ }
+
+ private void addIfNotNull(Collection<Node> collection, Node n) {
+ if ( n != null ) collection.add(n);
+ }
+
+ private Collection<Node> buildTree(TypeDeclaration[] children) {
+ if ( children == null ) return Collections.emptyList();
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildTree(type));
+ return childNodes;
+ }
+
+ private Node buildTree(TypeDeclaration type) {
+ if ( identityDetector.containsKey(type) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ childNodes.addAll(buildTree(type.fields));
+ childNodes.addAll(buildTree(type.memberTypes));
+ childNodes.addAll(buildTree(type.methods));
+ return putInMap(new Node(type, childNodes));
+ }
+
+ private Collection<Node> buildTree(FieldDeclaration[] children) {
+ if ( children == null ) return Collections.emptyList();
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( FieldDeclaration child : children ) addIfNotNull(childNodes, buildTree(child));
+ return childNodes;
+ }
+
+ private Node buildTree(FieldDeclaration field) {
+ if ( field instanceof Initializer ) return buildTree((Initializer)field);
+ if ( identityDetector.containsKey(field) ) return null;
+ return putInMap(new Node(field, buildWithStatement(field.initialization)));
+ }
+
+ private Node buildTree(Initializer initializer) {
+ if ( identityDetector.containsKey(initializer) ) return null;
+ return putInMap(new Node(initializer, buildWithStatement(initializer.block)));
+ }
+
+ private Collection<Node> buildTree(AbstractMethodDeclaration[] children) {
+ if ( children == null ) return Collections.emptyList();
+ List<Node> childNodes = new ArrayList<Node>();
+ for (AbstractMethodDeclaration method : children ) addIfNotNull(childNodes, buildTree(method));
+ return childNodes;
+ }
+
+ private Node buildTree(AbstractMethodDeclaration method) {
+ if ( identityDetector.containsKey(method) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ childNodes.addAll(buildTree(method.arguments));
+ childNodes.addAll(buildTree(method.statements));
+ return putInMap(new Node(method, childNodes));
+ }
+
+ //Arguments are a kind of LocalDeclaration. They can definitely contain lombok annotations, so we care about them.
+ private Collection<Node> buildTree(Argument[] children) {
+ if ( children == null ) return Collections.emptyList();
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( LocalDeclaration local : children ) {
+ if ( !identityDetector.containsKey(local) ) {
+ addIfNotNull(childNodes, buildTree(local));
+ childNodes.addAll(buildWithStatement(local.initialization));
+ }
+ }
+ return childNodes;
+ }
+
+ private Node buildTree(LocalDeclaration local) {
+ if ( identityDetector.containsKey(local) ) return null;
+ return putInMap(new Node(local, null));
+ }
+
+ private Collection<Node> buildTree(Statement[] children) {
+ if ( children == null ) return Collections.emptyList();
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( Statement child : children ) childNodes.addAll(buildWithStatement(child));
+ return childNodes;
+ }
+
+ //Almost anything is a statement, so this method has a different name to avoid overloading confusion
+ private Collection<Node> buildWithStatement(Statement child) {
+ if ( child == null || identityDetector.containsKey(child) ) return Collections.emptyList();
+ if ( child instanceof TypeDeclaration ) {
+ Node n = buildTree((TypeDeclaration)child);
+ return n == null ? Collections.<Node>emptyList() : Collections.singleton(n);
+ }
+
+ if ( child instanceof LocalDeclaration ) {
+ List<Node> childNodes = new ArrayList<Node>();
+ addIfNotNull(childNodes, buildTree((LocalDeclaration)child));
+ identityDetector.put(child, null);
+ childNodes.addAll(buildWithStatement(((LocalDeclaration)child).initialization));
+ return childNodes;
+ }
+ //We drill down because LocalDeclarations and TypeDeclarations can occur anywhere, even in, say,
+ //an if block, or even the expression on an assert statement!
+
+ identityDetector.put(child, null);
+ return drill(child);
+ }
+
+ private Collection<Node> drill(Statement child) {
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( FieldAccess fa : fieldsOf(child.getClass()) ) childNodes.addAll(buildWithField(child, fa));
+ return childNodes;
+ }
+
+ private static class FieldAccess {
+ final Field field;
+ final int dim;
+
+ FieldAccess(Field field, int dim) {
+ this.field = field;
+ this.dim = dim;
+ }
+ }
+ private Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>();
+ private Collection<FieldAccess> fieldsOf(Class<?> c) {
+ Collection<FieldAccess> fields = fieldsOfASTClasses.get(c);
+ if ( fields != null ) return fields;
+
+ fields = new ArrayList<FieldAccess>();
+ getFields(c, fields);
+ fieldsOfASTClasses.put(c, fields);
+ return fields;
+ }
+
+ private void getFields(Class<?> c, Collection<FieldAccess> fields) {
+ if ( c == ASTNode.class || c == null ) return;
+ for ( Field f : c.getDeclaredFields() ) {
+ Class<?> t = f.getType();
+ int dim = 0;
+ while ( t.isArray() ) {
+ dim++;
+ t = t.getComponentType();
+ }
+ if ( Statement.class.isAssignableFrom(t) ) {
+ f.setAccessible(true);
+ fields.add(new FieldAccess(f, dim));
+ }
+ }
+ getFields(c.getSuperclass(), fields);
+ }
+
+ private Collection<Node> buildWithField(Statement child, FieldAccess fa) {
+ if ( Modifier.isStatic(fa.field.getModifiers()) ) return Collections.emptyList();
+ List<Node> list = new ArrayList<Node>();
+ buildWithField(child, fa, list);
+ return list;
+ }
+
+ private void buildWithField(Statement child, FieldAccess fa, Collection<Node> list) {
+ try {
+ Object o = fa.field.get(child);
+ if ( fa.dim == 0 ) list.addAll(buildWithStatement((Statement)o));
+ else buildWithArray(o, list, fa.dim);
+ } catch ( IllegalAccessException e ) {
+ sneakyThrow(e);
+ }
+ }
+
+ private void buildWithArray(Object array, Collection<Node> list, int dim) {
+ if ( array == null ) return;
+ if ( dim == 1 ) for ( Object v : (Object[])array ) {
+ list.addAll(buildWithStatement((Statement)v));
+ } else for ( Object v : (Object[])array ) {
+ buildWithArray(v, list, dim-1);
+ }
+ }
+}
diff --git a/src/lombok/eclipse/EclipseASTAdapter.java b/src/lombok/eclipse/EclipseASTAdapter.java
new file mode 100644
index 00000000..fa87872f
--- /dev/null
+++ b/src/lombok/eclipse/EclipseASTAdapter.java
@@ -0,0 +1,25 @@
+package lombok.eclipse;
+
+import lombok.eclipse.EclipseAST.Node;
+
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Initializer;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+
+public abstract class EclipseASTAdapter implements EclipseASTVisitor {
+ @Override public void visitCompilationUnit(Node node, CompilationUnitDeclaration unit) {}
+ @Override public void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit) {}
+ @Override public void visitType(Node node, TypeDeclaration type) {}
+ @Override public void endVisitType(Node node, TypeDeclaration type) {}
+ @Override public void visitInitializer(Node node, Initializer initializer) {}
+ @Override public void endVisitInitializer(Node node, Initializer initializer) {}
+ @Override public void visitField(Node node, FieldDeclaration field) {}
+ @Override public void endVisitField(Node node, FieldDeclaration field) {}
+ @Override public void visitMethod(Node node, AbstractMethodDeclaration declaration) {}
+ @Override public void endVisitMethod(Node node, AbstractMethodDeclaration declaration) {}
+ @Override public void visitLocal(Node node, LocalDeclaration declaration) {}
+ @Override public void endVisitLocal(Node node, LocalDeclaration declaration) {}
+}
diff --git a/src/lombok/eclipse/EclipseASTVisitor.java b/src/lombok/eclipse/EclipseASTVisitor.java
new file mode 100644
index 00000000..aff1cc38
--- /dev/null
+++ b/src/lombok/eclipse/EclipseASTVisitor.java
@@ -0,0 +1,159 @@
+package lombok.eclipse;
+
+import java.lang.reflect.Modifier;
+
+import lombok.eclipse.EclipseAST.Node;
+
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Initializer;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+
+public interface EclipseASTVisitor {
+ /**
+ * Called at the very beginning and end.
+ */
+ void visitCompilationUnit(Node node, CompilationUnitDeclaration unit);
+ void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit);
+
+ /**
+ * Called when visiting a type (a class, interface, annotation, enum, etcetera).
+ */
+ void visitType(Node node, TypeDeclaration type);
+ void endVisitType(Node node, TypeDeclaration type);
+
+ /**
+ * Called when visiting a field of a class.
+ * Even though in eclipse initializers (both instance and static) are represented as Initializer objects,
+ * which are a subclass of FieldDeclaration, those do NOT result in a call to this method. They result
+ * in a call to the visitInitializer method.
+ */
+ void visitField(Node node, FieldDeclaration field);
+ void endVisitField(Node node, FieldDeclaration field);
+
+ /**
+ * Called for static and instance initializers. You can tell the difference via the modifier flag on the
+ * ASTNode (8 for static, 0 for not static). The content is in the 'block', not in the 'initialization',
+ * which would always be null for an initializer instance.
+ */
+ void visitInitializer(Node node, Initializer initializer);
+ void endVisitInitializer(Node node, Initializer initializer);
+
+ /**
+ * Called for both methods (MethodDeclaration) and constructors (ConstructorDeclaration), but not for
+ * Clinit objects, which are a vestigial eclipse thing that never contain anything. Static initializers
+ * show up as 'Initializer', in the visitInitializer method, with modifier bit STATIC set.
+ */
+ void visitMethod(Node node, AbstractMethodDeclaration declaration);
+ void endVisitMethod(Node node, AbstractMethodDeclaration declaration);
+
+ /**
+ * Visits a local declaration - that is, something like 'int x = 10;' on the method level. Also called
+ * for method parameter (those would be Arguments, a subclass of LocalDeclaration).
+ */
+ void visitLocal(Node node, LocalDeclaration declaration);
+ void endVisitLocal(Node node, LocalDeclaration declaration);
+
+ public static class EclipseASTPrinter implements EclipseASTVisitor {
+ int indent = 0;
+ private void print(String text, Object... params) {
+ StringBuilder sb = new StringBuilder();
+ for ( int i = 0 ; i < indent ; i++ ) sb.append(" ");
+ System.out.printf(sb.append(text).append('\n').toString(), params);
+ }
+
+ private String str(char[] c) {
+ if ( c == null ) return "(NULL)";
+ else return new String(c);
+ }
+
+ private String str(TypeReference type) {
+ if ( type == null ) return "(NULL)";
+ char[][] c = type.getTypeName();
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for ( char[] d : c ) {
+ sb.append(first ? "" : ".").append(new String(d));
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ @Override public void visitCompilationUnit(Node node, CompilationUnitDeclaration unit) {
+ System.out.println("---------------------------------------------------------");
+ System.out.println(node.isCompleteParse() ? "COMPLETE" : "incomplete");
+
+ print("<CUD %s>", node.getFileName());
+ indent++;
+ }
+
+ @Override public void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit) {
+ indent--;
+ print("</CUD>");
+ }
+
+ @Override public void visitType(Node node, TypeDeclaration type) {
+ print("<TYPE %s>", str(type.name));
+ indent++;
+ }
+
+ @Override public void endVisitType(Node node, TypeDeclaration type) {
+ indent--;
+ print("</TYPE %s>", str(type.name));
+ }
+
+ @Override public void visitInitializer(Node node, Initializer initializer) {
+ Block block = initializer.block;
+ boolean s = (block != null && block.statements != null);
+ print("<%s INITIALIZER: %s>",
+ (initializer.modifiers & Modifier.STATIC) > 0 ? "static" : "instance",
+ s ? "filled" : "blank");
+ indent++;
+ }
+
+ @Override public void endVisitInitializer(Node node, Initializer initializer) {
+ indent--;
+ print("</%s INITIALIZER>", (initializer.modifiers & Modifier.STATIC) > 0 ? "static" : "instance");
+ }
+
+ @Override public void visitField(Node node, FieldDeclaration field) {
+ print("<FIELD %s %s = %s>", str(field.type), str(field.name), field.initialization);
+ indent++;
+ }
+
+ @Override public void endVisitField(Node node, FieldDeclaration field) {
+ indent--;
+ print("</FIELD %s %s>", str(field.type), str(field.name));
+ }
+
+ @Override public void visitMethod(Node node, AbstractMethodDeclaration method) {
+ String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD";
+ print("<%s %s: %s>", type, str(method.selector), method.statements != null ? "filled" : "blank");
+ indent++;
+ }
+
+ @Override public void endVisitMethod(Node node, AbstractMethodDeclaration method) {
+ String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD";
+ indent--;
+ print("</%s %s>", type, str(method.selector));
+ }
+
+ @Override public void visitLocal(Node node, LocalDeclaration local) {
+ String type = local instanceof Argument ? "ARGUMENT" : "LOCAL";
+ print("<%s %s %s = %s>", type, str(local.type), str(local.name), local.initialization);
+ indent++;
+ }
+
+ @Override public void endVisitLocal(Node node, LocalDeclaration local) {
+ String type = local instanceof Argument ? "ARGUMENT" : "LOCAL";
+ indent--;
+ print("</%s %s %s>", type, str(local.type), str(local.name));
+ }
+ }
+}
diff --git a/src/lombok/eclipse/HandleGetter_ecj.java b/src/lombok/eclipse/HandleGetter_ecj.java
index f71376a3..fdfbd7be 100644
--- a/src/lombok/eclipse/HandleGetter_ecj.java
+++ b/src/lombok/eclipse/HandleGetter_ecj.java
@@ -2,6 +2,7 @@ package lombok.eclipse;
import java.lang.reflect.Modifier;
+import lombok.eclipse.EclipseAST.Node;
import lombok.transformations.TransformationsUtil;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -18,15 +19,17 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
public class HandleGetter_ecj {
- public void apply(Annotation annotation, TypeDeclaration type, FieldDeclaration field) {
+ public void apply(Annotation annotation, Node node, FieldDeclaration field) {
TypeReference fieldType = field.type;
- String getterName = TransformationsUtil.toGetterName(new String(field.name), nameEquals(fieldType.getTypeName(), "boolean"));
+ String getterName = TransformationsUtil.toGetterName(
+ new String(field.name), nameEquals(fieldType.getTypeName(), "boolean"));
- for ( AbstractMethodDeclaration method : type.methods ) {
+ TypeDeclaration parent = (TypeDeclaration) node.up().getEclipseNode();
+ if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) {
if ( method.selector != null && new String(method.selector).equals(getterName) ) return;
}
- MethodDeclaration method = new MethodDeclaration(type.compilationResult);
+ MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
method.modifiers = Modifier.PUBLIC;
method.returnType = field.type;
method.annotations = null;
@@ -35,17 +38,22 @@ public class HandleGetter_ecj {
method.binding = null;
method.thrownExceptions = null;
method.typeParameters = null;
- method.scope = new MethodScope(type.scope, method, false);
+ method.scope = parent.scope == null ? null : new MethodScope(parent.scope, method, false);
method.bits |= ASTNode.Bit24;
Expression fieldExpression = new SingleNameReference(field.name, (field.declarationSourceStart << 32) | field.declarationSourceEnd);
Statement returnStatement = new ReturnStatement(fieldExpression, field.sourceStart, field.sourceEnd);
method.bodyStart = method.declarationSourceStart = method.sourceStart = annotation.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = annotation.sourceEnd;
method.statements = new Statement[] { returnStatement };
- AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[type.methods.length + 1];
- System.arraycopy(type.methods, 0, newArray, 0, type.methods.length);
- newArray[type.methods.length] = method;
- type.methods = newArray;
+ if ( parent.methods == null ) {
+ parent.methods = new AbstractMethodDeclaration[1];
+ parent.methods[0] = method;
+ } else {
+ AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[parent.methods.length + 1];
+ System.arraycopy(parent.methods, 0, newArray, 0, parent.methods.length);
+ newArray[parent.methods.length] = method;
+ parent.methods = newArray;
+ }
}
private boolean nameEquals(char[][] typeName, String string) {
diff --git a/src/lombok/eclipse/TransformEclipseAST.java b/src/lombok/eclipse/TransformEclipseAST.java
index 9212a0d4..498b403f 100644
--- a/src/lombok/eclipse/TransformEclipseAST.java
+++ b/src/lombok/eclipse/TransformEclipseAST.java
@@ -1,12 +1,14 @@
package lombok.eclipse;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import lombok.eclipse.EclipseAST.Node;
+
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Initializer;
-import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.parser.Parser;
/**
@@ -23,6 +25,9 @@ import org.eclipse.jdt.internal.compiler.parser.Parser;
* @author rspilker
*/
public class TransformEclipseAST {
+ private static final Map<CompilationUnitDeclaration, EclipseAST> astCache =
+ new WeakHashMap<CompilationUnitDeclaration, EclipseAST>();
+
/**
* 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
@@ -35,26 +40,25 @@ public class TransformEclipseAST {
* @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 ( ast.types != null ) for ( TypeDeclaration type : ast.types ) {
- if ( type.fields != null ) for ( FieldDeclaration field : type.fields ) {
- if ( field.annotations != null ) for ( Annotation annotation : field.annotations ) {
- if ( annotation.type.toString().equals("Getter") ) {
- new HandleGetter_ecj().apply(annotation, type, field);
- }
- }
- }
- }
- }
-
- public static void transform(Parser parser, MethodDeclaration ast) {
-
- }
-
- public static void transform(Parser parser, ConstructorDeclaration ast) {
+ EclipseAST existing = astCache.get(ast);
+ if ( existing == null ) {
+ existing = new EclipseAST(ast);
+ astCache.put(ast, existing);
+ } else existing.reparse();
+ existing.traverse(new AnnotationVisitor());
}
- public static void transform(Parser parser, Initializer ast) {
+ private static class AnnotationVisitor extends EclipseASTAdapter {
+ @Override public void visitField(Node node, FieldDeclaration field) {
+ if ( field.annotations == null ) return;
+ for ( Annotation annotation : field.annotations ) {
+ TypeReference type = annotation.type;
+ if ( type != null && new String(type.getLastToken()).equals("Getter") ) {
+ new HandleGetter_ecj().apply(annotation, node, field);
+ }
+ }
+ }
}
}