diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lombok/apt/AnnotationTransponder.java | 111 | ||||
-rw-r--r-- | src/lombok/apt/HandleANY_ecj.java | 17 | ||||
-rw-r--r-- | src/lombok/apt/HandleGetter_javac.java | 77 | ||||
-rw-r--r-- | src/lombok/apt/HandlerForCompiler.java | 16 | ||||
-rw-r--r-- | src/lombok/apt/Processor.java | 35 | ||||
-rw-r--r-- | src/lombok/core/SpiLoadUtil.java | 37 | ||||
-rw-r--r-- | src/lombok/eclipse/HandlerLibrary.java | 66 | ||||
-rw-r--r-- | src/lombok/javac/HandlerLibrary.java | 222 | ||||
-rw-r--r-- | src/lombok/javac/JavacAnnotationHandler.java | 7 | ||||
-rw-r--r-- | src/lombok/javac/JavacNode.java | 63 | ||||
-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.java | 62 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleGetter_javac.java | 60 | ||||
-rw-r--r-- | src/lombok/javac/handlers/PKG.java | 34 |
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; + } + } +} |