aboutsummaryrefslogtreecommitdiff
path: root/src/lombok/javac
diff options
context:
space:
mode:
Diffstat (limited to 'src/lombok/javac')
-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
9 files changed, 703 insertions, 241 deletions
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);
}