aboutsummaryrefslogtreecommitdiff
path: root/src/lombok
diff options
context:
space:
mode:
Diffstat (limited to 'src/lombok')
-rw-r--r--src/lombok/core/AST.java268
-rw-r--r--src/lombok/core/AnnotationValues.java281
-rw-r--r--src/lombok/core/TypeResolver.java (renamed from src/lombok/eclipse/TypeResolver.java)53
-rw-r--r--src/lombok/eclipse/Eclipse.java10
-rw-r--r--src/lombok/eclipse/EclipseAST.java370
-rw-r--r--src/lombok/eclipse/EclipseASTAdapter.java4
-rw-r--r--src/lombok/eclipse/EclipseASTVisitor.java32
-rw-r--r--src/lombok/eclipse/EclipseAnnotationHandler.java4
-rw-r--r--src/lombok/eclipse/HandlerLibrary.java287
-rw-r--r--src/lombok/eclipse/TransformEclipseAST.java15
-rw-r--r--src/lombok/eclipse/handlers/HandleGetter_ecj.java11
-rw-r--r--src/lombok/javac/HandlerLibrary.java227
-rw-r--r--src/lombok/javac/JavacAST.java329
-rw-r--r--src/lombok/javac/JavacASTAdapter.java35
-rw-r--r--src/lombok/javac/JavacASTVisitor.java182
-rw-r--r--src/lombok/javac/JavacAnnotationHandler.java6
-rw-r--r--src/lombok/javac/JavacNode.java63
-rw-r--r--src/lombok/javac/apt/Processor.java66
-rw-r--r--src/lombok/javac/handlers/HandleGetter_javac.java27
-rw-r--r--src/lombok/javac/handlers/PKG.java9
20 files changed, 1565 insertions, 714 deletions
diff --git a/src/lombok/core/AST.java b/src/lombok/core/AST.java
new file mode 100644
index 00000000..99902672
--- /dev/null
+++ b/src/lombok/core/AST.java
@@ -0,0 +1,268 @@
+package lombok.core;
+
+import static lombok.Lombok.sneakyThrow;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+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;
+
+public abstract class AST<N> {
+ public enum Kind {
+ COMPILATION_UNIT, TYPE, FIELD, INITIALIZER, METHOD, ANNOTATION, ARGUMENT, LOCAL, STATEMENT;
+ }
+
+ private Node top;
+ private final String fileName;
+ private Map<N, Void> identityDetector = new IdentityHashMap<N, Void>();
+ private Map<N, Node> nodeMap = new IdentityHashMap<N, Node>();
+
+ protected AST(String fileName) {
+ this.fileName = fileName == null ? "(unknown).java" : fileName;
+ }
+
+ protected void setTop(Node top) {
+ this.top = top;
+ }
+
+ public abstract String getPackageDeclaration();
+
+ public abstract Collection<String> getImportStatements();
+
+ protected <T extends Node> T putInMap(T parent) {
+ nodeMap.put(parent.get(), parent);
+ identityDetector.put(parent.get(), null);
+ return parent;
+ }
+
+ protected Map<N, Node> getNodeMap() {
+ return nodeMap;
+ }
+
+ protected void clearState() {
+ identityDetector = new IdentityHashMap<N, Void>();
+ nodeMap = new IdentityHashMap<N, Node>();
+ }
+
+ protected boolean alreadyHandled(N node) {
+ return identityDetector.containsKey(node);
+ }
+
+ protected void setAsHandled(N node) {
+ identityDetector.put(node, null);
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public Node top() {
+ return top;
+ }
+
+ public Node get(N node) {
+ return nodeMap.get(node);
+ }
+
+ public abstract class Node {
+ protected final Kind kind;
+ protected final N node;
+ protected final Collection<? extends Node> children;
+ protected Node parent;
+ protected boolean handled;
+ protected boolean isStructurallySignificant;
+
+ protected Node(N node, Collection<? extends Node> children, Kind kind) {
+ this.kind = kind;
+ this.node = node;
+ this.children = children == null ? Collections.<Node>emptyList() : children;
+ for ( Node child : this.children ) child.parent = this;
+ this.isStructurallySignificant = calculateIsStructurallySignificant();
+ }
+
+ public String getPackageDeclaration() {
+ return AST.this.getPackageDeclaration();
+ }
+
+ public Collection<String> getImportStatements() {
+ return AST.this.getImportStatements();
+ }
+
+ protected abstract boolean calculateIsStructurallySignificant();
+
+ public N get() {
+ return node;
+ }
+
+ public Kind getKind() {
+ return kind;
+ }
+
+ /**
+ * Return the name of your type (simple name), method, field, or local variable. Return null if this
+ * node doesn't really have a name, such as initializers, while statements, etc.
+ */
+ public abstract String getName();
+
+ /** Returns the structurally significant node that encloses this one.
+ *
+ * @see #isStructurallySignificant()
+ */
+ public Node up() {
+ Node result = (Node)parent;
+ while ( result != null && !result.isStructurallySignificant ) result = (Node)result.parent;
+ return result;
+ }
+
+ /**
+ * Returns the direct parent node in the AST tree of this node. For example, a local variable declaration's
+ * direct parent can be e.g. an If block, but its up() Node is the Method that contains it.
+ */
+ public Node directUp() {
+ return parent;
+ }
+
+ public Collection<? extends 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 abstract void addError(String message);
+
+ public abstract void addWarning(String message);
+
+ /**
+ * Structurally significant means: LocalDeclaration, TypeDeclaration, MethodDeclaration, ConstructorDeclaration,
+ * FieldDeclaration, Initializer, and CompilationUnitDeclaration.
+ * The rest is e.g. if statements, while loops, etc.
+ */
+ public boolean isStructurallySignificant() {
+ return isStructurallySignificant;
+ }
+ }
+
+ protected static class FieldAccess {
+ public final Field field;
+ public final int dim;
+
+ FieldAccess(Field field, int dim) {
+ this.field = field;
+ this.dim = dim;
+ }
+ }
+
+ private static Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>();
+ protected 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 == Object.class || c == null ) return;
+ for ( Field f : c.getDeclaredFields() ) {
+ if ( Modifier.isStatic(f.getModifiers()) ) continue;
+ Class<?> t = f.getType();
+ int dim = 0;
+
+ if ( t.isArray() ) {
+ while ( t.isArray() ) {
+ dim++;
+ t = t.getComponentType();
+ }
+ } else if ( Collection.class.isAssignableFrom(t) ) {
+ while ( Collection.class.isAssignableFrom(t) ) {
+ dim++;
+ t = getComponentType(f.getGenericType());
+ }
+ }
+
+ for ( Class<?> statementType : getStatementTypes() ) {
+ if ( statementType.isAssignableFrom(t) ) {
+ f.setAccessible(true);
+ fields.add(new FieldAccess(f, dim));
+ break;
+ }
+ }
+ }
+ getFields(c.getSuperclass(), fields);
+ }
+
+ private Class<?> getComponentType(Type type) {
+ if ( type instanceof ParameterizedType ) {
+ Type component = ((ParameterizedType)type).getActualTypeArguments()[0];
+ return component instanceof Class<?> ? (Class<?>)component : Object.class;
+ } else return Object.class;
+ }
+
+ protected abstract Collection<Class<? extends N>> getStatementTypes();
+
+ protected <T extends Node> Collection<T> buildWithField(Class<T> nodeType, N statement, FieldAccess fa) {
+ List<T> list = new ArrayList<T>();
+ buildWithField0(nodeType, statement, fa, list);
+ return list;
+ }
+
+ private <T extends Node> void buildWithField0(Class<T> nodeType, N child, FieldAccess fa, Collection<T> list) {
+ try {
+ Object o = fa.field.get(child);
+ if ( o == null ) return;
+ if ( fa.dim == 0 ) {
+ Node node = buildStatement(o);
+ if ( node != null ) list.add(nodeType.cast(node));
+ } else if ( o.getClass().isArray() ) buildWithArray(nodeType, o, list, fa.dim);
+ else if ( Collection.class.isInstance(o) ) buildWithCollection(nodeType, o, list, fa.dim);
+ } catch ( IllegalAccessException e ) {
+ sneakyThrow(e);
+ }
+ }
+
+ private <T extends Node> void buildWithArray(Class<T> nodeType, Object array, Collection<T> list, int dim) {
+ if ( dim == 1 ) for ( Object v : (Object[])array ) {
+ if ( v == null ) continue;
+ Node node = buildStatement(v);
+ if ( node != null ) list.add(nodeType.cast(node));
+ } else for ( Object v : (Object[])array ) {
+ buildWithArray(nodeType, v, list, dim-1);
+ }
+ }
+
+ private <T extends Node> void buildWithCollection(Class<T> nodeType, Object collection, Collection<T> list, int dim) {
+ if ( dim == 1 ) for ( Object v : (Collection<?>)collection ) {
+ if ( v == null ) continue;
+ Node node = buildStatement(v);
+ if ( node != null ) list.add(nodeType.cast(node));
+ } else for ( Object v : (Collection<?>)collection ) {
+ buildWithCollection(nodeType, v, list, dim-1);
+ }
+ }
+
+ protected abstract Node buildStatement(Object statement);
+}
diff --git a/src/lombok/core/AnnotationValues.java b/src/lombok/core/AnnotationValues.java
new file mode 100644
index 00000000..ec17e34f
--- /dev/null
+++ b/src/lombok/core/AnnotationValues.java
@@ -0,0 +1,281 @@
+package lombok.core;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class AnnotationValues<A extends Annotation> {
+ private final Class<A> type;
+ private final Map<String, AnnotationValue> values;
+ private final AST<?>.Node ast;
+
+ public static class AnnotationValue {
+ public final List<String> raws;
+ public final List<Object> valueGuesses;
+ private final AST<?>.Node node;
+
+ /**
+ * 'raw' should be the exact expression, for example '5+7', 'AccessLevel.PUBLIC', or 'int.class'.
+ * 'valueGuess' should be a likely guess at the real value intended.
+ *
+ * For classes, supply the class name (qualified or not) as a string.<br />
+ * For enums, supply the simple name part (everything after the last dot) as a string.<br />
+ */
+ public AnnotationValue(AST<?>.Node node, String raw, Object valueGuess) {
+ this.node = node;
+ this.raws = Collections.singletonList(raw);
+ this.valueGuesses = Collections.singletonList(valueGuess);
+ }
+
+ /** When the value is an array type. */
+ public AnnotationValue(AST<?>.Node node, List<String> raws, List<Object> valueGuesses) {
+ this.node = node;
+ this.raws = raws;
+ this.valueGuesses = valueGuesses;
+ }
+
+ /**
+ * Override this if you want more specific behaviour (e.g. get the source position just right).
+ *
+ * @param message English message with the problem.
+ * @param valueIdx The index into the values for this annotation key that caused the problem.
+ * -1 for a problem that applies to all values, otherwise the 0-based index into an array of values.
+ * If there is no array for this value (e.g. value=1 instead of value={1,2}), then always -1 or 0.
+ */
+ public void setError(String message, int valueIdx) {
+ node.addError(message);
+ }
+ }
+
+ public AnnotationValues(Class<A> type, Map<String, AnnotationValue> values, AST<?>.Node ast) {
+ this.type = type;
+ this.values = values;
+ this.ast = ast;
+ }
+
+ public static class AnnotationValueDecodeFail extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public final int idx;
+ public final AnnotationValue owner;
+
+ public AnnotationValueDecodeFail(AnnotationValue owner, String msg, int idx) {
+ super(msg);
+ this.idx = idx;
+ this.owner = owner;
+ }
+ }
+
+ private static AnnotationValueDecodeFail makeNoDefaultFail(AnnotationValue owner, Method method) {
+ return new AnnotationValueDecodeFail(owner,
+ "No value supplied but " + method.getName() + " has no default either.", -1);
+ }
+
+ @SuppressWarnings("unchecked")
+ public A getInstance() throws AnnotationValueDecodeFail {
+ InvocationHandler invocations = new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ AnnotationValue v = values.get(method.getName());
+ if ( v == null ) {
+ Object defaultValue = method.getDefaultValue();
+ if ( defaultValue != null ) return defaultValue;
+ throw makeNoDefaultFail(v, method);
+ }
+
+ boolean isArray = false;
+ Class<?> expected = method.getReturnType();
+ Object array = null;
+ if ( expected.isArray() ) {
+ isArray = true;
+ expected = expected.getComponentType();
+ array = Array.newInstance(expected, 1);
+ }
+
+ if ( !isArray && v.valueGuesses.size() > 1 ) {
+ System.out.println(v.valueGuesses.size() + ": " + v.valueGuesses);
+ throw new AnnotationValueDecodeFail(v,
+ "Expected a single value, but " + method.getName() + " has an array of values", -1);
+ }
+
+ if ( v.valueGuesses.size() == 0 && !isArray ) {
+ Object defaultValue = method.getDefaultValue();
+ if ( defaultValue == null ) throw makeNoDefaultFail(v, method);
+ return defaultValue;
+ }
+
+ int idx = 0;
+ for ( Object guess : v.valueGuesses ) {
+ Object result = guess == null ? null : guessToType(guess, expected, v, idx);
+ if ( !isArray ) {
+ if ( result == null ) {
+ Object defaultValue = method.getDefaultValue();
+ if ( defaultValue == null ) throw makeNoDefaultFail(v, method);
+ return defaultValue;
+ } else return result;
+ } else {
+ if ( result == null ) {
+ if ( v.valueGuesses.size() == 1 ) {
+ Object defaultValue = method.getDefaultValue();
+ if ( defaultValue == null ) throw makeNoDefaultFail(v, method);
+ return defaultValue;
+ } else throw new AnnotationValueDecodeFail(v,
+ "I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
+ }
+ Array.set(array, idx++, result);
+ }
+ }
+
+ return array;
+ }
+ };
+
+ return (A) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, invocations);
+ }
+
+ private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
+ if ( expected == int.class ) {
+ if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) {
+ return ((Number)guess).intValue();
+ }
+ }
+
+ if ( expected == long.class ) {
+ if ( guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) {
+ return ((Number)guess).longValue();
+ }
+ }
+
+ if ( expected == short.class ) {
+ if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) {
+ int intVal = ((Number)guess).intValue();
+ int shortVal = ((Number)guess).shortValue();
+ if ( shortVal == intVal ) return shortVal;
+ }
+ }
+
+ if ( expected == byte.class ) {
+ if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) {
+ int intVal = ((Number)guess).intValue();
+ int byteVal = ((Number)guess).byteValue();
+ if ( byteVal == intVal ) return byteVal;
+ }
+ }
+
+ if ( expected == double.class ) {
+ if ( guess instanceof Number ) return ((Number)guess).doubleValue();
+ }
+
+ if ( expected == float.class ) {
+ if ( guess instanceof Number ) return ((Number)guess).floatValue();
+ }
+
+ if ( expected == boolean.class ) {
+ if ( guess instanceof Boolean ) return ((Boolean)guess).booleanValue();
+ }
+
+ if ( expected == char.class ) {
+ if ( guess instanceof Character ) return ((Character)guess).charValue();
+ }
+
+ if ( expected == String.class ) {
+ if ( guess instanceof String ) return expected;
+ }
+
+ if ( Enum.class.isAssignableFrom(expected) ) {
+ if ( guess instanceof String ) {
+ for ( Object enumConstant : expected.getEnumConstants() ) {
+ String target = ((Enum<?>)enumConstant).name();
+ if ( target.equals(guess) ) return enumConstant;
+ }
+ throw new AnnotationValueDecodeFail(v,
+ "Can't translate " + guess + " to an enum of type " + expected, pos);
+ }
+ }
+
+ if ( Class.class == expected ) {
+ if ( guess instanceof String ) try {
+ return Class.forName(toFQ((String)guess));
+ } catch ( ClassNotFoundException e ) {
+ throw new AnnotationValueDecodeFail(v,
+ "Can't translate " + guess + " to a class object.", pos);
+ }
+ }
+
+ throw new AnnotationValueDecodeFail(v,
+ "Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
+ }
+
+ public List<String> getRawExpressions(String annotationMethodName) {
+ AnnotationValue v = values.get(annotationMethodName);
+ return v == null ? Collections.<String>emptyList() : v.raws;
+ }
+
+ public String getRawExpression(String annotationMethodName) {
+ List<String> l = getRawExpressions(annotationMethodName);
+ return l.isEmpty() ? null : l.get(0);
+ }
+
+ public List<String> getProbableFQTypes(String annotationMethodName) {
+ List<String> result = new ArrayList<String>();
+ AnnotationValue v = values.get(annotationMethodName);
+ if ( v == null ) return Collections.emptyList();
+
+ for ( Object o : v.valueGuesses ) result.add(o == null ? null : toFQ(o.toString()));
+ return result;
+ }
+
+ private String toFQ(String typeName) {
+ Class<?> c;
+ boolean fqn = typeName.indexOf('.') > -1;
+ String prefix = fqn ? typeName.substring(0, typeName.indexOf('.')) : typeName;
+
+ for ( String im : ast.getImportStatements() ) {
+ int idx = im.lastIndexOf('.');
+ String simple = im;
+ if ( idx > -1 ) simple = im.substring(idx+1);
+ if ( simple.equals(prefix) ) {
+ return im + typeName.substring(prefix.length());
+ }
+ }
+
+ c = tryClass(typeName);
+ if ( c != null ) return c.getName();
+
+ c = tryClass("java.lang." + typeName);
+ if ( c != null ) return c.getName();
+
+ //Try star imports
+ for ( String im : ast.getImportStatements() ) {
+ if ( im.endsWith(".*") ) {
+ c = tryClass(im.substring(0, im.length() -1) + typeName);
+ if ( c != null ) return c.getName();
+ }
+ }
+
+ if ( !fqn ) {
+ String pkg = ast.getPackageDeclaration();
+ if ( pkg != null ) return pkg + "." + typeName;
+ }
+
+ return null;
+ }
+
+ private Class<?> tryClass(String name) {
+ try {
+ return Class.forName(name);
+ } catch ( ClassNotFoundException e ) {
+ return null;
+ }
+ }
+
+ public String getProbableFQType(String annotationMethodName) {
+ List<String> l = getProbableFQTypes(annotationMethodName);
+ return l.isEmpty() ? null : l.get(0);
+ }
+}
diff --git a/src/lombok/eclipse/TypeResolver.java b/src/lombok/core/TypeResolver.java
index 41618513..1e356f89 100644
--- a/src/lombok/eclipse/TypeResolver.java
+++ b/src/lombok/core/TypeResolver.java
@@ -1,44 +1,35 @@
-package lombok.eclipse;
+package lombok.core;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import lombok.core.TypeLibrary;
-import lombok.eclipse.EclipseAST.Node;
-
-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.ImportReference;
-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import lombok.core.AST.Kind;
public class TypeResolver {
private final TypeLibrary library;
private Collection<String> imports;
-
- public TypeResolver(TypeLibrary library, EclipseAST.Node top) {
+ public TypeResolver(TypeLibrary library, String packageString, Collection<String> importStrings) {
this.library = library;
- this.imports = makeImportList((CompilationUnitDeclaration) top.getEclipseNode());
+ this.imports = makeImportList(packageString, importStrings);
}
- private static Collection<String> makeImportList(CompilationUnitDeclaration declaration) {
+ private static Collection<String> makeImportList(String packageString, Collection<String> importStrings) {
Set<String> imports = new HashSet<String>();
- if ( declaration.currentPackage != null ) imports.add(toQualifiedName(declaration.currentPackage.getImportName()) + ".*");
- if ( declaration.imports != null ) for ( ImportReference importStatement : declaration.imports ) {
- imports.add(toQualifiedName(importStatement.getImportName()));
- }
+ if ( packageString != null ) imports.add(packageString + ".*");
+ imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings);
return imports;
}
- public Collection<String> findTypeMatches(Node context, TypeReference type) {
- Collection<String> potentialMatches = library.findCompatible(toQualifiedName(type.getTypeName()));
+ public Collection<String> findTypeMatches(AST<?>.Node context, String typeRef) {
+ Collection<String> potentialMatches = library.findCompatible(typeRef);
if ( potentialMatches.isEmpty() ) return Collections.emptyList();
- if ( type.getTypeName().length > 1 ) return potentialMatches;
-
- String simpleName = new String(type.getTypeName()[0]);
+ int idx = typeRef.indexOf('.');
+ if ( idx > -1 ) return potentialMatches;
+ String simpleName = typeRef.substring(idx+1);
//If there's an import statement that explicitly imports a 'Getter' that isn't any of our potentials, return no matches.
if ( nameConflictInImportList(simpleName, potentialMatches) ) return Collections.emptyList();
@@ -48,11 +39,11 @@ public class TypeResolver {
if ( potentialMatches.isEmpty() ) return Collections.emptyList();
//Find a lexically accessible type of the same simple name in the same Compilation Unit. If it exists: no matches.
- Node n = context;
+ AST<?>.Node n = context;
while ( n != null ) {
- if ( n.getEclipseNode() instanceof TypeDeclaration ) {
- char[] name = ((TypeDeclaration)n.getEclipseNode()).name;
- if ( name != null && new String(name).equals(simpleName) ) return Collections.emptyList();
+ if ( n.getKind() == Kind.TYPE ) {
+ String name = n.getName();
+ if ( name != null && name.equals(simpleName) ) return Collections.emptyList();
}
n = n.up();
}
@@ -65,7 +56,7 @@ public class TypeResolver {
Set<String> results = new HashSet<String>();
for ( String importedType : imports ) {
- Collection<String> reduced = library.findCompatible(importedType);
+ Collection<String> reduced = new HashSet<String>(library.findCompatible(importedType));
reduced.retainAll(potentialMatches);
results.addAll(reduced);
}
@@ -87,14 +78,4 @@ public class TypeResolver {
int idx = typeName.lastIndexOf('.');
return idx == -1 ? typeName : typeName.substring(idx+1);
}
-
- private static String toQualifiedName(char[][] typeName) {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for ( char[] c : typeName ) {
- sb.append(first ? "" : ".").append(c);
- first = false;
- }
- return sb.toString();
- }
}
diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java
index e1ff95cd..961b9536 100644
--- a/src/lombok/eclipse/Eclipse.java
+++ b/src/lombok/eclipse/Eclipse.java
@@ -31,4 +31,14 @@ public class Eclipse {
log.log(new Status(IStatus.ERROR, bundleName, message, error));
}
+
+ static String toQualifiedName(char[][] typeName) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for ( char[] c : typeName ) {
+ sb.append(first ? "" : ".").append(c);
+ first = false;
+ }
+ return sb.toString();
+ }
}
diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java
index 3fdd02e4..53a993ec 100644
--- a/src/lombok/eclipse/EclipseAST.java
+++ b/src/lombok/eclipse/EclipseAST.java
@@ -1,17 +1,13 @@
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 lombok.core.AST;
+
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.apt.dispatch.AptProblem;
@@ -22,24 +18,44 @@ import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Clinit;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;
-public class EclipseAST {
+public class EclipseAST extends AST<ASTNode> {
+ @Override public String getPackageDeclaration() {
+ CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get();
+ ImportReference pkg = cud.currentPackage;
+ return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName());
+ }
+
+ @Override public Collection<String> getImportStatements() {
+ List<String> imports = new ArrayList<String>();
+ CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get();
+ if ( cud.imports == null ) return imports;
+ for ( ImportReference imp : cud.imports ) {
+ if ( imp == null ) continue;
+ imports.add(Eclipse.toQualifiedName(imp.getImportName()));
+ }
+
+ return imports;
+ }
+
public void traverse(EclipseASTVisitor visitor) {
Node current = top();
- visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.node);
+ visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.get());
traverseChildren(visitor, current);
- visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.node);
+ visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.get());
}
private void traverseChildren(EclipseASTVisitor visitor, Node node) {
- for ( Node child : node.children ) {
- ASTNode n = child.node;
+ for ( Node child : node.down() ) {
+ ASTNode n = child.get();
if ( n instanceof TypeDeclaration ) {
visitor.visitType(child, (TypeDeclaration)n);
traverseChildren(visitor, child);
@@ -57,20 +73,33 @@ public class EclipseAST {
visitor.visitMethod(child, (AbstractMethodDeclaration)n);
traverseChildren(visitor, child);
visitor.endVisitMethod(child, (AbstractMethodDeclaration)n);
+ } else if ( n instanceof Argument ) {
+ ASTNode parent = child.up().get();
+ AbstractMethodDeclaration method = null;
+ if ( parent instanceof AbstractMethodDeclaration ) method = (AbstractMethodDeclaration)parent;
+ else System.out.println("Weird, this isn't a desc of method: " + parent.getClass() + ": " + parent);
+ visitor.visitMethodArgument(child, (Argument)n, method);
+ traverseChildren(visitor, child);
+ visitor.endVisitMethodArgument(child, (Argument)n, method);
} else if ( n instanceof LocalDeclaration ) {
visitor.visitLocal(child, (LocalDeclaration)n);
traverseChildren(visitor, child);
visitor.endVisitLocal(child, (LocalDeclaration)n);
} else if ( n instanceof Annotation ) {
Node parent = child.up();
- if ( parent.node instanceof TypeDeclaration )
- visitor.visitAnnotationOnType((TypeDeclaration)parent.node, child, (Annotation)n);
- else if ( parent.node instanceof AbstractMethodDeclaration )
- visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)parent.node, child, (Annotation)n);
- else if ( parent.node instanceof FieldDeclaration )
- visitor.visitAnnotationOnField((FieldDeclaration)parent.node, child, (Annotation)n);
- else if ( parent.node instanceof LocalDeclaration )
- visitor.visitAnnotationOnLocal((LocalDeclaration)parent.node, child, (Annotation)n);
+ if ( parent.get() instanceof TypeDeclaration )
+ visitor.visitAnnotationOnType((TypeDeclaration)parent.get(), child, (Annotation)n);
+ else if ( parent.get() instanceof AbstractMethodDeclaration )
+ visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)parent.get(), child, (Annotation)n);
+ else if ( parent.get() instanceof FieldDeclaration )
+ visitor.visitAnnotationOnField((FieldDeclaration)parent.get(), child, (Annotation)n);
+ else if ( parent.get() instanceof Argument )
+ visitor.visitAnnotationOnMethodArgument(
+ (Argument)parent.get(),
+ (AbstractMethodDeclaration)parent.directUp().get(),
+ child, (Annotation)n);
+ else if ( parent.get() instanceof LocalDeclaration )
+ visitor.visitAnnotationOnLocal((LocalDeclaration)parent.get(), child, (Annotation)n);
} else if ( n instanceof Statement ) {
visitor.visitStatement(child, (Statement)n);
traverseChildren(visitor, child);
@@ -83,16 +112,12 @@ public class EclipseAST {
return completeParse;
}
- public String getFileName() {
- return fileName;
- }
-
- public Node top() {
- return top;
+ @Override public Node top() {
+ return (Node) super.top();
}
public Node get(ASTNode node) {
- return nodeMap.get(node);
+ return (Node) super.get(node);
}
private class ParseProblem {
@@ -111,14 +136,14 @@ public class EclipseAST {
}
void addToCompilationResult() {
- addProblemToCompilationResult(getFileName(), (CompilationUnitDeclaration) top().getEclipseNode(),
- isWarning, message, node.getEclipseNode(), sourceStart, sourceEnd);
+ addProblemToCompilationResult(getFileName(), (CompilationUnitDeclaration) top().get(),
+ isWarning, message, node.get(), sourceStart, sourceEnd);
}
}
- public void propagateProblems() {
+ private void propagateProblems() {
if ( queuedProblems.isEmpty() ) return;
- CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().getEclipseNode();
+ CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get();
if ( cud.compilationResult == null ) return;
for ( ParseProblem problem : queuedProblems ) problem.addToCompilationResult();
queuedProblems.clear();
@@ -152,59 +177,44 @@ public class EclipseAST {
ast.compilationResult.record(ecProblem, null);
}
- public final class Node {
- final ASTNode node;
- Node parent;
- final Collection<Node> children;
- boolean handled;
- private final boolean isStructurallySignificant;
+ public final class Node extends AST<ASTNode>.Node {
+ Node(ASTNode node, Collection<Node> children, Kind kind) {
+ super(node, children, kind);
+ }
- Node(ASTNode node, Collection<Node> children) {
- this.node = node;
- this.children = children == null ? Collections.<Node>emptyList() : children;
- this.isStructurallySignificant = calculateIsStructurallySignificant();
+ @Override public String getName() {
+ final char[] n;
+ if ( node instanceof TypeDeclaration ) n = ((TypeDeclaration)node).name;
+ else if ( node instanceof FieldDeclaration ) n = ((FieldDeclaration)node).name;
+ else if ( node instanceof AbstractMethodDeclaration ) n = ((AbstractMethodDeclaration)node).selector;
+ else if ( node instanceof LocalDeclaration ) n = ((LocalDeclaration)node).name;
+ else n = null;
+
+ return n == null ? null : new String(n);
}
- public void addError(String message) {
- this.addError(message, this.getEclipseNode().sourceStart, this.getEclipseNode().sourceEnd);
+ @Override public void addError(String message) {
+ this.addError(message, this.get().sourceStart, this.get().sourceEnd);
}
public void addError(String message, int sourceStart, int sourceEnd) {
addProblem(new ParseProblem(false, message, this, sourceStart, sourceEnd));
}
- public void addWarning(String message) {
- this.addWarning(message, this.getEclipseNode().sourceStart, this.getEclipseNode().sourceEnd);
+ @Override public void addWarning(String message) {
+ this.addWarning(message, this.get().sourceStart, this.get().sourceEnd);
}
public void addWarning(String message, int sourceStart, int sourceEnd) {
addProblem(new ParseProblem(true, message, this, sourceStart, sourceEnd));
}
- public ASTNode getEclipseNode() {
- return node;
- }
-
- /** Returns the structurally significant node that encloses this one.
- *
- * @see #isStructurallySignificant()
- */
- public Node up() {
- Node result = parent;
- while ( result != null && !result.isStructurallySignificant() ) result = result.parent;
- return result;
- }
-
- /**
- * Structurally significant means: LocalDeclaration, TypeDeclaration, MethodDeclaration, ConstructorDeclaration,
- * FieldDeclaration, Initializer, and CompilationUnitDeclaration.
- * The rest is e.g. if statements, while loops, etc.
- */
- public boolean isStructurallySignificant() {
- return isStructurallySignificant;
+ /** {@inheritDoc} */
+ @Override public Node up() {
+ return (Node) super.up();
}
- private boolean calculateIsStructurallySignificant() {
+ @Override protected boolean calculateIsStructurallySignificant() {
if ( node instanceof TypeDeclaration ) return true;
if ( node instanceof AbstractMethodDeclaration ) return true;
if ( node instanceof FieldDeclaration ) return true;
@@ -213,33 +223,20 @@ public class EclipseAST {
return false;
}
- /**
- * Returns the direct parent node in the AST tree of this node. For example, a local variable declaration's
- * direct parent can be e.g. an If block, but its up() Node is the Method that contains it.
- */
+ /** {@inheritDoc} */
public Node directUp() {
- return parent;
- }
-
- public Collection<Node> down() {
- return children;
- }
-
- public boolean isHandled() {
- return handled;
+ return (Node) super.directUp();
}
- public Node setHandled() {
- this.handled = true;
- return this;
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public Collection<Node> down() {
+ return (Collection<Node>) children;
}
- public Node top() {
- return top;
- }
-
- public String getFileName() {
- return fileName;
+ /** {@inheritDoc} */
+ @Override public Node top() {
+ return (Node) super.top();
}
public boolean isCompleteParse() {
@@ -247,33 +244,33 @@ public class EclipseAST {
}
}
- 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) {
+ super(toFileName(ast));
this.compilationUnitDeclaration = ast;
- this.fileName = ast.compilationResult.fileName == null ? "(unknown).java" : new String(ast.compilationResult.fileName);
- this.top = buildTree(ast);
+ setTop(buildCompilationUnit(ast));
this.completeParse = isComplete(ast);
}
+ private static String toFileName(CompilationUnitDeclaration ast) {
+ return ast.compilationResult.fileName == null ? null : new String(ast.compilationResult.fileName);
+ }
+
public void reparse() {
propagateProblems();
if ( completeParse ) return;
boolean newCompleteParse = isComplete(compilationUnitDeclaration);
if ( !newCompleteParse ) return;
- Map<ASTNode, Node> oldMap = nodeMap;
- nodeMap = new HashMap<ASTNode, Node>();
- this.top = buildTree(compilationUnitDeclaration);
+ Map<ASTNode, AST<ASTNode>.Node> oldMap = getNodeMap();
+ clearState();
+ setTop(buildCompilationUnit(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;
+ for ( Map.Entry<ASTNode, AST<ASTNode>.Node> e : getNodeMap().entrySet() ) {
+ Node oldEntry = (Node) oldMap.get(e.getKey());
+ if ( oldEntry != null && oldEntry.isHandled() ) e.getValue().setHandled();
}
this.completeParse = true;
@@ -283,44 +280,36 @@ public class EclipseAST {
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 Node buildCompilationUnit(CompilationUnitDeclaration top) {
+ Collection<Node> children = buildTypes(top.types);
+ return putInMap(new Node(top, children, Kind.COMPILATION_UNIT));
}
private void addIfNotNull(Collection<Node> collection, Node n) {
if ( n != null ) collection.add(n);
}
- private Collection<Node> buildTree(TypeDeclaration[] children) {
+ private Collection<Node> buildTypes(TypeDeclaration[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
- for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildTree(type));
+ for ( TypeDeclaration type : children ) addIfNotNull(childNodes, buildType(type));
return childNodes;
}
- private Node buildTree(TypeDeclaration type) {
- if ( identityDetector.containsKey(type) ) return null;
+ private Node buildType(TypeDeclaration type) {
+ if ( alreadyHandled(type) ) return null;
List<Node> childNodes = new ArrayList<Node>();
- childNodes.addAll(buildTree(type.fields));
- childNodes.addAll(buildTree(type.memberTypes));
- childNodes.addAll(buildTree(type.methods));
- childNodes.addAll(buildTree(type.annotations));
- return putInMap(new Node(type, childNodes));
+ childNodes.addAll(buildFields(type.fields));
+ childNodes.addAll(buildTypes(type.memberTypes));
+ childNodes.addAll(buildMethods(type.methods));
+ childNodes.addAll(buildAnnotations(type.annotations));
+ return putInMap(new Node(type, childNodes, Kind.TYPE));
}
- private Collection<Node> buildTree(FieldDeclaration[] children) {
+ private Collection<Node> buildFields(FieldDeclaration[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
- for ( FieldDeclaration child : children ) addIfNotNull(childNodes, buildTree(child));
+ for ( FieldDeclaration child : children ) addIfNotNull(childNodes, buildField(child));
return childNodes;
}
@@ -329,152 +318,97 @@ public class EclipseAST {
else return Collections.singleton(item);
}
- private Node buildTree(FieldDeclaration field) {
- if ( field instanceof Initializer ) return buildTree((Initializer)field);
- if ( identityDetector.containsKey(field) ) return null;
+ private Node buildField(FieldDeclaration field) {
+ if ( field instanceof Initializer ) return buildInitializer((Initializer)field);
+ if ( alreadyHandled(field) ) return null;
List<Node> childNodes = new ArrayList<Node>();
- addIfNotNull(childNodes, buildWithStatement(field.initialization));
- childNodes.addAll(buildTree(field.annotations));
- return putInMap(new Node(field, childNodes));
+ addIfNotNull(childNodes, buildStatement(field.initialization));
+ childNodes.addAll(buildAnnotations(field.annotations));
+ return putInMap(new Node(field, childNodes, Kind.FIELD));
}
- private Node buildTree(Initializer initializer) {
- if ( identityDetector.containsKey(initializer) ) return null;
- return putInMap(new Node(initializer, singleton(buildWithStatement(initializer.block))));
+ private Node buildInitializer(Initializer initializer) {
+ if ( alreadyHandled(initializer) ) return null;
+ return putInMap(new Node(initializer, singleton(buildStatement(initializer.block)), Kind.INITIALIZER));
}
- private Collection<Node> buildTree(AbstractMethodDeclaration[] children) {
+ private Collection<Node> buildMethods(AbstractMethodDeclaration[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
- for (AbstractMethodDeclaration method : children ) addIfNotNull(childNodes, buildTree(method));
+ for (AbstractMethodDeclaration method : children ) addIfNotNull(childNodes, buildMethod(method));
return childNodes;
}
- private Node buildTree(AbstractMethodDeclaration method) {
- if ( identityDetector.containsKey(method) ) return null;
+ private Node buildMethod(AbstractMethodDeclaration method) {
+ if ( alreadyHandled(method) ) return null;
List<Node> childNodes = new ArrayList<Node>();
- childNodes.addAll(buildTree(method.arguments));
- childNodes.addAll(buildTree(method.statements));
- childNodes.addAll(buildTree(method.annotations));
- return putInMap(new Node(method, childNodes));
+ childNodes.addAll(buildArguments(method.arguments));
+ childNodes.addAll(buildStatements(method.statements));
+ childNodes.addAll(buildAnnotations(method.annotations));
+ return putInMap(new Node(method, childNodes, Kind.METHOD));
}
//Arguments are a kind of LocalDeclaration. They can definitely contain lombok annotations, so we care about them.
- private Collection<Node> buildTree(Argument[] children) {
+ private Collection<Node> buildArguments(Argument[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
for ( LocalDeclaration local : children ) {
- addIfNotNull(childNodes, buildTree(local));
+ addIfNotNull(childNodes, buildLocal(local));
}
return childNodes;
}
- private Node buildTree(LocalDeclaration local) {
- if ( identityDetector.containsKey(local) ) return null;
+ private Node buildLocal(LocalDeclaration local) {
+ if ( alreadyHandled(local) ) return null;
List<Node> childNodes = new ArrayList<Node>();
- addIfNotNull(childNodes, buildWithStatement(local.initialization));
- childNodes.addAll(buildTree(local.annotations));
- return putInMap(new Node(local, childNodes));
+ addIfNotNull(childNodes, buildStatement(local.initialization));
+ childNodes.addAll(buildAnnotations(local.annotations));
+ return putInMap(new Node(local, childNodes, Kind.LOCAL));
}
- private Collection<Node> buildTree(Annotation[] annotations) {
+ private Collection<Node> buildAnnotations(Annotation[] annotations) {
if ( annotations == null ) return Collections.emptyList();
List<Node> elements = new ArrayList<Node>();
for ( Annotation an : annotations ) {
if ( an == null ) continue;
- elements.add(putInMap(new Node(an, null)));
+ if ( alreadyHandled(an) ) continue;
+ elements.add(putInMap(new Node(an, null, Kind.ANNOTATION)));
}
return elements;
}
- private Collection<Node> buildTree(Statement[] children) {
+ private Collection<Node> buildStatements(Statement[] children) {
if ( children == null ) return Collections.emptyList();
List<Node> childNodes = new ArrayList<Node>();
- for ( Statement child : children ) addIfNotNull(childNodes, buildWithStatement(child));
+ for ( Statement child : children ) addIfNotNull(childNodes, buildStatement(child));
return childNodes;
}
//Almost anything is a statement, so this method has a different name to avoid overloading confusion
- private Node buildWithStatement(Statement child) {
- if ( child == null || identityDetector.containsKey(child) ) return null;
- if ( child instanceof TypeDeclaration ) return buildTree((TypeDeclaration)child);
+ private Node buildStatement(Statement child) {
+ if ( child == null || alreadyHandled(child) ) return null;
+ if ( child instanceof TypeDeclaration ) return buildType((TypeDeclaration)child);
- if ( child instanceof LocalDeclaration ) return buildTree((LocalDeclaration)child);
+ if ( child instanceof LocalDeclaration ) return buildLocal((LocalDeclaration)child);
//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);
+ setAsHandled(child);
return drill(child);
}
- private Node drill(Statement statement) {
+ protected Node drill(Statement statement) {
List<Node> childNodes = new ArrayList<Node>();
- for ( FieldAccess fa : fieldsOf(statement.getClass()) ) childNodes.addAll(buildWithField(statement, fa));
- return putInMap(new Node(statement, childNodes));
- }
-
- private static class FieldAccess {
- final Field field;
- final int dim;
-
- FieldAccess(Field field, int dim) {
- this.field = field;
- this.dim = dim;
- }
- }
-
- private static 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() ) {
- if ( Modifier.isStatic(f.getModifiers()) ) continue;
- 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);
+ for ( FieldAccess fa : fieldsOf(statement.getClass()) ) childNodes.addAll(buildWithField(Node.class, statement, fa));
+ return putInMap(new Node(statement, childNodes, Kind.STATEMENT));
}
- private Collection<Node> buildWithField(Statement statement, FieldAccess fa) {
- List<Node> list = new ArrayList<Node>();
- buildWithField(statement, fa, list);
- return list;
+ @Override protected Collection<Class<? extends ASTNode>> getStatementTypes() {
+ return Collections.<Class<? extends ASTNode>>singleton(Statement.class);
}
- private void buildWithField(Statement child, FieldAccess fa, Collection<Node> list) {
- try {
- Object o = fa.field.get(child);
- if ( fa.dim == 0 ) addIfNotNull(list, 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 ) {
- addIfNotNull(list, buildWithStatement((Statement)v));
- } else for ( Object v : (Object[])array ) {
- buildWithArray(v, list, dim-1);
- }
+ @Override protected Node buildStatement(Object node) {
+ return buildStatement((Statement)node);
}
}
diff --git a/src/lombok/eclipse/EclipseASTAdapter.java b/src/lombok/eclipse/EclipseASTAdapter.java
index 1bd5ae3d..61d161d1 100644
--- a/src/lombok/eclipse/EclipseASTAdapter.java
+++ b/src/lombok/eclipse/EclipseASTAdapter.java
@@ -4,6 +4,7 @@ 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.Initializer;
@@ -25,6 +26,9 @@ public abstract class EclipseASTAdapter implements EclipseASTVisitor {
@Override public void visitMethod(Node methodNode, AbstractMethodDeclaration method) {}
@Override public void visitAnnotationOnMethod(AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {}
@Override public void endVisitMethod(Node methodNode, AbstractMethodDeclaration method) {}
+ @Override public void visitMethodArgument(Node argNode, Argument arg, AbstractMethodDeclaration method) {}
+ @Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {}
+ @Override public void endVisitMethodArgument(Node argNode, Argument arg, AbstractMethodDeclaration method) {}
@Override public void visitLocal(Node localNode, LocalDeclaration local) {}
@Override public void visitAnnotationOnLocal(LocalDeclaration local, Node annotationNode, Annotation annotation) {}
@Override public void endVisitLocal(Node localNode, LocalDeclaration local) {}
diff --git a/src/lombok/eclipse/EclipseASTVisitor.java b/src/lombok/eclipse/EclipseASTVisitor.java
index 9ab56ebe..0c55d222 100644
--- a/src/lombok/eclipse/EclipseASTVisitor.java
+++ b/src/lombok/eclipse/EclipseASTVisitor.java
@@ -60,8 +60,14 @@ public interface EclipseASTVisitor {
void endVisitMethod(Node methodNode, AbstractMethodDeclaration method);
/**
- * 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).
+ * Visits a method argument
+ */
+ void visitMethodArgument(Node argNode, Argument arg, AbstractMethodDeclaration method);
+ void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, Node annotationNode, Annotation annotation);
+ void endVisitMethodArgument(Node argNode, Argument arg, AbstractMethodDeclaration method);
+
+ /**
+ * Visits a local declaration - that is, something like 'int x = 10;' on the method level.
*/
void visitLocal(Node localNode, LocalDeclaration local);
void visitAnnotationOnLocal(LocalDeclaration local, Node annotationNode, Annotation annotation);
@@ -69,8 +75,6 @@ public interface EclipseASTVisitor {
/**
* Visits a statement that isn't any of the other visit methods (e.g. TypeDeclaration).
- * @param node
- * @param statement
*/
void visitStatement(Node statementNode, Statement statement);
void endVisitStatement(Node statementNode, Statement statement);
@@ -171,9 +175,22 @@ public interface EclipseASTVisitor {
print("</%s %s>", type, str(method.selector));
}
+ @Override public void visitMethodArgument(Node node, Argument arg, AbstractMethodDeclaration method) {
+ print("<METHODARG %s %s = %s>", str(arg.type), str(arg.name), arg.initialization);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, Node node, Annotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitMethodArgument(Node node, Argument arg, AbstractMethodDeclaration method) {
+ indent--;
+ print("</METHODARG %s %s>", str(arg.type), str(arg.name));
+ }
+
@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);
+ print("<LOCAL %s %s = %s>", str(local.type), str(local.name), local.initialization);
indent++;
}
@@ -182,9 +199,8 @@ public interface EclipseASTVisitor {
}
@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));
+ print("</LOCAL %s %s>", str(local.type), str(local.name));
}
@Override public void visitStatement(Node node, Statement statement) {
diff --git a/src/lombok/eclipse/EclipseAnnotationHandler.java b/src/lombok/eclipse/EclipseAnnotationHandler.java
index c6cc23be..816ba61c 100644
--- a/src/lombok/eclipse/EclipseAnnotationHandler.java
+++ b/src/lombok/eclipse/EclipseAnnotationHandler.java
@@ -1,5 +1,7 @@
package lombok.eclipse;
+import lombok.core.AnnotationValues;
+
public interface EclipseAnnotationHandler<T extends java.lang.annotation.Annotation> {
- void handle(T annotation, org.eclipse.jdt.internal.compiler.ast.Annotation ast, EclipseAST.Node annotationNode);
+ void handle(AnnotationValues<T> annotation, org.eclipse.jdt.internal.compiler.ast.Annotation ast, EclipseAST.Node annotationNode);
}
diff --git a/src/lombok/eclipse/HandlerLibrary.java b/src/lombok/eclipse/HandlerLibrary.java
index 8a7f6edc..2e3e4541 100644
--- a/src/lombok/eclipse/HandlerLibrary.java
+++ b/src/lombok/eclipse/HandlerLibrary.java
@@ -1,34 +1,36 @@
package lombok.eclipse;
+import static lombok.eclipse.Eclipse.toQualifiedName;
+
import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
+import lombok.core.AnnotationValues;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
+import lombok.core.TypeResolver;
+import lombok.core.AnnotationValues.AnnotationValue;
+import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
import lombok.eclipse.EclipseAST.Node;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
-import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
-import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
public class HandlerLibrary {
@@ -43,11 +45,56 @@ public class HandlerLibrary {
this.annotationClass = annotationClass;
}
- @SuppressWarnings("unchecked")
- public void handle(Object annInstance,
- org.eclipse.jdt.internal.compiler.ast.Annotation annotation,
- Node annotationNode) {
- handler.handle((T) annInstance, annotation, annotationNode);
+ public void handle(org.eclipse.jdt.internal.compiler.ast.Annotation annotation,
+ final Node annotationNode) {
+ Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
+
+ final MemberValuePair[] pairs = annotation.memberValuePairs();
+ for ( Method m : annotationClass.getDeclaredMethods() ) {
+ if ( !Modifier.isPublic(m.getModifiers()) ) continue;
+ String name = m.getName();
+ List<String> raws = new ArrayList<String>();
+ List<Object> guesses = new ArrayList<Object>();
+ Expression fullExpression = null;
+ Expression[] expressions = null;
+
+ if ( pairs != null ) for ( MemberValuePair pair : pairs ) {
+ char[] n = pair.name;
+ String mName = n == null ? "value" : new String(name);
+ if ( !mName.equals(name) ) continue;
+ fullExpression = pair.value;
+ }
+
+ if ( fullExpression != null ) {
+ if ( fullExpression instanceof ArrayInitializer ) {
+ expressions = ((ArrayInitializer)fullExpression).expressions;
+ } else expressions = new Expression[] { fullExpression };
+ for ( Expression ex : expressions ) {
+ StringBuffer sb = new StringBuffer();
+ ex.print(0, sb);
+ raws.add(sb.toString());
+ guesses.add(calculateValue(ex));
+ }
+ }
+
+ final Expression fullExpr = fullExpression;
+ final Expression[] exprs = expressions;
+
+ values.put(name, new AnnotationValue(annotationNode, raws, guesses) {
+ @Override public void setError(String message, int valueIdx) {
+ Expression ex;
+ if ( valueIdx == -1 ) ex = fullExpr;
+ else ex = exprs[valueIdx];
+
+ int sourceStart = ex.sourceStart;
+ int sourceEnd = ex.sourceEnd;
+
+ annotationNode.addError(message, sourceStart, sourceEnd);
+ }
+ });
+ }
+
+ handler.handle(new AnnotationValues<T>(annotationClass, values, annotationNode), annotation, annotationNode);
}
}
@@ -56,192 +103,32 @@ public class HandlerLibrary {
private Collection<EclipseASTVisitor> visitorHandlers = new ArrayList<EclipseASTVisitor>();
- @SuppressWarnings("unchecked")
- public <A extends Annotation> A createAnnotation(Class<A> target,
- CompilationUnitDeclaration ast,
- org.eclipse.jdt.internal.compiler.ast.Annotation node) throws AnnotationValueDecodeFail {
- final Map<String, Object> values = new HashMap<String, Object>();
-
- final MemberValuePair[] pairs = node.memberValuePairs();
-
- for ( Method m : target.getMethods() ) {
- String name = m.getName();
- Object value = m.getDefaultValue();
- for ( MemberValuePair pair : pairs ) {
- if ( name.equals(new String(pair.name)) ) {
- value = calculateValue(pair, ast, m.getReturnType(), pair.value);
- break;
- }
- }
- values.put(name, value);
- }
-
- InvocationHandler invocations = new InvocationHandler() {
- @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return values.get(method.getName());
- }
- };
-
- return (A) Proxy.newProxyInstance(target.getClassLoader(), new Class[] { target }, invocations);
- }
-
- private Object calculateValue(MemberValuePair pair,
- CompilationUnitDeclaration ast, Class<?> type, Expression e) throws AnnotationValueDecodeFail {
+ private static Object calculateValue(Expression e) {
if ( e instanceof Literal ) {
((Literal)e).computeConstant();
- return convertConstant(pair, type, e.constant);
- } else if ( e instanceof ArrayInitializer ) {
- if ( !type.isArray() ) throw new AnnotationValueDecodeFail(pair, "Did not expect an array here.");
-
- Class<?> component = type.getComponentType();
- Expression[] expressions = ((ArrayInitializer)e).expressions;
- int length = expressions == null ? 0 : expressions.length;
- Object[] values = new Object[length];
- for (int i = 0; i < length; i++) {
- values[i] = calculateValue(pair, ast, component, expressions[i]);
+ switch ( e.constant.typeID() ) {
+ case TypeIds.T_int: return e.constant.intValue();
+ case TypeIds.T_byte: return e.constant.byteValue();
+ case TypeIds.T_short: return e.constant.shortValue();
+ case TypeIds.T_char: return e.constant.charValue();
+ case TypeIds.T_float: return e.constant.floatValue();
+ case TypeIds.T_double: return e.constant.doubleValue();
+ case TypeIds.T_boolean: return e.constant.booleanValue();
+ case TypeIds.T_long: return e.constant.longValue();
+ case TypeIds.T_JavaLangString: return e.constant.stringValue();
+ default: return null;
}
- return values;
} else if ( e instanceof ClassLiteralAccess ) {
- if ( type == Class.class ) return toClass(pair, ast, str(((ClassLiteralAccess)e).type.getTypeName()));
- else throw new AnnotationValueDecodeFail(pair, "Expected a " + type + " literal.");
- } else if ( e instanceof NameReference ) {
- String s = null;
- if ( e instanceof SingleNameReference ) s = new String(((SingleNameReference)e).token);
- else if ( e instanceof QualifiedNameReference ) s = str(((QualifiedNameReference)e).tokens);
- if ( Enum.class.isAssignableFrom(type) ) return toEnum(pair, type, s);
- throw new AnnotationValueDecodeFail(pair, "Lombok annotations must contain literals only.");
- } else {
- throw new AnnotationValueDecodeFail(pair, "Lombok could not decode this annotation parameter.");
- }
- }
-
- private Enum<?> toEnum(MemberValuePair pair, Class<?> enumType, String ref) throws AnnotationValueDecodeFail {
- int idx = ref.indexOf('.');
- if ( idx > -1 ) ref = ref.substring(idx +1);
- Object[] enumConstants = enumType.getEnumConstants();
- for ( Object constant : enumConstants ) {
- String target = ((Enum<?>)constant).name();
- if ( target.equals(ref) ) return (Enum<?>) constant;
+ return Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName());
+ } else if ( e instanceof SingleNameReference ) {
+ return new String(((SingleNameReference)e).token);
+ } else if ( e instanceof QualifiedNameReference ) {
+ String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
+ int idx = qName.lastIndexOf('.');
+ return idx == -1 ? qName : qName.substring(idx+1);
}
- throw new AnnotationValueDecodeFail(pair, "I can't figure out which enum constant you mean.");
- }
-
- private Class<?> toClass(MemberValuePair pair, CompilationUnitDeclaration ast, String typeName) throws AnnotationValueDecodeFail {
- Class<?> c;
- boolean fqn = typeName.indexOf('.') > -1;
-
- if ( fqn ) {
- c = tryClass(typeName);
- if ( c != null ) return c;
- }
-
- for ( ImportReference ref : ast.imports ) {
- String im = str(ref.tokens);
- int idx = im.lastIndexOf('.');
- String simple = im;
- if ( idx > -1 ) simple = im.substring(idx+1);
- if ( simple.equals(typeName) ) {
- c = tryClass(im);
- if ( c != null ) return c;
- }
- }
-
- if ( ast.currentPackage != null && ast.currentPackage.tokens != null ) {
- String pkg = str(ast.currentPackage.tokens);
- c = tryClass(pkg + "." + typeName);
- if ( c != null ) return c;
- }
-
- c = tryClass("java.lang." + typeName);
- if ( c != null ) return c;
- if ( !fqn ) {
- c = tryClass(typeName);
- if ( c != null ) return c;
- }
-
- //Try star imports
- for ( ImportReference ref : ast.imports ) {
- String im = str(ref.tokens);
- if ( im.endsWith(".*") ) {
- c = tryClass(im.substring(0, im.length() -1) + typeName);
- if ( c != null ) return c;
- }
- }
-
- throw new AnnotationValueDecodeFail(pair, "I can't find this class. Try using the fully qualified name.");
- }
-
- private Class<?> tryClass(String name) {
- try {
- return Class.forName(name);
- } catch ( ClassNotFoundException e ) {
- return null;
- }
- }
-
- private Object convertConstant(MemberValuePair pair, Class<?> type, Constant constant) throws AnnotationValueDecodeFail {
- int targetTypeID;
- boolean array = type.isArray();
- if ( array ) type = type.getComponentType();
-
- if ( type == int.class ) targetTypeID = TypeIds.T_int;
- else if ( type == long.class ) targetTypeID = TypeIds.T_long;
- else if ( type == short.class ) targetTypeID = TypeIds.T_short;
- else if ( type == byte.class ) targetTypeID = TypeIds.T_byte;
- else if ( type == double.class ) targetTypeID = TypeIds.T_double;
- else if ( type == float.class ) targetTypeID = TypeIds.T_float;
- else if ( type == String.class ) targetTypeID = TypeIds.T_JavaLangString;
- else if ( type == char.class ) targetTypeID = TypeIds.T_char;
- else if ( type == boolean.class ) targetTypeID = TypeIds.T_boolean;
- else {
- //Enum or Class, so a constant isn't going to be very useful.
- throw new AnnotationValueDecodeFail(pair, "Expected a constant of some sort here (a number or a string)");
- }
- if ( !Expression.isConstantValueRepresentable(constant, constant.typeID(), targetTypeID) ) {
- throw new AnnotationValueDecodeFail(pair, "I can't turn this literal into a " + type);
- }
-
- Object o = null;
-
- if ( type == int.class ) o = constant.intValue();
- else if ( type == long.class ) o = constant.longValue();
- else if ( type == short.class ) o = constant.shortValue();
- else if ( type == byte.class ) o = constant.byteValue();
- else if ( type == double.class ) o = constant.doubleValue();
- else if ( type == float.class ) o = constant.floatValue();
- else if ( type == String.class ) o = constant.stringValue();
- else if ( type == char.class ) o = constant.charValue();
- else if ( type == boolean.class ) o = constant.booleanValue();
-
- if ( array ) {
- Object a = Array.newInstance(type, 1);
- Array.set(a, 0, o);
- return a;
- }
-
- return o;
- }
-
- private static class AnnotationValueDecodeFail extends Exception {
- private static final long serialVersionUID = 1L;
-
- MemberValuePair pair;
-
- AnnotationValueDecodeFail(MemberValuePair pair, String msg) {
- super(msg);
- this.pair = pair;
- }
- }
-
- private static String str(char[][] c) {
- boolean first = true;
- StringBuilder sb = new StringBuilder();
- for ( char[] part : c ) {
- sb.append(first ? "" : ".").append(part);
- first = false;
- }
- return sb.toString();
+ return null;
}
public static HandlerLibrary load() {
@@ -284,22 +171,20 @@ public class HandlerLibrary {
public void handle(CompilationUnitDeclaration ast, EclipseAST.Node annotationNode,
org.eclipse.jdt.internal.compiler.ast.Annotation annotation) {
- TypeResolver resolver = new TypeResolver(typeLibrary, annotationNode.top());
+ String pkgName = annotationNode.getPackageDeclaration();
+ Collection<String> imports = annotationNode.getImportStatements();
+
+ TypeResolver resolver = new TypeResolver(typeLibrary, pkgName, imports);
TypeReference rawType = annotation.type;
if ( rawType == null ) return;
- for ( String fqn : resolver.findTypeMatches(annotationNode, annotation.type) ) {
+ for ( String fqn : resolver.findTypeMatches(annotationNode, toQualifiedName(annotation.type.getTypeName())) ) {
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if ( container == null ) continue;
- Object annInstance;
- try {
- annInstance = createAnnotation(container.annotationClass, ast, annotation);
- } catch ( AnnotationValueDecodeFail e ) {
- annotationNode.addError(e.getMessage(), e.pair.sourceStart, e.pair.sourceEnd);
- return;
- }
try {
- container.handle(annInstance, annotation, annotationNode);
+ container.handle(annotation, annotationNode);
+ } catch ( AnnotationValueDecodeFail fail ) {
+ fail.owner.setError(fail.getMessage(), fail.idx);
} catch ( Throwable t ) {
Eclipse.error(String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
}
diff --git a/src/lombok/eclipse/TransformEclipseAST.java b/src/lombok/eclipse/TransformEclipseAST.java
index 55cdd822..f77639e1 100644
--- a/src/lombok/eclipse/TransformEclipseAST.java
+++ b/src/lombok/eclipse/TransformEclipseAST.java
@@ -6,6 +6,7 @@ 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;
@@ -114,25 +115,31 @@ public class TransformEclipseAST {
private static class AnnotationVisitor extends EclipseASTAdapter {
@Override public void visitAnnotationOnField(FieldDeclaration field, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
- handlers.handle((CompilationUnitDeclaration) annotationNode.top().node, annotationNode, annotation);
+ handlers.handle((CompilationUnitDeclaration) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
+ }
+
+ @Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handle((CompilationUnitDeclaration) annotationNode.top().get(), annotationNode, annotation);
annotationNode.setHandled();
}
@Override public void visitAnnotationOnLocal(LocalDeclaration local, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
- handlers.handle((CompilationUnitDeclaration) annotationNode.top().node, annotationNode, annotation);
+ handlers.handle((CompilationUnitDeclaration) annotationNode.top().get(), annotationNode, annotation);
annotationNode.setHandled();
}
@Override public void visitAnnotationOnMethod(AbstractMethodDeclaration method, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
- handlers.handle((CompilationUnitDeclaration) annotationNode.top().node, annotationNode, annotation);
+ handlers.handle((CompilationUnitDeclaration) annotationNode.top().get(), annotationNode, annotation);
annotationNode.setHandled();
}
@Override public void visitAnnotationOnType(TypeDeclaration type, Node annotationNode, Annotation annotation) {
if ( annotationNode.isHandled() ) return;
- handlers.handle((CompilationUnitDeclaration) annotationNode.top().node, annotationNode, annotation);
+ handlers.handle((CompilationUnitDeclaration) annotationNode.top().get(), annotationNode, annotation);
annotationNode.setHandled();
}
}
diff --git a/src/lombok/eclipse/handlers/HandleGetter_ecj.java b/src/lombok/eclipse/handlers/HandleGetter_ecj.java
index f9bbb884..cde71f83 100644
--- a/src/lombok/eclipse/handlers/HandleGetter_ecj.java
+++ b/src/lombok/eclipse/handlers/HandleGetter_ecj.java
@@ -4,6 +4,7 @@ import java.lang.reflect.Modifier;
import lombok.AccessLevel;
import lombok.Getter;
+import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseAST.Node;
@@ -28,14 +29,14 @@ public class HandleGetter_ecj implements EclipseAnnotationHandler<Getter> {
annotationNode.addWarning(String.format("Not generating %s(): A method with that name already exists", methodName));
}
- @Override public void handle(Getter annotation, Annotation ast, Node annotationNode) {
- if ( !(annotationNode.up().getEclipseNode() instanceof FieldDeclaration) ) return;
- FieldDeclaration field = (FieldDeclaration) annotationNode.up().getEclipseNode();
+ @Override public void handle(AnnotationValues<Getter> annotation, Annotation ast, Node annotationNode) {
+ if ( !(annotationNode.up().get() instanceof FieldDeclaration) ) return;
+ FieldDeclaration field = (FieldDeclaration) annotationNode.up().get();
TypeReference fieldType = field.type;
String getterName = TransformationsUtil.toGetterName(
new String(field.name), nameEquals(fieldType.getTypeName(), "boolean"));
- TypeDeclaration parent = (TypeDeclaration) annotationNode.up().up().getEclipseNode();
+ TypeDeclaration parent = (TypeDeclaration) annotationNode.up().up().get();
if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) {
if ( method.selector != null && new String(method.selector).equals(getterName) ) {
generateDuplicateGetterWarning(annotationNode, getterName);
@@ -44,7 +45,7 @@ public class HandleGetter_ecj implements EclipseAnnotationHandler<Getter> {
}
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
- method.modifiers = toModifier(annotation.value());
+ method.modifiers = toModifier(annotation.getInstance().value());
method.returnType = field.type;
method.annotations = null;
method.arguments = null;
diff --git a/src/lombok/javac/HandlerLibrary.java b/src/lombok/javac/HandlerLibrary.java
index 6f33003f..f8fd0d75 100644
--- a/src/lombok/javac/HandlerLibrary.java
+++ b/src/lombok/javac/HandlerLibrary.java
@@ -1,13 +1,9 @@
package lombok.javac;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -16,19 +12,28 @@ import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
+import lombok.core.AnnotationValues;
import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.core.TypeResolver;
+import lombok.core.AnnotationValues.AnnotationValue;
+import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
+
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCNewArray;
public class HandlerLibrary {
+ private final TypeLibrary typeLibrary = new TypeLibrary();
private final Map<String, AnnotationHandlerContainer<?>> annotationHandlers = new HashMap<String, AnnotationHandlerContainer<?>>();
// private final Collection<JavacASTVisitor> visitorHandlers = new ArrayList<JavacASTVisitor>();
@@ -41,9 +46,56 @@ public class HandlerLibrary {
this.annotationClass = annotationClass;
}
- @SuppressWarnings("unchecked")
- public void handle(JavacNode node, Object annInstance) {
- handler.handle(node, (T) annInstance);
+ private Object calculateGuess(JCExpression expr) {
+ if ( expr instanceof JCLiteral ) {
+ return ((JCLiteral)expr).value;
+ } else if ( expr instanceof JCIdent || expr instanceof JCFieldAccess ) {
+ String x = expr.toString();
+ if ( x.endsWith(".class") ) x = x.substring(0, x.length() - 6);
+ else {
+ int idx = x.lastIndexOf('.');
+ if ( idx > -1 ) x = x.substring(idx + 1);
+ }
+ return x;
+ } else return null;
+ }
+
+ public void handle(JavacAST.Node node) {
+ Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
+ JCAnnotation anno = (JCAnnotation) node.get();
+ List<JCExpression> arguments = anno.getArguments();
+ for ( Method m : annotationClass.getDeclaredMethods() ) {
+ if ( !Modifier.isPublic(m.getModifiers()) ) continue;
+ String name = m.getName();
+ List<String> raws = new ArrayList<String>();
+ List<Object> guesses = new ArrayList<Object>();
+
+ for ( JCExpression arg : arguments ) {
+ JCAssign assign = (JCAssign) arg;
+ String mName = assign.lhs.toString();
+ if ( !mName.equals(name) ) continue;
+ JCExpression rhs = assign.rhs;
+ if ( rhs instanceof JCNewArray ) {
+ List<JCExpression> elems = ((JCNewArray)rhs).elems;
+ for ( JCExpression inner : elems ) {
+ raws.add(inner.toString());
+ guesses.add(calculateGuess(inner));
+ }
+ } else {
+ raws.add(rhs.toString());
+ guesses.add(calculateGuess(rhs));
+ }
+ }
+
+ values.put(name, new AnnotationValue(node, raws, guesses) {
+ @Override public void setError(String message, int valueIdx) {
+ //TODO
+ super.setError(message, valueIdx);
+ }
+ });
+ }
+
+ handler.handle(new AnnotationValues<T>(annotationClass, values, node), (JCAnnotation)node.get(), node);
}
}
@@ -68,6 +120,7 @@ public class HandlerLibrary {
messager.printMessage(Diagnostic.Kind.WARNING,
"Duplicate handlers for annotation type: " + container.annotationClass.getName());
}
+ lib.typeLibrary.addType(container.annotationClass.getName());
} catch ( ServiceConfigurationError e ) {
messager.printMessage(Diagnostic.Kind.WARNING,
"Can't load Lombok annotation handler for javac: " + e);
@@ -75,144 +128,26 @@ public class HandlerLibrary {
}
}
- public void handleAnnotation(JavacNode node, TypeElement annotationType) {
- AnnotationHandlerContainer<?> container = annotationHandlers.get(annotationType.getQualifiedName().toString());
- if ( container == null ) return;
- try {
- container.handle(node, createAnnotation(container.annotationClass, annotationType.getQualifiedName(), node));
- } catch ( AnnotationValueDecodeFail e ) {
- node.addError(e.getMessage(), e.mirror, e.value);
- }
- }
-
- private Object createAnnotation(Class<? extends Annotation> target, Name annotationName, JavacNode node)
- throws AnnotationValueDecodeFail {
- AnnotationMirror mirror = fetchMirror(annotationName, node);
- if ( mirror == null ) throw new AssertionError("This can't be.");
-
- InvocationHandler invocations = new AnnotationMirrorInvocationHandler(target, mirror);
- return Proxy.newProxyInstance(target.getClassLoader(), new Class[] { target }, invocations);
- }
-
- private static class AnnotationValueDecodeFail extends Exception {
- private static final long serialVersionUID = 1L;
-
- AnnotationMirror mirror;
- AnnotationValue value;
-
- AnnotationValueDecodeFail(String msg, AnnotationMirror mirror, AnnotationValue value) {
- super(msg);
- this.mirror = mirror;
- this.value = value;
- }
- }
-
- private static class AnnotationMirrorInvocationHandler implements InvocationHandler {
- private final AnnotationMirror mirror;
- private final Map<String, Object> values = new HashMap<String, Object>();
-
- AnnotationMirrorInvocationHandler(Class<?> target, AnnotationMirror mirror) throws AnnotationValueDecodeFail {
- this.mirror = mirror;
-
- for ( Method m : target.getDeclaredMethods() ) {
- if ( !Modifier.isPublic(m.getModifiers()) ) continue;
- values.put(m.getName(), decode(m));
- }
- }
-
- private Object decode(Method m) throws AnnotationValueDecodeFail {
- for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
- mirror.getElementValues().entrySet() ) {
-
- if ( entry.getKey().getSimpleName().contentEquals(m.getName()) ) {
- AnnotationValue value = entry.getValue();
- return convert(m.getReturnType(), mirror, value, value.getValue());
- }
- }
+ public void handleAnnotation(JCCompilationUnit unit, JavacAST.Node node, JCAnnotation annotation) {
+ TypeResolver resolver = new TypeResolver(typeLibrary, node.getPackageDeclaration(), node.getImportStatements());
+ String rawType = annotation.annotationType.toString();
+ for ( String fqn : resolver.findTypeMatches(node, rawType) ) {
+ AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
+ if ( container == null ) continue;
- return m.getDefaultValue();
- }
-
- @Override public Object invoke(Object proxy, Method method, Object[] args) {
- return values.get(method.getName());
- }
-
- private Object convert(Class<?> expected, AnnotationMirror mirror, AnnotationValue value, Object v) throws AnnotationValueDecodeFail {
- if ( expected == int.class ) {
- if ( v instanceof Number ) return ((Number)v).intValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == long.class ) {
- if ( v instanceof Number ) return ((Number)v).longValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == short.class ) {
- if ( v instanceof Number ) return ((Number)v).shortValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == byte.class ) {
- if ( v instanceof Number ) return ((Number)v).byteValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == double.class ) {
- if ( v instanceof Number ) return ((Number)v).doubleValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == float.class ) {
- if ( v instanceof Number ) return ((Number)v).floatValue();
- else throw new AnnotationValueDecodeFail("Expected a numeric value here", mirror, value);
- } else if ( expected == char.class ) {
- if ( v instanceof Character ) return v;
- else throw new AnnotationValueDecodeFail("Expected a character here", mirror, value);
- } else if ( expected == boolean.class ) {
- if ( v instanceof Boolean ) return v;
- else throw new AnnotationValueDecodeFail("Expected a boolean here", mirror, value);
- } else if ( expected == String.class ) {
- if ( v instanceof String ) return v;
- else throw new AnnotationValueDecodeFail("Expected a String here", mirror, value);
- } else if ( expected == Class.class ) {
- if ( v instanceof TypeMirror ) {
- try {
- return Class.forName(v.toString());
- } catch ( ClassNotFoundException e ) {
- throw new AnnotationValueDecodeFail(
- "I can't find this class. Lombok only works well with types in the core java libraries.",
- mirror, value);
- }
- } else throw new AnnotationValueDecodeFail("Expected a class literal here", mirror, value);
- } else if ( Enum.class.isAssignableFrom(expected) ) {
- if ( v instanceof VariableElement ) {
- String n = ((VariableElement)v).getSimpleName().toString();
- @SuppressWarnings("unchecked")
- Object enumVal = Enum.valueOf((Class<? extends Enum>)expected, n);
- return enumVal;
- } else throw new AnnotationValueDecodeFail("Expected an enum value here", mirror, value);
- } else if ( expected.isArray() ) {
- if ( v instanceof Collection<?> ) {
- List<Object> convertedValues = new ArrayList<Object>();
- Class<?> componentType = expected.getComponentType();
- for ( Object innerV : (Collection<?>)v ) {
- convertedValues.add(convert(componentType, mirror, value, innerV));
- }
-
- Object array = Array.newInstance(componentType, convertedValues.size());
- int pos = 0;
- for ( Object converted : convertedValues ) Array.set(array, pos++, converted);
- return array;
- } else throw new AnnotationValueDecodeFail("Expected an array value here", mirror, value);
-// Collection<AnnotationValue> result = (Collection<AnnotationValue>)entry.getValue().getValue();
-// return result;
- } else {
- throw new AssertionError("We didn't know this is even a legal annotation type: " + expected);
+ try {
+ container.handle(node);
+ } catch ( AnnotationValueDecodeFail fail ) {
+ fail.owner.setError(fail.getMessage(), fail.idx);
+ } catch ( Throwable t ) {
+ t.printStackTrace();
+// Eclipse.error(String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
+ //TODO
}
}
}
- private AnnotationMirror fetchMirror(Name lookingFor, JavacNode node) {
- for ( AnnotationMirror mirror : node.getJavacAST().getAnnotationMirrors() ) {
- if ( !lookingFor.contentEquals(
- ((TypeElement)(mirror.getAnnotationType()).asElement()).getQualifiedName()) ) continue;
- return mirror;
- }
- return null;
- }
-
- public void handleType(TypeElement typeElement) {
+ public void handleAST(JavacAST ast) {
//Later!
}
diff --git a/src/lombok/javac/JavacAST.java b/src/lombok/javac/JavacAST.java
new file mode 100644
index 00000000..f56477fb
--- /dev/null
+++ b/src/lombok/javac/JavacAST.java
@@ -0,0 +1,329 @@
+package lombok.javac;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.processing.Messager;
+
+import lombok.core.AST;
+
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCImport;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.Name;
+
+public class JavacAST extends AST<JCTree> {
+ private final Trees trees;
+ private final JavacProcessingEnvironment env;
+ private final Messager messager;
+ private final Name.Table nameTable;
+ private final TreeMaker treeMaker;
+
+ public JavacAST(Trees trees, JavacProcessingEnvironment env, JCCompilationUnit top) {
+ super(top.sourcefile == null ? null : top.sourcefile.toString());
+ setTop(buildCompilationUnit(top));
+ this.trees = trees;
+ this.env = env;
+ this.messager = env.getMessager();
+ this.nameTable = Name.Table.instance(env.getContext());
+ this.treeMaker = TreeMaker.instance(env.getContext());
+ }
+
+ @Override public String getPackageDeclaration() {
+ JCCompilationUnit unit = (JCCompilationUnit)top().get();
+ return unit.pid instanceof JCFieldAccess ? unit.pid.toString() : null;
+ }
+
+ @Override public Collection<String> getImportStatements() {
+ List<String> imports = new ArrayList<String>();
+ JCCompilationUnit unit = (JCCompilationUnit)top().get();
+ for ( JCTree def : unit.defs ) {
+ if ( def instanceof JCImport ) {
+ imports.add(((JCImport)def).qualid.toString());
+ }
+ }
+
+ return imports;
+ }
+
+ public void traverse(JavacASTVisitor visitor) {
+ Node current = top();
+ visitor.visitCompilationUnit(current, (JCCompilationUnit)current.get());
+ traverseChildren(visitor, current);
+ visitor.endVisitCompilationUnit(current, (JCCompilationUnit)current.get());
+ }
+
+ private void traverseChildren(JavacASTVisitor visitor, Node node) {
+ for ( Node child : node.down() ) {
+ JCTree n = child.get();
+
+ switch ( child.getKind() ) {
+ case TYPE:
+ visitor.visitType(child, (JCClassDecl)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitType(child, (JCClassDecl)n);
+ break;
+ case FIELD:
+ visitor.visitField(child, (JCVariableDecl)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitField(child, (JCVariableDecl)n);
+ break;
+ case METHOD:
+ visitor.visitMethod(child, (JCMethodDecl)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitMethod(child, (JCMethodDecl)n);
+ break;
+ case INITIALIZER:
+ visitor.visitInitializer(child, (JCBlock)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitInitializer(child, (JCBlock)n);
+ break;
+ case ARGUMENT:
+ JCMethodDecl parent = (JCMethodDecl) child.up().get();
+ visitor.visitMethodArgument(child, (JCVariableDecl)n, parent);
+ traverseChildren(visitor, child);
+ visitor.endVisitMethodArgument(child, (JCVariableDecl)n, parent);
+ break;
+ case LOCAL:
+ visitor.visitLocal(child, (JCVariableDecl)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitLocal(child, (JCVariableDecl)n);
+ break;
+ case STATEMENT:
+ visitor.visitStatement(child, (JCTree)n);
+ traverseChildren(visitor, child);
+ visitor.endVisitStatement(node, (JCTree)n);
+ break;
+ case ANNOTATION:
+ switch ( child.up().getKind() ) {
+ case TYPE:
+ visitor.visitAnnotationOnType((JCClassDecl)child.up().get(), child, (JCAnnotation)n);
+ break;
+ case FIELD:
+ visitor.visitAnnotationOnField((JCVariableDecl)child.up().get(), child, (JCAnnotation)n);
+ break;
+ case METHOD:
+ visitor.visitAnnotationOnMethod((JCMethodDecl)child.up().get(), child, (JCAnnotation)n);
+ break;
+ case ARGUMENT:
+ JCVariableDecl argument = (JCVariableDecl)child.up().get();
+ JCMethodDecl method = (JCMethodDecl)child.up().up().get();
+ visitor.visitAnnotationOnMethodArgument(argument, method, child, (JCAnnotation)n);
+ break;
+ case LOCAL:
+ visitor.visitAnnotationOnLocal((JCVariableDecl)child.up().get(), child, (JCAnnotation)n);
+ break;
+ default:
+ throw new AssertionError("Can't be reached");
+ }
+ break;
+ default:
+ throw new AssertionError("Can't be reached: " + child.getKind());
+ }
+ }
+ }
+
+ @Override public Node top() {
+ return (Node) super.top();
+ }
+
+ @Override public Node get(JCTree astNode) {
+ return (Node) super.get(astNode);
+ }
+
+ public Name toName(String name) {
+ return nameTable.fromString(name);
+ }
+
+ public TreeMaker getTreeMaker() {
+ return treeMaker;
+ }
+
+ private Node buildCompilationUnit(JCCompilationUnit top) {
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( JCTree s : top.defs ) {
+ if ( s instanceof JCClassDecl ) {
+ addIfNotNull(childNodes, buildType((JCClassDecl)s));
+ } // else they are import statements, which we don't care about. Or Skip objects, whatever those are.
+ }
+
+ return new Node(top, childNodes, Kind.COMPILATION_UNIT);
+ }
+
+ private Node buildType(JCClassDecl type) {
+ if ( alreadyHandled(type) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+
+ for ( JCTree def : type.defs ) {
+ for ( JCAnnotation annotation : type.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation));
+ /* A def can be:
+ * JCClassDecl for inner types
+ * JCMethodDecl for constructors and methods
+ * JCVariableDecl for fields
+ * JCBlock for (static) initializers
+ */
+ if ( def instanceof JCMethodDecl ) addIfNotNull(childNodes, buildMethod((JCMethodDecl)def));
+ else if ( def instanceof JCClassDecl ) addIfNotNull(childNodes, buildType((JCClassDecl)def));
+ else if ( def instanceof JCVariableDecl ) addIfNotNull(childNodes, buildField((JCVariableDecl)def));
+ else if ( def instanceof JCBlock ) addIfNotNull(childNodes, buildInitializer((JCBlock)def));
+ }
+
+ return putInMap(new Node(type, childNodes, Kind.TYPE));
+ }
+
+ private Node buildField(JCVariableDecl field) {
+ if ( alreadyHandled(field) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( JCAnnotation annotation : field.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation));
+ addIfNotNull(childNodes, buildExpression(field.init));
+ return putInMap(new Node(field, childNodes, Kind.FIELD));
+ }
+
+ private Node buildLocalVar(JCVariableDecl local) {
+ if ( alreadyHandled(local) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( JCAnnotation annotation : local.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation));
+ addIfNotNull(childNodes, buildExpression(local.init));
+ return putInMap(new Node(local, childNodes, Kind.LOCAL));
+ }
+
+ private Node buildInitializer(JCBlock initializer) {
+ if ( alreadyHandled(initializer) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( JCStatement statement: initializer.stats ) addIfNotNull(childNodes, buildStatement(statement));
+ return putInMap(new Node(initializer, childNodes, Kind.INITIALIZER));
+ }
+
+ private Node buildMethod(JCMethodDecl method) {
+ if ( alreadyHandled(method) ) return null;
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( JCAnnotation annotation : method.mods.annotations ) addIfNotNull(childNodes, buildAnnotation(annotation));
+ for ( JCVariableDecl param : method.params ) addIfNotNull(childNodes, buildLocalVar(param));
+ if ( method.body != null && method.body.stats != null )
+ for ( JCStatement statement : method.body.stats ) addIfNotNull(childNodes, buildStatement(statement));
+ return putInMap(new Node(method, childNodes, Kind.METHOD));
+ }
+
+ private Node buildAnnotation(JCAnnotation annotation) {
+ if ( alreadyHandled(annotation) ) return null;
+ return putInMap(new Node(annotation, null, Kind.ANNOTATION));
+ }
+
+ private Node buildExpression(JCExpression expression) {
+ return buildStatementOrExpression(expression);
+ }
+
+ private Node buildStatement(JCStatement statement) {
+ return buildStatementOrExpression(statement);
+ }
+
+ private Node buildStatementOrExpression(JCTree statement) {
+ if ( statement == null || alreadyHandled(statement) ) return null;
+ if ( statement instanceof JCAnnotation ) return null;
+ if ( statement instanceof JCClassDecl ) return buildType((JCClassDecl)statement);
+ if ( statement instanceof JCVariableDecl ) return buildLocalVar((JCVariableDecl)statement);
+
+ //We drill down because LocalDeclarations and TypeDeclarations can occur anywhere, even in, say,
+ //an if block, or even the expression on an assert statement!
+
+ setAsHandled(statement);
+ return drill(statement);
+ }
+
+ private Node drill(JCTree statement) {
+ List<Node> childNodes = new ArrayList<Node>();
+ for ( FieldAccess fa : fieldsOf(statement.getClass()) ) childNodes.addAll(buildWithField(Node.class, statement, fa));
+ return putInMap(new Node(statement, childNodes, Kind.STATEMENT));
+ }
+
+ protected Collection<Class<? extends JCTree>> getStatementTypes() {
+ Collection<Class<? extends JCTree>> collection = new ArrayList<Class<? extends JCTree>>(2);
+ collection.add(JCStatement.class);
+ collection.add(JCExpression.class);
+ return collection;
+ }
+
+ private static void addIfNotNull(Collection<Node> nodes, Node node) {
+ if ( node != null ) nodes.add(node);
+ }
+
+ public class Node extends AST<JCTree>.Node {
+ public Node(JCTree node, Collection<Node> children, Kind kind) {
+ super(node, children, kind);
+ }
+
+ @Override public String getName() {
+ final Name n;
+
+ if ( node instanceof JCClassDecl ) n = ((JCClassDecl)node).name;
+ else if ( node instanceof JCMethodDecl ) n = ((JCMethodDecl)node).name;
+ else if ( node instanceof JCVariableDecl ) n = ((JCVariableDecl)node).name;
+ else n = null;
+
+ return n == null ? null : n.toString();
+ }
+
+ @Override protected boolean calculateIsStructurallySignificant() {
+ if ( node instanceof JCClassDecl ) return true;
+ if ( node instanceof JCMethodDecl ) return true;
+ if ( node instanceof JCVariableDecl ) return true;
+ if ( node instanceof JCCompilationUnit ) return true;
+ return false;
+ }
+
+ public TreeMaker getTreeMaker() {
+ return treeMaker;
+ }
+
+ public Name toName(String name) {
+ return JavacAST.this.toName(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Node directUp() {
+ return (Node) super.directUp();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Node up() {
+ return (Node) super.up();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Node top() {
+ return (Node) super.top();
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public Collection<Node> down() {
+ return (Collection<Node>) children;
+ }
+
+ public void addError(String message) {
+ System.err.println("ERR: " + message);
+ //TODO
+ }
+
+ public void addWarning(String message) {
+ System.err.println("WARN: " + message);
+ //TODO
+ }
+ }
+
+ @Override protected Node buildStatement(Object statement) {
+ return buildStatementOrExpression((JCTree) statement);
+ }
+}
diff --git a/src/lombok/javac/JavacASTAdapter.java b/src/lombok/javac/JavacASTAdapter.java
new file mode 100644
index 00000000..5d678f44
--- /dev/null
+++ b/src/lombok/javac/JavacASTAdapter.java
@@ -0,0 +1,35 @@
+package lombok.javac;
+
+import lombok.javac.JavacAST.Node;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+
+public class JavacASTAdapter implements JavacASTVisitor {
+ @Override public void visitCompilationUnit(Node top, JCCompilationUnit unit) {}
+ @Override public void endVisitCompilationUnit(Node top, JCCompilationUnit unit) {}
+ @Override public void visitType(Node typeNode, JCClassDecl type) {}
+ @Override public void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation) {}
+ @Override public void endVisitType(Node typeNode, JCClassDecl type) {}
+ @Override public void visitField(Node fieldNode, JCVariableDecl field) {}
+ @Override public void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation) {}
+ @Override public void endVisitField(Node fieldNode, JCVariableDecl field) {}
+ @Override public void visitInitializer(Node initializerNode, JCBlock initializer) {}
+ @Override public void endVisitInitializer(Node initializerNode, JCBlock initializer) {}
+ @Override public void visitMethod(Node methodNode, JCMethodDecl method) {}
+ @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {}
+ @Override public void endVisitMethod(Node methodNode, JCMethodDecl method) {}
+ @Override public void visitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method) {}
+ @Override public void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {}
+ @Override public void endVisitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method) {}
+ @Override public void visitLocal(Node localNode, JCVariableDecl local) {}
+ @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation) {}
+ @Override public void endVisitLocal(Node localNode, JCVariableDecl local) {}
+ @Override public void visitStatement(Node statementNode, JCTree statement) {}
+ @Override public void endVisitStatement(Node statementNode, JCTree statement) {}
+}
diff --git a/src/lombok/javac/JavacASTVisitor.java b/src/lombok/javac/JavacASTVisitor.java
new file mode 100644
index 00000000..daeb6b80
--- /dev/null
+++ b/src/lombok/javac/JavacASTVisitor.java
@@ -0,0 +1,182 @@
+package lombok.javac;
+
+import lombok.javac.JavacAST.Node;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+
+public interface JavacASTVisitor {
+ /**
+ * Called at the very beginning and end.
+ */
+ void visitCompilationUnit(Node top, JCCompilationUnit unit);
+ void endVisitCompilationUnit(Node top, JCCompilationUnit unit);
+
+ /**
+ * Called when visiting a type (a class, interface, annotation, enum, etcetera).
+ */
+ void visitType(Node typeNode, JCClassDecl type);
+ void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation);
+ void endVisitType(Node typeNode, JCClassDecl type);
+
+ /**
+ * Called when visiting a field of a class.
+ */
+ void visitField(Node fieldNode, JCVariableDecl field);
+ void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation);
+ void endVisitField(Node fieldNode, JCVariableDecl field);
+
+ /**
+ * Called for static and instance initializers. You can tell the difference via the isStatic() method.
+ */
+ void visitInitializer(Node initializerNode, JCBlock initializer);
+ void endVisitInitializer(Node initializerNode, JCBlock initializer);
+
+ /**
+ * Called for both methods and constructors.
+ */
+ void visitMethod(Node methodNode, JCMethodDecl method);
+ void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation);
+ void endVisitMethod(Node methodNode, JCMethodDecl method);
+
+ /**
+ * Visits a method argument.
+ */
+ void visitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method);
+ void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation);
+ void endVisitMethodArgument(Node argumentNode, JCVariableDecl argument, JCMethodDecl method);
+
+ /**
+ * Visits a local declaration - that is, something like 'int x = 10;' on the method level. Also called
+ * for method parameters.
+ */
+ void visitLocal(Node localNode, JCVariableDecl local);
+ void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation);
+ void endVisitLocal(Node localNode, JCVariableDecl local);
+
+ /**
+ * Visits a statement that isn't any of the other visit methods (e.g. JCClassDecl).
+ * The statement object is guaranteed to be either a JCStatement or a JCExpression.
+ */
+ void visitStatement(Node statementNode, JCTree statement);
+ void endVisitStatement(Node statementNode, JCTree statement);
+
+ public static class JavacASTPrinter implements JavacASTVisitor {
+ 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);
+ }
+
+ @Override public void visitCompilationUnit(Node Node, JCCompilationUnit unit) {
+ System.out.println("---------------------------------------------------------");
+
+ print("<CU %s>", Node.getFileName());
+ indent++;
+ }
+
+ @Override public void endVisitCompilationUnit(Node node, JCCompilationUnit unit) {
+ indent--;
+ print("</CUD>");
+ }
+
+ @Override public void visitType(Node node, JCClassDecl type) {
+ print("<TYPE %s>", type.name);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnType(JCClassDecl type, Node node, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitType(Node node, JCClassDecl type) {
+ indent--;
+ print("</TYPE %s>", type.name);
+ }
+
+ @Override public void visitInitializer(Node node, JCBlock initializer) {
+ print("<%s INITIALIZER>",
+ initializer.isStatic() ? "static" : "instance");
+ indent++;
+ }
+
+ @Override public void endVisitInitializer(Node node, JCBlock initializer) {
+ indent--;
+ print("</%s INITIALIZER>", initializer.isStatic() ? "static" : "instance");
+ }
+
+ @Override public void visitField(Node node, JCVariableDecl field) {
+ print("<FIELD %s %s>", field.type, field.name);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnField(JCVariableDecl field, Node node, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitField(Node node, JCVariableDecl field) {
+ indent--;
+ print("</FIELD %s %s>", field.type, field.name);
+ }
+
+ @Override public void visitMethod(Node node, JCMethodDecl method) {
+ String type = method.name.contentEquals("<init>") ? "CONSTRUCTOR" : "METHOD";
+ print("<%s %s>", type, method.name);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node node, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitMethod(Node node, JCMethodDecl method) {
+ String type = method.name.contentEquals("<init>") ? "CONSTRUCTOR" : "METHOD";
+ indent--;
+ print("</%s %s>", type, method.name);
+ }
+
+ @Override public void visitMethodArgument(Node node, JCVariableDecl arg, JCMethodDecl method) {
+ print("<METHOARG %s %s = %s>", arg.type, arg.name);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnMethodArgument(JCVariableDecl arg, JCMethodDecl method, Node nodeAnnotation, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitMethodArgument(Node node, JCVariableDecl arg, JCMethodDecl method) {
+ indent--;
+ print("</METHODARG %s %s>", arg.type, arg.name);
+ }
+
+ @Override public void visitLocal(Node node, JCVariableDecl local) {
+ print("<LOCAL %s %s>", local.type, local.name);
+ indent++;
+ }
+
+ @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node node, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitLocal(Node node, JCVariableDecl local) {
+ indent--;
+ print("</LOCAL %s %s>", local.type, local.name);
+ }
+
+ @Override public void visitStatement(Node node, JCTree statement) {
+ print("<%s>", statement.getClass());
+ indent++;
+ }
+
+ @Override public void endVisitStatement(Node node, JCTree statement) {
+ indent--;
+ print("</%s>", statement.getClass());
+ }
+ }
+}
diff --git a/src/lombok/javac/JavacAnnotationHandler.java b/src/lombok/javac/JavacAnnotationHandler.java
index 67542a12..ce3c5fd7 100644
--- a/src/lombok/javac/JavacAnnotationHandler.java
+++ b/src/lombok/javac/JavacAnnotationHandler.java
@@ -2,6 +2,10 @@ package lombok.javac;
import java.lang.annotation.Annotation;
+import lombok.core.AnnotationValues;
+
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+
public interface JavacAnnotationHandler<T extends Annotation> {
- void handle(JavacNode annotedElement, T annotation);
+ void handle(AnnotationValues<T> annotation, JCAnnotation ast, JavacAST.Node annotedElement);
}
diff --git a/src/lombok/javac/JavacNode.java b/src/lombok/javac/JavacNode.java
deleted file mode 100644
index 2e65e1d1..00000000
--- a/src/lombok/javac/JavacNode.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package lombok.javac;
-
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-import com.sun.source.util.Trees;
-import com.sun.tools.javac.processing.JavacProcessingEnvironment;
-import com.sun.tools.javac.tree.TreeMaker;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
-import com.sun.tools.javac.util.Name;
-
-public class JavacNode {
- private final Element node;
- private final Messager messager;
- private final JavacProcessingEnvironment env;
- private final Trees trees;
-
- public JavacNode(Trees trees, JavacProcessingEnvironment env, Element node) {
- this.trees = trees;
- this.env = env;
- this.node = node;
- this.messager = env.getMessager();
- }
-
- public Element getJavacAST() {
- return node;
- }
-
- public JCClassDecl getEnclosingType() {
- Element parent = node;
- while ( !(parent instanceof TypeElement) ) parent = node.getEnclosingElement();
- TypeElement classElement = (TypeElement)parent;
- return (JCClassDecl)trees.getTree(classElement);
- }
-
- public Name.Table createNameTable() {
- return Name.Table.instance(env.getContext());
- }
-
- public TreeMaker createTreeMaker() {
- return TreeMaker.instance(env.getContext());
- }
-
- public void addError(String message) {
- this.messager.printMessage(Diagnostic.Kind.ERROR, message, node);
- }
-
- public void addError(String message, AnnotationMirror mirror, AnnotationValue value) {
- this.messager.printMessage(Diagnostic.Kind.ERROR, message, node, mirror, value);
- }
-
- public void addWarning(String message) {
- this.messager.printMessage(Diagnostic.Kind.WARNING, message, node);
- }
-
- public void addWarning(String message, AnnotationMirror mirror, AnnotationValue value) {
- this.messager.printMessage(Diagnostic.Kind.WARNING, message, node, mirror, value);
- }
-}
diff --git a/src/lombok/javac/apt/Processor.java b/src/lombok/javac/apt/Processor.java
index 864ef52c..2780ce16 100644
--- a/src/lombok/javac/apt/Processor.java
+++ b/src/lombok/javac/apt/Processor.java
@@ -1,5 +1,8 @@
package lombok.javac.apt;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
@@ -12,10 +15,17 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import lombok.javac.HandlerLibrary;
-import lombok.javac.JavacNode;
+import lombok.javac.JavacAST;
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacAST.Node;
import com.sun.source.util.Trees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@SupportedAnnotationTypes("*")
@@ -39,23 +49,53 @@ public class Processor extends AbstractProcessor {
if ( processingEnv == null ) return false;
- for ( TypeElement annotationType : annotations ) {
- if ( !handlers.hasHandlerFor(annotationType) ) continue;
- for ( Element element : roundEnv.getElementsAnnotatedWith(annotationType) ) {
- handlers.handleAnnotation(createNode(element), annotationType);
- }
+ IdentityHashMap<JCCompilationUnit, Void> units = new IdentityHashMap<JCCompilationUnit, Void>();
+ for ( Element element : roundEnv.getRootElements() ) units.put(toUnit(element), null);
+
+ List<JavacAST> asts = new ArrayList<JavacAST>();
+
+ for ( JCCompilationUnit unit : units.keySet() ) asts.add(new JavacAST(trees, processingEnv, unit));
+
+ for ( JavacAST ast : asts ) {
+ ast.traverse(new AnnotationVisitor());
+ handlers.handleAST(ast);
+ }
+ return false;
+ }
+
+ private class AnnotationVisitor extends JavacASTAdapter {
+ @Override public void visitAnnotationOnType(JCClassDecl type, Node annotationNode, JCAnnotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
}
- for ( Element element : roundEnv.getRootElements() ) {
- if ( element instanceof TypeElement ) {
- handlers.handleType((TypeElement)element);
- }
+ @Override public void visitAnnotationOnField(JCVariableDecl field, Node annotationNode, JCAnnotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
}
- return false;
+ @Override public void visitAnnotationOnMethod(JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
+ }
+
+ @Override public void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, Node annotationNode, JCAnnotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
+ }
+
+ @Override public void visitAnnotationOnLocal(JCVariableDecl local, Node annotationNode, JCAnnotation annotation) {
+ if ( annotationNode.isHandled() ) return;
+ handlers.handleAnnotation((JCCompilationUnit) annotationNode.top().get(), annotationNode, annotation);
+ annotationNode.setHandled();
+ }
}
- private JavacNode createNode(Element element) {
- return new JavacNode(trees, processingEnv, element);
+ private JCCompilationUnit toUnit(Element element) {
+ return (JCCompilationUnit) trees.getPath(element).getCompilationUnit();
}
}
diff --git a/src/lombok/javac/handlers/HandleGetter_javac.java b/src/lombok/javac/handlers/HandleGetter_javac.java
index 7863a097..8501b65f 100644
--- a/src/lombok/javac/handlers/HandleGetter_javac.java
+++ b/src/lombok/javac/handlers/HandleGetter_javac.java
@@ -2,17 +2,15 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.PKG.*;
-import javax.lang.model.element.Element;
-
import lombok.Getter;
+import lombok.core.AnnotationValues;
+import lombok.core.AST.Kind;
import lombok.javac.JavacAnnotationHandler;
-import lombok.javac.JavacNode;
+import lombok.javac.JavacAST;
import org.mangosdk.spi.ProviderFor;
import com.sun.source.tree.MethodTree;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
@@ -27,26 +25,29 @@ import com.sun.tools.javac.util.Name;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleGetter_javac implements JavacAnnotationHandler<Getter> {
- @Override public void handle(JavacNode node, Getter getter) {
- if ( !node.getJavacAST().getKind().isField() ) {
+ @Override public void handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacAST.Node node) {
+ if ( node.up().getKind() != Kind.FIELD ) {
node.addError("@Getter is only supported on a field.");
return;
}
- JCClassDecl javacClassTree = node.getEnclosingType();
+ Getter getter = annotation.getInstance();
+
+ JCClassDecl javacClassTree = (JCClassDecl) node.up().up().get();
int access = toJavacModifier(getter.value());
- MethodTree getterMethod = createGetter(access, node.getJavacAST(), node.createTreeMaker(), node.createNameTable());
+ MethodTree getterMethod = createGetter(access, node.up(), node.getTreeMaker());
javacClassTree.defs = javacClassTree.defs.append((JCTree)getterMethod);
}
- private MethodTree createGetter(int access, Element field, TreeMaker treeMaker, Name.Table nameTable) {
- JCStatement returnStatement = treeMaker.Return(treeMaker.Ident((Symbol)field));
+ private MethodTree createGetter(int access, JavacAST.Node field, TreeMaker treeMaker) {
+ JCVariableDecl fieldNode = (JCVariableDecl) field.get();
+ JCStatement returnStatement = treeMaker.Return(treeMaker.Ident(fieldNode.getName()));
JCBlock methodBody = treeMaker.Block(0, List.of(returnStatement));
- Name methodName = Name.fromString(nameTable, toGetterName(field));
- JCExpression methodType = treeMaker.Type((Type)field.asType());
+ Name methodName = field.toName(toGetterName((JCVariableDecl)field.get()));
+ JCExpression methodType = fieldNode.type != null ? treeMaker.Type(fieldNode.type) : fieldNode.vartype;
List<JCTypeParameter> methodGenericParams = List.nil();
List<JCVariableDecl> parameters = List.nil();
diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java
index 4622c3ee..9ba3765a 100644
--- a/src/lombok/javac/handlers/PKG.java
+++ b/src/lombok/javac/handlers/PKG.java
@@ -2,17 +2,16 @@ package lombok.javac.handlers;
import java.lang.reflect.Modifier;
-import javax.lang.model.element.Element;
-import javax.lang.model.type.TypeKind;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import lombok.AccessLevel;
import lombok.core.TransformationsUtil;
class PKG {
- static String toGetterName(Element field) {
- CharSequence fieldName = field.getSimpleName();
+ static String toGetterName(JCVariableDecl field) {
+ CharSequence fieldName = field.name;
- boolean isBoolean = field.asType().getKind() == TypeKind.BOOLEAN;
+ boolean isBoolean = field.vartype.toString().equals("boolean");
return TransformationsUtil.toGetterName(fieldName, isBoolean);
}