From e84bdd32e71b029d8d1f2fa7849e998953984f47 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Fri, 12 Jun 2009 03:40:07 +0200 Subject: 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. --- src/lombok/eclipse/EclipseAST.java | 337 ++++++++++++++++++++++++++++ src/lombok/eclipse/EclipseASTAdapter.java | 25 +++ src/lombok/eclipse/EclipseASTVisitor.java | 159 +++++++++++++ src/lombok/eclipse/HandleGetter_ecj.java | 26 ++- src/lombok/eclipse/TransformEclipseAST.java | 46 ++-- 5 files changed, 563 insertions(+), 30 deletions(-) create mode 100644 src/lombok/eclipse/EclipseAST.java create mode 100644 src/lombok/eclipse/EclipseASTAdapter.java create mode 100644 src/lombok/eclipse/EclipseASTVisitor.java (limited to 'src/lombok/eclipse') 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 children; + boolean handled; + + Node(ASTNode node, Collection children) { + this.node = node; + this.children = children == null ? Collections.emptyList() : children; + } + + public ASTNode getEclipseNode() { + return node; + } + + public Node up() { + return parent; + } + + public Collection 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 identityDetector = new IdentityHashMap(); + private Map nodeMap = new HashMap(); + 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 oldMap = nodeMap; + nodeMap = new HashMap(); + this.top = buildTree(compilationUnitDeclaration); + + //Retain 'handled' flags. + for ( Map.Entry 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 children = buildTree(top.types); + return putInMap(new Node(top, children)); + } + + private void addIfNotNull(Collection collection, Node n) { + if ( n != null ) collection.add(n); + } + + private Collection buildTree(TypeDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List childNodes = new ArrayList(); + for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildTree(type)); + return childNodes; + } + + private Node buildTree(TypeDeclaration type) { + if ( identityDetector.containsKey(type) ) return null; + List childNodes = new ArrayList(); + childNodes.addAll(buildTree(type.fields)); + childNodes.addAll(buildTree(type.memberTypes)); + childNodes.addAll(buildTree(type.methods)); + return putInMap(new Node(type, childNodes)); + } + + private Collection buildTree(FieldDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List childNodes = new ArrayList(); + 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 buildTree(AbstractMethodDeclaration[] children) { + if ( children == null ) return Collections.emptyList(); + List childNodes = new ArrayList(); + for (AbstractMethodDeclaration method : children ) addIfNotNull(childNodes, buildTree(method)); + return childNodes; + } + + private Node buildTree(AbstractMethodDeclaration method) { + if ( identityDetector.containsKey(method) ) return null; + List childNodes = new ArrayList(); + 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 buildTree(Argument[] children) { + if ( children == null ) return Collections.emptyList(); + List childNodes = new ArrayList(); + 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 buildTree(Statement[] children) { + if ( children == null ) return Collections.emptyList(); + List childNodes = new ArrayList(); + 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 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.emptyList() : Collections.singleton(n); + } + + if ( child instanceof LocalDeclaration ) { + List childNodes = new ArrayList(); + 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 drill(Statement child) { + List childNodes = new ArrayList(); + 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, Collection> fieldsOfASTClasses = new HashMap, Collection>(); + private Collection fieldsOf(Class c) { + Collection fields = fieldsOfASTClasses.get(c); + if ( fields != null ) return fields; + + fields = new ArrayList(); + getFields(c, fields); + fieldsOfASTClasses.put(c, fields); + return fields; + } + + private void getFields(Class c, Collection 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 buildWithField(Statement child, FieldAccess fa) { + if ( Modifier.isStatic(fa.field.getModifiers()) ) return Collections.emptyList(); + List list = new ArrayList(); + buildWithField(child, fa, list); + return list; + } + + private void buildWithField(Statement child, FieldAccess fa, Collection 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 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("", node.getFileName()); + indent++; + } + + @Override public void endVisitCompilationUnit(Node node, CompilationUnitDeclaration unit) { + indent--; + print(""); + } + + @Override public void visitType(Node node, TypeDeclaration type) { + print("", str(type.name)); + indent++; + } + + @Override public void endVisitType(Node node, TypeDeclaration type) { + indent--; + print("", 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("", (initializer.modifiers & Modifier.STATIC) > 0 ? "static" : "instance"); + } + + @Override public void visitField(Node node, FieldDeclaration field) { + print("", str(field.type), str(field.name), field.initialization); + indent++; + } + + @Override public void endVisitField(Node node, FieldDeclaration field) { + indent--; + print("", 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("", 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("", 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 astCache = + new WeakHashMap(); + /** * 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); + } + } + } } } -- cgit