aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@tipit.to>2009-06-16 03:04:46 +0200
committerReinier Zwitserloot <reinier@tipit.to>2009-06-16 03:04:46 +0200
commitf36be2eb01bcbff01a513d64bff2d1aba54460b1 (patch)
tree7d49ebe90cb836e054c35d2a4382be6b999ca452 /src
parentd78a04d74886101c81de77659b067d16cb2d0de2 (diff)
downloadlombok-f36be2eb01bcbff01a513d64bff2d1aba54460b1.tar.gz
lombok-f36be2eb01bcbff01a513d64bff2d1aba54460b1.tar.bz2
lombok-f36be2eb01bcbff01a513d64bff2d1aba54460b1.zip
Implemented a lot of stuff for javac, but we ran into 2 major issues still to be implemented:
1. The visit mode of a lombok handler (does not trigger off of annotations, instead sees every field, method, type, and statement), needs to be coded, 2. triggering off of annotations via APT's annotation handling system skips method-local classes. We'll need to recode this via an AST visitor like we need for issue #1 Other than that, triggering off of annotations works swimmingly!
Diffstat (limited to 'src')
-rw-r--r--src/lombok/apt/AnnotationTransponder.java111
-rw-r--r--src/lombok/apt/HandleANY_ecj.java17
-rw-r--r--src/lombok/apt/HandleGetter_javac.java77
-rw-r--r--src/lombok/apt/HandlerForCompiler.java16
-rw-r--r--src/lombok/apt/Processor.java35
-rw-r--r--src/lombok/core/SpiLoadUtil.java37
-rw-r--r--src/lombok/eclipse/HandlerLibrary.java66
-rw-r--r--src/lombok/javac/HandlerLibrary.java222
-rw-r--r--src/lombok/javac/JavacAnnotationHandler.java7
-rw-r--r--src/lombok/javac/JavacNode.java63
-rw-r--r--src/lombok/javac/apt/PKG.java (renamed from src/lombok/apt/PKG.java)16
-rw-r--r--src/lombok/javac/apt/Processor.java62
-rw-r--r--src/lombok/javac/handlers/HandleGetter_javac.java60
-rw-r--r--src/lombok/javac/handlers/PKG.java34
14 files changed, 505 insertions, 318 deletions
diff --git a/src/lombok/apt/AnnotationTransponder.java b/src/lombok/apt/AnnotationTransponder.java
deleted file mode 100644
index 01a79c12..00000000
--- a/src/lombok/apt/AnnotationTransponder.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package lombok.apt;
-
-import static lombok.apt.PKG.CURRENT_SUPPORT;
-import static lombok.apt.PKG.isInstanceOf;
-import static lombok.apt.PKG.readResource;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-import javax.tools.Diagnostic;
-
-/**
- * Responsible for redirecting the need to handle an annotation to a class that knows how to handle a given annotation type in a given compiler environment.
- * Will dynamically locate a class in this package using the naming pattern: "HandleFoo_compilerType", e.g. "HandleGetter_ecj".
- * Responsible for injecting the proper class into the right classloader so that it has open access to the classes required to inspect the live AST and
- * modify it so that annotations can cause changes to the live AST.
- *
- * @author rzwitserloot
- *
- * @param <T> The annotation class that this transponder should handle (example: Getter.class).
- */
-public class AnnotationTransponder<T extends Annotation> {
- private HandlerForCompiler<T> impl;
- private final ProcessingEnvironment processEnv;
- private final RoundEnvironment roundEnv;
- private String error;
-
- @SuppressWarnings("unchecked")
- private void createInstance(Class<T> annotation, ClassLoader loader, String compilerType) {
- try {
- if ( loader == null ) loader = AnnotationTransponder.class.getClassLoader();
- Class<?> implClass;
- try {
- implClass = loader.loadClass(String.format(
- "org.javanext.apt.Handle%s_%s", annotation.getSimpleName(), compilerType));
- } catch ( ClassNotFoundException e ) {
- implClass = loader.loadClass(String.format("lombok.apt.HandleANY_%s", compilerType));
- }
-
- Constructor<?> constructor;
-
- constructor = implClass.getDeclaredConstructor();
- constructor.setAccessible(true);
- impl = (HandlerForCompiler<T>)constructor.newInstance();
- impl.processEnv = processEnv;
- impl.roundEnv = roundEnv;
- try {
- impl.init();
- } catch ( Exception e ) {
- error = "Exception initializing handler: " + e;
- impl = null;
- }
- } catch ( Exception e ) {
- e.printStackTrace();
- error = "You are using " + compilerType + " but a version that's changed the compiler internals. I can't work with it.";
- }
- }
-
- public AnnotationTransponder(Class<T> annotation, RoundEnvironment roundEnv, ProcessingEnvironment processEnv) {
- this.processEnv = processEnv;
- this.roundEnv = roundEnv;
- if ( isInstanceOf(processEnv, "com.sun.tools.javac.processing.JavacProcessingEnvironment") ) {
- createInstance(annotation, null, "javac");
- } else if ( isInstanceOf(processEnv, "org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeBuildProcessingEnvImpl") ) {
- final ClassLoader[] parentLoaders =
- new ClassLoader[] { processEnv.getClass().getClassLoader(), AnnotationTransponder.class.getClassLoader() };
-
- ClassLoader loader = new ClassLoader() {
- @Override public Class<?> findClass(String name) throws ClassNotFoundException {
- if ( name.equals(HandlerForCompiler.class.getName()) ) return HandlerForCompiler.class;
- if ( name.startsWith(AnnotationTransponder.class.getPackage().getName()) ) {
- byte[] data = readResource(name.replace(".", "/") + ".class");
- return defineClass(name, data, 0, data.length);
- }
- for ( int i = 0 ; i < parentLoaders.length ; i++ ) {
- try {
- return parentLoaders[i].loadClass(name);
- } catch ( ClassNotFoundException e ) {
- if ( i == parentLoaders.length -1 ) throw e;
- }
- }
-
- return null;
- }
- };
-
- createInstance(annotation, loader, "ecj");
- } else {
- impl = null;
- this.error = "I cannot work with your compiler. I currently only support " + CURRENT_SUPPORT + ".\n" +
- "This is a: " + processEnv.getClass();
- }
- }
-
-
- public void handle(Element element, T annotation) {
- if ( impl == null ) {
- processEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, error, element);
- } else {
- try {
- impl.handle(element, annotation);
- } catch ( Exception e ) {
- e.printStackTrace();
- processEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Exception in JavaNext: " + e, element);
- }
- }
- }
-}
diff --git a/src/lombok/apt/HandleANY_ecj.java b/src/lombok/apt/HandleANY_ecj.java
deleted file mode 100644
index 587444d9..00000000
--- a/src/lombok/apt/HandleANY_ecj.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package lombok.apt;
-
-import java.lang.annotation.Annotation;
-
-import javax.lang.model.element.Element;
-import javax.tools.Diagnostic;
-
-public class HandleANY_ecj extends HandlerForCompiler<Annotation> {
- @Override public void handle(Element element, Annotation annotation) throws Exception {
- //TODO: We should find eclipse's eclipse.ini file and patch us in as a javaagent and bootclasspath/a.
- //Though, we should probably use reflection to find eclipse's SWT system and generate a popup dialog for
- //confirmation.
-
- String msg = "You'll need to install the eclipse patch. See http://lombok.github.org/ for more info.";
- processEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg, element);
- }
-}
diff --git a/src/lombok/apt/HandleGetter_javac.java b/src/lombok/apt/HandleGetter_javac.java
deleted file mode 100644
index bfa2746d..00000000
--- a/src/lombok/apt/HandleGetter_javac.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package lombok.apt;
-
-import java.lang.reflect.Modifier;
-
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-import lombok.Getter;
-
-import com.sun.source.tree.MethodTree;
-import com.sun.source.util.Trees;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.code.Type;
-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.JCExpression;
-import com.sun.tools.javac.tree.JCTree.JCStatement;
-import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
-import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.Name;
-
-class HandleGetter_javac extends HandlerForCompiler<Getter> {
- private final Trees trees;
- private final Messager messager;
- private JavacProcessingEnvironment env;
-
- HandleGetter_javac() {
- this.messager = processEnv.getMessager();
- this.trees = Trees.instance(processEnv);
- }
-
- @Override public void init() {
- this.env = (JavacProcessingEnvironment)processEnv;
- }
-
- @Override public void handle(Element element, Getter getter) {
- if ( !element.getKind().isField() ) {
- messager.printMessage(Diagnostic.Kind.ERROR, "@Getter is only supported on a field.");
- return;
- }
-
- TypeElement classElement = (TypeElement)element.getEnclosingElement();
- JCClassDecl javacClassTree = (JCClassDecl)trees.getTree(classElement);
-
- Name.Table nameTable = Name.Table.instance(env.getContext());
- TreeMaker treeMaker = TreeMaker.instance(env.getContext());
-
- MethodTree getterMethod = createGetter(element, treeMaker, nameTable);
- javacClassTree.defs = javacClassTree.defs.append((JCTree)getterMethod);
- }
-
- private MethodTree createGetter(Element field, TreeMaker treeMaker, Name.Table nameTable) {
- JCStatement returnStatement = treeMaker.Return(treeMaker.Ident((Symbol)field));
-
- //TODO Trab the position in the source file of the field by looking it up in the JCClassDecl,
- //and copy it into the 'position' info for the Ident and Return AST Nodes.
-
- JCBlock methodBody = treeMaker.Block(0, List.of(returnStatement));
- Name methodName = Name.fromString(nameTable, PKG.toGetterName(field));
- JCExpression methodType = treeMaker.Type((Type)field.asType());
-
- List<JCTypeParameter> methodGenericParams = List.nil();
- List<JCVariableDecl> parameters = List.nil();
- List<JCExpression> throwsClauses = List.nil();
- JCExpression annotationMethodDefaultValue = null;
-
- return treeMaker.MethodDef(treeMaker.Modifiers(Modifier.PUBLIC, List.<JCAnnotation>nil()), methodName, methodType,
- methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue);
- }
-}
diff --git a/src/lombok/apt/HandlerForCompiler.java b/src/lombok/apt/HandlerForCompiler.java
deleted file mode 100644
index 7eb29385..00000000
--- a/src/lombok/apt/HandlerForCompiler.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package lombok.apt;
-
-import java.lang.annotation.Annotation;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-
-public abstract class HandlerForCompiler<T extends Annotation> {
- protected ProcessingEnvironment processEnv;
- protected RoundEnvironment roundEnv;
-
- public void init() throws Exception {}
-
- public abstract void handle(Element element, T annotation) throws Exception;
-}
diff --git a/src/lombok/apt/Processor.java b/src/lombok/apt/Processor.java
deleted file mode 100644
index d63ec006..00000000
--- a/src/lombok/apt/Processor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package lombok.apt;
-
-import java.lang.annotation.Annotation;
-import java.util.Set;
-
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-import lombok.Getter;
-
-
-@SupportedAnnotationTypes("lombok.*")
-@SupportedSourceVersion(SourceVersion.RELEASE_6)
-public class Processor extends AbstractProcessor {
- @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- for ( TypeElement typeElement : annotations ) {
- if ( typeElement.getQualifiedName().contentEquals(Getter.class.getName()) )
- return handle(roundEnv, Getter.class, typeElement);
- }
-
- return false;
- }
-
- private <T extends Annotation> boolean handle(RoundEnvironment roundEnv, Class<T> annotation, TypeElement typeElement) {
- for ( Element element : roundEnv.getElementsAnnotatedWith(typeElement) ) {
- new AnnotationTransponder<T>(annotation, roundEnv, processingEnv).handle(element, element.getAnnotation(annotation));
- }
- return true;
- }
-}
diff --git a/src/lombok/core/SpiLoadUtil.java b/src/lombok/core/SpiLoadUtil.java
new file mode 100644
index 00000000..bf4bddf4
--- /dev/null
+++ b/src/lombok/core/SpiLoadUtil.java
@@ -0,0 +1,37 @@
+package lombok.core;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public class SpiLoadUtil {
+ private SpiLoadUtil() {}
+
+ @SuppressWarnings("unchecked")
+ public static Class<? extends Annotation> findAnnotationClass(Class<?> c, Class<?> base) {
+ if ( c == Object.class || c == null ) return null;
+ for ( Type iface : c.getGenericInterfaces() ) {
+ if ( iface instanceof ParameterizedType ) {
+ ParameterizedType p = (ParameterizedType)iface;
+ if ( !base.equals(p.getRawType()) ) continue;
+ Type target = p.getActualTypeArguments()[0];
+ if ( target instanceof Class<?> ) {
+ if ( Annotation.class.isAssignableFrom((Class<?>) target) ) {
+ return (Class<? extends Annotation>) target;
+ }
+ }
+
+ throw new ClassCastException("Not an annotation type: " + target);
+ }
+ }
+
+ Class<? extends Annotation> potential = findAnnotationClass(c.getSuperclass(), base);
+ if ( potential != null ) return potential;
+ for ( Class<?> iface : c.getInterfaces() ) {
+ potential = findAnnotationClass(iface, base);
+ if ( potential != null ) return potential;
+ }
+
+ return null;
+ }
+}
diff --git a/src/lombok/eclipse/HandlerLibrary.java b/src/lombok/eclipse/HandlerLibrary.java
index f458cf45..8a7f6edc 100644
--- a/src/lombok/eclipse/HandlerLibrary.java
+++ b/src/lombok/eclipse/HandlerLibrary.java
@@ -4,9 +4,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -15,6 +13,7 @@ import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
+import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
import lombok.eclipse.EclipseAST.Node;
@@ -60,7 +59,7 @@ public class HandlerLibrary {
@SuppressWarnings("unchecked")
public <A extends Annotation> A createAnnotation(Class<A> target,
CompilationUnitDeclaration ast,
- org.eclipse.jdt.internal.compiler.ast.Annotation node) throws EnumDecodeFail {
+ org.eclipse.jdt.internal.compiler.ast.Annotation node) throws AnnotationValueDecodeFail {
final Map<String, Object> values = new HashMap<String, Object>();
final MemberValuePair[] pairs = node.memberValuePairs();
@@ -87,12 +86,12 @@ public class HandlerLibrary {
}
private Object calculateValue(MemberValuePair pair,
- CompilationUnitDeclaration ast, Class<?> type, Expression e) throws EnumDecodeFail {
+ CompilationUnitDeclaration ast, Class<?> type, Expression e) throws AnnotationValueDecodeFail {
if ( e instanceof Literal ) {
((Literal)e).computeConstant();
return convertConstant(pair, type, e.constant);
} else if ( e instanceof ArrayInitializer ) {
- if ( !type.isArray() ) throw new EnumDecodeFail(pair, "Did not expect an array here.");
+ if ( !type.isArray() ) throw new AnnotationValueDecodeFail(pair, "Did not expect an array here.");
Class<?> component = type.getComponentType();
Expression[] expressions = ((ArrayInitializer)e).expressions;
@@ -104,19 +103,19 @@ public class HandlerLibrary {
return values;
} else if ( e instanceof ClassLiteralAccess ) {
if ( type == Class.class ) return toClass(pair, ast, str(((ClassLiteralAccess)e).type.getTypeName()));
- else throw new EnumDecodeFail(pair, "Expected a " + type + " literal.");
+ 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 EnumDecodeFail(pair, "Lombok annotations must contain literals only.");
+ throw new AnnotationValueDecodeFail(pair, "Lombok annotations must contain literals only.");
} else {
- throw new EnumDecodeFail(pair, "Lombok could not decode this annotation parameter.");
+ throw new AnnotationValueDecodeFail(pair, "Lombok could not decode this annotation parameter.");
}
}
- private Enum<?> toEnum(MemberValuePair pair, Class<?> enumType, String ref) throws EnumDecodeFail {
+ 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();
@@ -124,10 +123,10 @@ public class HandlerLibrary {
String target = ((Enum<?>)constant).name();
if ( target.equals(ref) ) return (Enum<?>) constant;
}
- throw new EnumDecodeFail(pair, "I can't figure out which enum constant you mean.");
+ throw new AnnotationValueDecodeFail(pair, "I can't figure out which enum constant you mean.");
}
- private Class<?> toClass(MemberValuePair pair, CompilationUnitDeclaration ast, String typeName) throws EnumDecodeFail {
+ private Class<?> toClass(MemberValuePair pair, CompilationUnitDeclaration ast, String typeName) throws AnnotationValueDecodeFail {
Class<?> c;
boolean fqn = typeName.indexOf('.') > -1;
@@ -170,7 +169,7 @@ public class HandlerLibrary {
}
}
- throw new EnumDecodeFail(pair, "I can't find this class. Try using the fully qualified name.");
+ throw new AnnotationValueDecodeFail(pair, "I can't find this class. Try using the fully qualified name.");
}
private Class<?> tryClass(String name) {
@@ -181,7 +180,7 @@ public class HandlerLibrary {
}
}
- private Object convertConstant(MemberValuePair pair, Class<?> type, Constant constant) throws EnumDecodeFail {
+ private Object convertConstant(MemberValuePair pair, Class<?> type, Constant constant) throws AnnotationValueDecodeFail {
int targetTypeID;
boolean array = type.isArray();
if ( array ) type = type.getComponentType();
@@ -197,10 +196,10 @@ public class HandlerLibrary {
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 EnumDecodeFail(pair, "Expected a constant of some sort here (a number or a string)");
+ 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 EnumDecodeFail(pair, "I can't turn this literal into a " + type);
+ throw new AnnotationValueDecodeFail(pair, "I can't turn this literal into a " + type);
}
Object o = null;
@@ -224,12 +223,12 @@ public class HandlerLibrary {
return o;
}
- private static class EnumDecodeFail extends Exception {
+ private static class AnnotationValueDecodeFail extends Exception {
private static final long serialVersionUID = 1L;
MemberValuePair pair;
- EnumDecodeFail(MemberValuePair pair, String msg) {
+ AnnotationValueDecodeFail(MemberValuePair pair, String msg) {
super(msg);
this.pair = pair;
}
@@ -259,7 +258,8 @@ public class HandlerLibrary {
while ( it.hasNext() ) {
try {
EclipseAnnotationHandler<?> handler = it.next();
- Class<? extends Annotation> annotationClass = lib.findAnnotationClass(handler.getClass());
+ Class<? extends Annotation> annotationClass =
+ SpiLoadUtil.findAnnotationClass(handler.getClass(), EclipseAnnotationHandler.class);
AnnotationHandlerContainer<?> container = new AnnotationHandlerContainer(handler, annotationClass);
if ( lib.annotationHandlers.put(container.annotationClass.getName(), container) != null ) {
Eclipse.error("Duplicate handlers for annotation type: " + container.annotationClass.getName());
@@ -282,34 +282,6 @@ public class HandlerLibrary {
}
}
- @SuppressWarnings("unchecked")
- private Class<? extends Annotation> findAnnotationClass(Class<?> c) {
- if ( c == Object.class || c == null ) return null;
- for ( Type iface : c.getGenericInterfaces() ) {
- if ( iface instanceof ParameterizedType ) {
- ParameterizedType p = (ParameterizedType)iface;
- if ( !EclipseAnnotationHandler.class.equals(p.getRawType()) ) continue;
- Type target = p.getActualTypeArguments()[0];
- if ( target instanceof Class<?> ) {
- if ( Annotation.class.isAssignableFrom((Class<?>) target) ) {
- return (Class<? extends Annotation>) target;
- }
- }
-
- throw new ClassCastException("Not an annotation type: " + target);
- }
- }
-
- Class<? extends Annotation> potential = findAnnotationClass(c.getSuperclass());
- if ( potential != null ) return potential;
- for ( Class<?> iface : c.getInterfaces() ) {
- potential = findAnnotationClass(iface);
- if ( potential != null ) return potential;
- }
-
- return null;
- }
-
public void handle(CompilationUnitDeclaration ast, EclipseAST.Node annotationNode,
org.eclipse.jdt.internal.compiler.ast.Annotation annotation) {
TypeResolver resolver = new TypeResolver(typeLibrary, annotationNode.top());
@@ -321,7 +293,7 @@ public class HandlerLibrary {
Object annInstance;
try {
annInstance = createAnnotation(container.annotationClass, ast, annotation);
- } catch ( EnumDecodeFail e ) {
+ } catch ( AnnotationValueDecodeFail e ) {
annotationNode.addError(e.getMessage(), e.pair.sourceStart, e.pair.sourceEnd);
return;
}
diff --git a/src/lombok/javac/HandlerLibrary.java b/src/lombok/javac/HandlerLibrary.java
new file mode 100644
index 00000000..6f33003f
--- /dev/null
+++ b/src/lombok/javac/HandlerLibrary.java
@@ -0,0 +1,222 @@
+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;
+import java.util.Map;
+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.SpiLoadUtil;
+
+
+public class HandlerLibrary {
+ private final Map<String, AnnotationHandlerContainer<?>> annotationHandlers = new HashMap<String, AnnotationHandlerContainer<?>>();
+// private final Collection<JavacASTVisitor> visitorHandlers = new ArrayList<JavacASTVisitor>();
+
+ private static class AnnotationHandlerContainer<T extends Annotation> {
+ private JavacAnnotationHandler<T> handler;
+ private Class<T> annotationClass;
+
+ AnnotationHandlerContainer(JavacAnnotationHandler<T> handler, Class<T> annotationClass) {
+ this.handler = handler;
+ this.annotationClass = annotationClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void handle(JavacNode node, Object annInstance) {
+ handler.handle(node, (T) annInstance);
+ }
+ }
+
+ public static HandlerLibrary load(Messager messager) {
+ HandlerLibrary library = new HandlerLibrary();
+ loadAnnotationHandlers(messager, library);
+ return library;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void loadAnnotationHandlers(Messager messager, HandlerLibrary lib) {
+ //No, that seemingly superfluous reference to JavacAnnotationHandler's classloader is not in fact superfluous!
+ Iterator<JavacAnnotationHandler> it = ServiceLoader.load(JavacAnnotationHandler.class,
+ JavacAnnotationHandler.class.getClassLoader()).iterator();
+ while ( it.hasNext() ) {
+ try {
+ JavacAnnotationHandler<?> handler = it.next();
+ Class<? extends Annotation> annotationClass =
+ SpiLoadUtil.findAnnotationClass(handler.getClass(), JavacAnnotationHandler.class);
+ AnnotationHandlerContainer<?> container = new AnnotationHandlerContainer(handler, annotationClass);
+ if ( lib.annotationHandlers.put(container.annotationClass.getName(), container) != null ) {
+ messager.printMessage(Diagnostic.Kind.WARNING,
+ "Duplicate handlers for annotation type: " + container.annotationClass.getName());
+ }
+ } catch ( ServiceConfigurationError e ) {
+ messager.printMessage(Diagnostic.Kind.WARNING,
+ "Can't load Lombok annotation handler for javac: " + e);
+ }
+ }
+ }
+
+ 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());
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ 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) {
+ //Later!
+ }
+
+ public boolean hasHandlerFor(TypeElement annotationType) {
+ return annotationHandlers.containsKey(annotationType.getQualifiedName().toString());
+ }
+}
diff --git a/src/lombok/javac/JavacAnnotationHandler.java b/src/lombok/javac/JavacAnnotationHandler.java
new file mode 100644
index 00000000..67542a12
--- /dev/null
+++ b/src/lombok/javac/JavacAnnotationHandler.java
@@ -0,0 +1,7 @@
+package lombok.javac;
+
+import java.lang.annotation.Annotation;
+
+public interface JavacAnnotationHandler<T extends Annotation> {
+ void handle(JavacNode annotedElement, T annotation);
+}
diff --git a/src/lombok/javac/JavacNode.java b/src/lombok/javac/JavacNode.java
new file mode 100644
index 00000000..2e65e1d1
--- /dev/null
+++ b/src/lombok/javac/JavacNode.java
@@ -0,0 +1,63 @@
+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/apt/PKG.java b/src/lombok/javac/apt/PKG.java
index 2808e31e..2ecf1c7a 100644
--- a/src/lombok/apt/PKG.java
+++ b/src/lombok/javac/apt/PKG.java
@@ -1,19 +1,13 @@
-package lombok.apt;
+package lombok.javac.apt;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import javax.lang.model.element.Element;
-import javax.lang.model.type.TypeKind;
-
import lombok.Lombok;
-import lombok.core.TransformationsUtil;
class PKG {
- static final String CURRENT_SUPPORT = "javac 1.6 and eclipse (ecj).";
-
private PKG() {}
static boolean isInstanceOf(Object o, String className) {
@@ -49,14 +43,6 @@ class PKG {
}
}
- static String toGetterName(Element field) {
- CharSequence fieldName = field.getSimpleName();
-
- boolean isBoolean = field.asType().getKind() == TypeKind.BOOLEAN;
-
- return TransformationsUtil.toGetterName(fieldName, isBoolean);
- }
-
static byte[] readStream(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[65536];
diff --git a/src/lombok/javac/apt/Processor.java b/src/lombok/javac/apt/Processor.java
new file mode 100644
index 00000000..14a62367
--- /dev/null
+++ b/src/lombok/javac/apt/Processor.java
@@ -0,0 +1,62 @@
+package lombok.javac.apt;
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+import lombok.javac.HandlerLibrary;
+import lombok.javac.JavacNode;
+
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+
+
+@SupportedAnnotationTypes("*")
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class Processor extends AbstractProcessor {
+ private JavacProcessingEnvironment processingEnv;
+ private HandlerLibrary handlers;
+ private Trees trees;
+
+ @Override public void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ if ( !(processingEnv instanceof JavacProcessingEnvironment) ) this.processingEnv = null;
+ else {
+ this.processingEnv = (JavacProcessingEnvironment) processingEnv;
+ handlers = HandlerLibrary.load(processingEnv.getMessager());
+ }
+ }
+
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if ( processingEnv == null ) return false;
+
+ trees = Trees.instance(processingEnv);
+
+ for ( TypeElement annotationType : annotations ) {
+ if ( !handlers.hasHandlerFor(annotationType) ) continue;
+ for ( Element element : roundEnv.getElementsAnnotatedWith(annotationType) ) {
+ System.out.println("HIGHER PING: " + element);
+ handlers.handleAnnotation(createNode(element), annotationType);
+ }
+ }
+
+ for ( Element element : roundEnv.getRootElements() ) {
+ if ( element instanceof TypeElement ) {
+ handlers.handleType((TypeElement)element);
+ }
+ }
+
+ return false;
+ }
+
+ private JavacNode createNode(Element element) {
+ return new JavacNode(trees, processingEnv, element);
+ }
+}
diff --git a/src/lombok/javac/handlers/HandleGetter_javac.java b/src/lombok/javac/handlers/HandleGetter_javac.java
new file mode 100644
index 00000000..77ce2b4f
--- /dev/null
+++ b/src/lombok/javac/handlers/HandleGetter_javac.java
@@ -0,0 +1,60 @@
+package lombok.javac.handlers;
+
+import static lombok.javac.handlers.PKG.*;
+
+import javax.lang.model.element.Element;
+
+import lombok.Getter;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+
+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;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+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) {
+ System.out.println("PING: " + node.getJavacAST());
+ if ( !node.getJavacAST().getKind().isField() ) {
+ node.addError("@Getter is only supported on a field.");
+ return;
+ }
+
+ JCClassDecl javacClassTree = node.getEnclosingType();
+
+ int access = toJavacModifier(getter.value());
+
+ MethodTree getterMethod = createGetter(access, node.getJavacAST(), node.createTreeMaker(), node.createNameTable());
+ 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));
+
+ JCBlock methodBody = treeMaker.Block(0, List.of(returnStatement));
+ Name methodName = Name.fromString(nameTable, toGetterName(field));
+ JCExpression methodType = treeMaker.Type((Type)field.asType());
+
+ List<JCTypeParameter> methodGenericParams = List.nil();
+ List<JCVariableDecl> parameters = List.nil();
+ List<JCExpression> throwsClauses = List.nil();
+ JCExpression annotationMethodDefaultValue = null;
+
+ return treeMaker.MethodDef(treeMaker.Modifiers(access, List.<JCAnnotation>nil()), methodName, methodType,
+ methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue);
+ }
+}
diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java
new file mode 100644
index 00000000..4622c3ee
--- /dev/null
+++ b/src/lombok/javac/handlers/PKG.java
@@ -0,0 +1,34 @@
+package lombok.javac.handlers;
+
+import java.lang.reflect.Modifier;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeKind;
+
+import lombok.AccessLevel;
+import lombok.core.TransformationsUtil;
+
+class PKG {
+ static String toGetterName(Element field) {
+ CharSequence fieldName = field.getSimpleName();
+
+ boolean isBoolean = field.asType().getKind() == TypeKind.BOOLEAN;
+
+ return TransformationsUtil.toGetterName(fieldName, isBoolean);
+ }
+
+ static int toJavacModifier(AccessLevel accessLevel) {
+ switch ( accessLevel ) {
+ case MODULE:
+ case PACKAGE:
+ return 0;
+ default:
+ case PUBLIC:
+ return Modifier.PUBLIC;
+ case PRIVATE:
+ return Modifier.PRIVATE;
+ case PROTECTED:
+ return Modifier.PROTECTED;
+ }
+ }
+}