aboutsummaryrefslogtreecommitdiff
path: root/src/lombok/eclipse
diff options
context:
space:
mode:
Diffstat (limited to 'src/lombok/eclipse')
-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);
+ }
+ }
+ }
}
}