* Added priorities to handlers, along with implementation of the priority system for javac and ecj.

* @Value now makes the class itself final by default.
This commit is contained in:
Reinier Zwitserloot committed 2012-08-10 18:06:13 +02:00
1 parent 70317c7384
commit 1be7da7601
20 files changed
+274 -174

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
package lombok.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlerPriority {
int value();
/**
* This can be used to differentiate 2 handlers with the same value to be at a different handler priority anyway.
* <strong>DO NOT USE THIS</strong> unless someone has been crowding out the numbers and there's no room left.
*/
int subValue() default 0;
}
+68 -17
View File
@@ -28,17 +28,20 @@ import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import lombok.Lombok;
import lombok.core.AnnotationValues;
import lombok.core.PrintAST;
import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
import lombok.core.HandlerPriority;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -59,13 +62,39 @@ public class HandlerLibrary {
private TypeLibrary typeLibrary = new TypeLibrary();
private static class VisitorContainer {
private final EclipseASTVisitor visitor;
private final long priority;
private final boolean deferUntilPostDiet;
VisitorContainer(EclipseASTVisitor visitor) {
this.visitor = visitor;
this.deferUntilPostDiet = visitor.getClass().isAnnotationPresent(DeferUntilPostDiet.class);
HandlerPriority hp = visitor.getClass().getAnnotation(HandlerPriority.class);
this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue();
}
public boolean deferUntilPostDiet() {
return deferUntilPostDiet;
}
public long getPriority() {
return priority;
}
}
private static class AnnotationHandlerContainer<T extends Annotation> {
private EclipseAnnotationHandler<T> handler;
private Class<T> annotationClass;
private final EclipseAnnotationHandler<T> handler;
private final Class<T> annotationClass;
private final long priority;
private final boolean deferUntilPostDiet;
AnnotationHandlerContainer(EclipseAnnotationHandler<T> handler, Class<T> annotationClass) {
this.handler = handler;
this.annotationClass = annotationClass;
this.deferUntilPostDiet = handler.getClass().isAnnotationPresent(DeferUntilPostDiet.class);
HandlerPriority hp = handler.getClass().getAnnotation(HandlerPriority.class);
this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue();
}
public void handle(org.eclipse.jdt.internal.compiler.ast.Annotation annotation,
@@ -81,14 +110,18 @@ public class HandlerLibrary {
}
public boolean deferUntilPostDiet() {
return handler.getClass().isAnnotationPresent(DeferUntilPostDiet.class);
return deferUntilPostDiet;
}
public long getPriority() {
return priority;
}
}
private Map<String, AnnotationHandlerContainer<?>> annotationHandlers =
new HashMap<String, AnnotationHandlerContainer<?>>();
private Collection<EclipseASTVisitor> visitorHandlers = new ArrayList<EclipseASTVisitor>();
private Collection<VisitorContainer> visitorHandlers = new ArrayList<VisitorContainer>();
/**
* Creates a new HandlerLibrary. Errors will be reported to the Eclipse Error log.
@@ -101,9 +134,24 @@ public class HandlerLibrary {
loadAnnotationHandlers(lib);
loadVisitorHandlers(lib);
lib.calculatePriorities();
return lib;
}
private SortedSet<Long> priorities;
public SortedSet<Long> getPriorities() {
return priorities;
}
private void calculatePriorities() {
SortedSet<Long> set = new TreeSet<Long>();
for (AnnotationHandlerContainer<?> container : annotationHandlers.values()) set.add(container.getPriority());
for (VisitorContainer container : visitorHandlers) set.add(container.getPriority());
this.priorities = Collections.unmodifiableSortedSet(set);
}
/** Uses SPI Discovery to find implementations of {@link EclipseAnnotationHandler}. */
@SuppressWarnings({"rawtypes", "unchecked"})
private static void loadAnnotationHandlers(HandlerLibrary lib) {
@@ -131,7 +179,7 @@ public class HandlerLibrary {
private static void loadVisitorHandlers(HandlerLibrary lib) {
try {
for (EclipseASTVisitor visitor : SpiLoadUtil.findServices(EclipseASTVisitor.class, EclipseASTVisitor.class.getClassLoader())) {
lib.visitorHandlers.add(visitor);
lib.visitorHandlers.add(new VisitorContainer(visitor));
}
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
@@ -170,7 +218,7 @@ public class HandlerLibrary {
* @param annotationNode The Lombok AST Node representing the Annotation AST Node.
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, boolean skipPrintAst) {
public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) {
String pkgName = annotationNode.getPackageDeclaration();
Collection<String> imports = annotationNode.getImportStatements();
@@ -179,11 +227,10 @@ public class HandlerLibrary {
if (rawType == null) return;
for (String fqn : resolver.findTypeMatches(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()))) {
boolean isPrintAST = fqn.equals(PrintAST.class.getName());
if (isPrintAST == skipPrintAst) continue;
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) continue;
if (priority != container.getPriority()) continue;
if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
continue;
@@ -202,12 +249,16 @@ public class HandlerLibrary {
/**
* Will call all registered {@link EclipseASTVisitor} instances.
*/
public void callASTVisitors(EclipseAST ast) {
for (EclipseASTVisitor visitor : visitorHandlers) try {
ast.traverse(visitor);
} catch (Throwable t) {
error((CompilationUnitDeclaration) ast.top().get(),
String.format("Lombok visitor handler %s failed", visitor.getClass()), t);
public void callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) {
for (VisitorContainer container : visitorHandlers) {
if (!isCompleteParse && container.deferUntilPostDiet()) continue;
if (priority != container.getPriority()) continue;
try {
ast.traverse(container.visitor);
} catch (Throwable t) {
error((CompilationUnitDeclaration) ast.top().get(),
String.format("Lombok visitor handler %s failed", container.visitor.getClass()), t);
}
}
}
}
@@ -165,41 +165,42 @@ public class TransformEclipseAST {
* then handles any PrintASTs.
*/
public void go() {
ast.traverse(new AnnotationVisitor(true));
handlers.callASTVisitors(ast);
ast.traverse(new AnnotationVisitor(false));
for (Long d : handlers.getPriorities()) {
ast.traverse(new AnnotationVisitor(d));
handlers.callASTVisitors(ast, d, ast.isCompleteParse());
}
}
private static class AnnotationVisitor extends EclipseASTAdapter {
private final boolean skipPrintAst;
private final long priority;
public AnnotationVisitor(boolean skipAllButPrintAST) {
this.skipPrintAst = skipAllButPrintAST;
public AnnotationVisitor(long priority) {
this.priority = priority;
}
@Override public void visitAnnotationOnField(FieldDeclaration field, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, skipPrintAst);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, skipPrintAst);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnLocal(LocalDeclaration local, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, skipPrintAst);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnMethod(AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, skipPrintAst);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnType(TypeDeclaration type, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, skipPrintAst);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
}
}
@@ -26,13 +26,17 @@ import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.ExtensionMethod;
// This handler just does some additional error checking; the real work is done in the agent.
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(66560) // 2^16 + 2^10; we must run AFTER HandleVal which is at 2^16
public class HandleExtensionMethod extends EclipseAnnotationHandler<ExtensionMethod> {
@Override public void handle(AnnotationValues<ExtensionMethod> annotation, Annotation ast, EclipseNode annotationNode) {
TypeDeclaration typeDecl = null;
@@ -25,6 +25,7 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.FieldDefaults;
@@ -42,6 +43,7 @@ import org.mangosdk.spi.ProviderFor;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -30,6 +30,7 @@ import org.mangosdk.spi.ProviderFor;
import lombok.Lombok;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.PrintAST;
import lombok.eclipse.DeferUntilPostDiet;
import lombok.eclipse.EclipseASTVisitor;
@@ -41,6 +42,7 @@ import lombok.eclipse.EclipseNode;
*/
@ProviderFor(EclipseAnnotationHandler.class)
@DeferUntilPostDiet
@HandlerPriority(536870912) // 2^29; this handler is customarily run at the very end.
public class HandlePrintAST extends EclipseAnnotationHandler<PrintAST> {
public void handle(AnnotationValues<PrintAST> annotation, Annotation ast, EclipseNode annotationNode) {
PrintStream stream = System.out;
@@ -22,6 +22,7 @@
package lombok.eclipse.handlers;
import lombok.val;
import lombok.core.HandlerPriority;
import lombok.eclipse.DeferUntilPostDiet;
import lombok.eclipse.EclipseASTAdapter;
import lombok.eclipse.EclipseASTVisitor;
@@ -38,6 +39,7 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseASTVisitor.class)
@DeferUntilPostDiet
@HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated.
public class HandleVal extends EclipseASTAdapter {
@Override public void visitLocal(EclipseNode localNode, LocalDeclaration local) {
if (!EclipseHandlerUtil.typeMatches(val.class, localNode, local.type)) return;
@@ -21,11 +21,14 @@
*/
package lombok.eclipse.handlers;
import static lombok.eclipse.handlers.EclipseHandlerUtil.hasAnnotation;
import lombok.AccessLevel;
import lombok.experimental.Value;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -36,6 +39,7 @@ import org.mangosdk.spi.ProviderFor;
* Handles the {@code lombok.Value} annotation for eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends EclipseAnnotationHandler<Value> {
public void handle(AnnotationValues<Value> annotation, Annotation ast, EclipseNode annotationNode) {
Value ann = annotation.getInstance();
@@ -52,6 +56,11 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
return;
}
// Make class final.
if (!hasAnnotation(NonFinal.class, typeNode)) typeDecl.modifiers |= ClassFileConstants.AccFinal;
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
//Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to
//'find callers' on the annotation node will find callers of the constructor, which is by far the
//most useful of the many methods built by @Value. This trick won't work for the non-static constructor,
@@ -62,7 +71,6 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
new HandleWither().generateWitherForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, ast);
}
}
+5 -3
View File
@@ -35,12 +35,14 @@ import java.lang.annotation.Target;
* <li>Generates withers for all fields (except final fields that are initialized in the field declaration itself)
* <li>Generates a constructor for each argument
* <li>Adds {@code private} and {@code final} to each field.
* <li>Makes the class itself final.
* </ul>
*
* In other words, {@code @Value} is a shorthand for {@code @Getter @Wither @FieldDefaults(makeFinal=true,level=AccessLevel.PRIVATE) @EqualsAndHashCode @ToString @AllArgsConstructor}.
* In other words, {@code @Value} is a shorthand for:<br />
* {@code final @Getter @Wither @FieldDefaults(makeFinal=true,level=AccessLevel.PRIVATE) @EqualsAndHashCode @ToString @AllArgsConstructor}.
* <p>
* If any method to be generated already exists (in name - the return type or parameters are not relevant), then
* that method will not be generated by the Data annotation.
* If any method to be generated already exists (in name and parameter c ount - the return type or parameter types are not relevant), then
* that method will not be generated by the Value annotation.
* <p>
* The generated constructor will have 1 parameter for each field. The generated toString will print all fields,
* while the generated hashCode and equals take into account all non-transient fields.<br>
+51 -43
View File
@@ -25,14 +25,17 @@ import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
import lombok.core.PrintAST;
import lombok.core.HandlerPriority;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
@@ -52,9 +55,8 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
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>();
private final Collection<VisitorContainer> visitorHandlers = new ArrayList<VisitorContainer>();
private final Messager messager;
private int phase = 0;
/**
* Creates a new HandlerLibrary that will report any problems or errors to the provided messager.
@@ -64,22 +66,53 @@ public class HandlerLibrary {
this.messager = messager;
}
private static class VisitorContainer {
private final JavacASTVisitor visitor;
private final long priority;
VisitorContainer(JavacASTVisitor visitor) {
this.visitor = visitor;
HandlerPriority hp = visitor.getClass().getAnnotation(HandlerPriority.class);
this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue();
}
public long getPriority() {
return priority;
}
}
private static class AnnotationHandlerContainer<T extends Annotation> {
private JavacAnnotationHandler<T> handler;
private Class<T> annotationClass;
private final JavacAnnotationHandler<T> handler;
private final Class<T> annotationClass;
private final long priority;
AnnotationHandlerContainer(JavacAnnotationHandler<T> handler, Class<T> annotationClass) {
this.handler = handler;
this.annotationClass = annotationClass;
}
public boolean isResolutionBased() {
return handler.getClass().isAnnotationPresent(ResolutionBased.class);
HandlerPriority hp = handler.getClass().getAnnotation(HandlerPriority.class);
this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue();
}
public void handle(final JavacNode node) {
handler.handle(JavacHandlerUtil.createAnnotation(annotationClass, node), (JCAnnotation)node.get(), node);
}
public long getPriority() {
return priority;
}
}
private SortedSet<Long> priorities;
public SortedSet<Long> getPriorities() {
return priorities;
}
private void calculatePriorities() {
SortedSet<Long> set = new TreeSet<Long>();
for (AnnotationHandlerContainer<?> container : annotationHandlers.values()) set.add(container.getPriority());
for (VisitorContainer container : visitorHandlers) set.add(container.getPriority());
this.priorities = Collections.unmodifiableSortedSet(set);
}
/**
@@ -97,6 +130,8 @@ public class HandlerLibrary {
System.err.println("Lombok isn't running due to misconfigured SPI files: " + e);
}
library.calculatePriorities();
return library;
}
@@ -120,7 +155,7 @@ public class HandlerLibrary {
private static void loadVisitorHandlers(HandlerLibrary lib) throws IOException {
//No, that seemingly superfluous reference to JavacASTVisitor's classloader is not in fact superfluous!
for (JavacASTVisitor visitor : SpiLoadUtil.findServices(JavacASTVisitor.class, JavacASTVisitor.class.getClassLoader())) {
lib.visitorHandlers.add(visitor);
lib.visitorHandlers.add(new VisitorContainer(visitor));
}
}
@@ -171,24 +206,15 @@ public class HandlerLibrary {
* @param node The Lombok AST Node representing the Annotation AST Node.
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation) {
public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation, long priority) {
TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
String rawType = annotation.annotationType.toString();
for (String fqn : resolver.findTypeMatches(node, typeLibrary, rawType)) {
boolean isPrintAST = fqn.equals(PrintAST.class.getName());
if (isPrintAST && phase != 2) continue;
if (!isPrintAST && phase == 2) continue;
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) continue;
try {
if (container.isResolutionBased() && phase == 1) {
if (checkAndSetHandled(annotation)) container.handle(node);
}
if (!container.isResolutionBased() && phase == 0) {
if (checkAndSetHandled(annotation)) container.handle(node);
}
if (container.annotationClass == PrintAST.class && phase == 2) {
if (container.getPriority() == priority) {
if (checkAndSetHandled(annotation)) container.handle(node);
}
} catch (AnnotationValueDecodeFail fail) {
@@ -204,29 +230,11 @@ public class HandlerLibrary {
/**
* Will call all registered {@link JavacASTVisitor} instances.
*/
public void callASTVisitors(JavacAST ast) {
for (JavacASTVisitor visitor : visitorHandlers) try {
boolean isResolutionBased = visitor.getClass().isAnnotationPresent(ResolutionBased.class);
if (!isResolutionBased && phase == 0) ast.traverse(visitor);
if (isResolutionBased && phase == 1) ast.traverse(visitor);
public void callASTVisitors(JavacAST ast, long priority) {
for (VisitorContainer container : visitorHandlers) try {
if (container.getPriority() == priority) ast.traverse(container.visitor);
} catch (Throwable t) {
javacError(String.format("Lombok visitor handler %s failed", visitor.getClass()), t);
javacError(String.format("Lombok visitor handler %s failed", container.visitor.getClass()), t);
}
}
/**
* Lombok does not currently support triggering annotations in a specified order; the order is essentially
* random right now. As a temporary hack we've identified 3 important phases.
*/
public void setPreResolutionPhase() {
phase = 0;
}
public void setPostResolutionPhase() {
phase = 1;
}
public void setPrintASTPhase() {
phase = 2;
}
}
+20 -26
View File
@@ -22,10 +22,10 @@
package lombok.javac;
import java.util.ArrayList;
import java.util.SortedSet;
import javax.annotation.processing.Messager;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -43,7 +43,11 @@ public class JavacTransformer {
this.handlers = HandlerLibrary.load(messager);
}
public void transform(boolean postResolution, Context context, java.util.List<JCCompilationUnit> compilationUnitsRaw) {
public SortedSet<Long> getPriorities() {
return handlers.getPriorities();
}
public void transform(long priority, Context context, java.util.List<JCCompilationUnit> compilationUnitsRaw) {
List<JCCompilationUnit> compilationUnits;
if (compilationUnitsRaw instanceof List<?>) {
compilationUnits = (List<JCCompilationUnit>)compilationUnitsRaw;
@@ -58,54 +62,44 @@ public class JavacTransformer {
for (JCCompilationUnit unit : compilationUnits) asts.add(new JavacAST(messager, context, unit));
if (!postResolution) {
handlers.setPreResolutionPhase();
for (JavacAST ast : asts) {
ast.traverse(new AnnotationVisitor());
handlers.callASTVisitors(ast);
}
}
if (postResolution) {
handlers.setPostResolutionPhase();
for (JavacAST ast : asts) {
ast.traverse(new AnnotationVisitor());
handlers.callASTVisitors(ast);
}
handlers.setPrintASTPhase();
for (JavacAST ast : asts) {
ast.traverse(new AnnotationVisitor());
}
for (JavacAST ast : asts) {
ast.traverse(new AnnotationVisitor(priority));
handlers.callASTVisitors(ast, priority);
}
for (JavacAST ast : asts) if (ast.isChanged()) LombokOptions.markChanged(context, (JCCompilationUnit) ast.top().get());
}
private class AnnotationVisitor extends JavacASTAdapter {
private final long priority;
AnnotationVisitor(long priority) {
this.priority = priority;
}
@Override public void visitAnnotationOnType(JCClassDecl type, JavacNode annotationNode, JCAnnotation annotation) {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnField(JCVariableDecl field, JavacNode annotationNode, JCAnnotation annotation) {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnMethod(JCMethodDecl method, JavacNode annotationNode, JCAnnotation annotation) {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnMethodArgument(JCVariableDecl argument, JCMethodDecl method, JavacNode annotationNode, JCAnnotation annotation) {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
@Override public void visitAnnotationOnLocal(JCVariableDecl local, JavacNode annotationNode, JCAnnotation annotation) {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation);
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
}
}
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2012 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker to indicate a handler is to be called after all the non-resolution based visitors.
* NB: Temporary solution - will be rewritten to a different style altogether in a future release.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResolutionBased {
}
+60 -25
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2010 The Project Lombok Authors.
* Copyright (C) 2009-2012 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,10 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
@@ -80,6 +82,14 @@ public class Processor extends AbstractProcessor {
placePostCompileAndDontMakeForceRoundDummiesHook();
transformer = new JavacTransformer(procEnv.getMessager());
trees = Trees.instance(procEnv);
SortedSet<Long> p = transformer.getPriorities();
if (p.isEmpty()) {
this.priorityLevels = new long[] {0L};
} else {
this.priorityLevels = new long[p.size()];
int i = 0;
for (Long prio : p) this.priorityLevels[i++] = prio;
}
}
private void placePostCompileAndDontMakeForceRoundDummiesHook() {
@@ -204,47 +214,72 @@ public class Processor extends AbstractProcessor {
}
}
private final IdentityHashMap<JCCompilationUnit, Void> rootsAtPhase0 = new IdentityHashMap<JCCompilationUnit, Void>();
private final IdentityHashMap<JCCompilationUnit, Void> rootsAtPhase1 = new IdentityHashMap<JCCompilationUnit, Void>();
private int dummyCount = 0;
private final IdentityHashMap<JCCompilationUnit, Long> roots = new IdentityHashMap<JCCompilationUnit, Long>();
private long[] priorityLevels;
/** {@inheritDoc} */
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) return false;
if (!rootsAtPhase0.isEmpty()) {
ArrayList<JCCompilationUnit> cus = new ArrayList<JCCompilationUnit>(rootsAtPhase0.keySet());
transformer.transform(true, processingEnv.getContext(), cus);
rootsAtPhase1.putAll(rootsAtPhase0);
rootsAtPhase0.clear();
}
// We have: A sorted set of all priority levels: 'priorityLevels'
// Step 1: Take all CUs which aren't already in the map. Give them the first priority level.
for (Element element : roundEnv.getRootElements()) {
JCCompilationUnit unit = toUnit(element);
if (unit != null) {
if (!rootsAtPhase1.containsKey(unit)) rootsAtPhase0.put(unit, null);
}
if (unit == null) continue;
if (roots.containsKey(unit)) continue;
roots.put(unit, priorityLevels[0]);
}
if (!rootsAtPhase0.isEmpty()) {
ArrayList<JCCompilationUnit> cus = new ArrayList<JCCompilationUnit>(rootsAtPhase0.keySet());
transformer.transform(false, processingEnv.getContext(), cus);
JavacFiler filer = (JavacFiler) processingEnv.getFiler();
if (!filer.newFiles()) {
try {
JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++));
Writer w = dummy.openWriter();
w.close();
} catch (Exception e) {
processingEnv.getMessager().printMessage(Kind.WARNING,
"Can't force a new processing round. Lombok features that require resolution won't work.");
// Step 2: For all CUs (in the map, not the roundEnv!), run them across all handlers at their current prio level.
for (long prio : priorityLevels) {
List<JCCompilationUnit> cusForThisRound = new ArrayList<JCCompilationUnit>();
for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
Long prioOfCu = entry.getValue();
if (prioOfCu == null || prioOfCu != prio) continue;
cusForThisRound.add(entry.getKey());
}
transformer.transform(prio, processingEnv.getContext(), cusForThisRound);
}
// Step 3: Push up all CUs to the next level. Set level to null if there is no next level.
boolean nextRoundNeeded = false;
for (int i = priorityLevels.length - 1; i >= 0; i--) {
Long curLevel = priorityLevels[i];
Long nextLevel = (i == priorityLevels.length - 1) ? null : priorityLevels[i + 1];
for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
if (curLevel.equals(entry.getValue())) {
entry.setValue(nextLevel);
if (nextLevel != null) nextRoundNeeded = true;
}
}
}
// Step 4: If ALL values are null, quit. Else, force new round.
if (nextRoundNeeded) forceNewRound((JavacFiler) processingEnv.getFiler());
return false;
}
private int dummyCount = 0;
private void forceNewRound(JavacFiler filer) {
if (!filer.newFiles()) {
try {
JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++));
Writer w = dummy.openWriter();
w.close();
} catch (Exception e) {
e.printStackTrace();
processingEnv.getMessager().printMessage(Kind.WARNING,
"Can't force a new processing round. Lombok won't work.");
}
}
}
private JCCompilationUnit toUnit(Element element) {
TreePath path = trees == null ? null : trees.getPath(element);
if (path == null) return null;
@@ -43,11 +43,11 @@ import javax.lang.model.type.TypeMirror;
import lombok.Delegate;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.javac.FindTypeVarScanner;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacResolution;
import lombok.javac.ResolutionBased;
import lombok.javac.JavacResolution.TypeNotConvertibleException;
import org.mangosdk.spi.ProviderFor;
@@ -75,7 +75,7 @@ import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
@ProviderFor(JavacAnnotationHandler.class)
@ResolutionBased
@HandlerPriority(65536) //2^16; to make sure that we also delegate generated methods.
public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
private static final List<String> METHODS_IN_OBJECT = Collections.unmodifiableList(Arrays.asList(
"hashCode()",
@@ -31,10 +31,10 @@ import java.util.List;
import javax.lang.model.element.ElementKind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.ExtensionMethod;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.ResolutionBased;
import org.mangosdk.spi.ProviderFor;
@@ -60,7 +60,7 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
* Handles the {@link ExtensionMethod} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
@ResolutionBased
@HandlerPriority(66560) // 2^16 + 2^10; we must run AFTER HandleVal which is at 2^16
public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMethod> {
@Override
public void handle(final AnnotationValues<ExtensionMethod> annotation, final JCAnnotation source, final JavacNode annotationNode) {
@@ -83,9 +83,6 @@ public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMetho
final List<Extension> extensions = getExtensions(annotationNode, extensionProviders);
if (extensions.isEmpty()) return;
// call HandleVal explicitly to ensure val gets handled before @ExtensionMethdod gets handled.
// TODO maybe we should prioritize lombok handler
annotationNode.traverse(new HandleVal());
new ExtensionMethodReplaceVisitor(annotationNode, extensions, suppressBaseMethods).replace();
annotationNode.rebuild();
@@ -25,6 +25,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;
@@ -43,6 +44,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -44,7 +44,6 @@ import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
public class HandleLog {
private HandleLog() {
throw new UnsupportedOperationException();
}
@@ -31,6 +31,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import lombok.Lombok;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.PrintAST;
import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacAnnotationHandler;
@@ -40,6 +41,7 @@ import lombok.javac.JavacNode;
* Handles the {@code lombok.core.PrintAST} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(536870912) // 2^29; this handler is customarily run at the very end.
public class HandlePrintAST extends JavacAnnotationHandler<PrintAST> {
@Override public void handle(AnnotationValues<PrintAST> annotation, JCAnnotation ast, JavacNode annotationNode) {
PrintStream stream = System.out;
@@ -24,11 +24,11 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.val;
import lombok.core.HandlerPriority;
import lombok.javac.JavacASTAdapter;
import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacNode;
import lombok.javac.JavacResolution;
import lombok.javac.ResolutionBased;
import org.mangosdk.spi.ProviderFor;
@@ -44,7 +44,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
@ProviderFor(JavacASTVisitor.class)
@ResolutionBased
@HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated.
public class HandleVal extends JavacASTAdapter {
@Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return;
@@ -24,18 +24,23 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.AccessLevel;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
/**
* Handles the {@code lombok.Value} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends JavacAnnotationHandler<Value> {
@Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) {
deleteAnnotationIfNeccessary(annotationNode, Value.class);
@@ -49,12 +54,16 @@ public class HandleValue extends JavacAnnotationHandler<Value> {
String staticConstructorName = annotation.getInstance().staticConstructor();
if (!hasAnnotationAndDeleteIfNeccessary(NonFinal.class, typeNode)) {
((JCClassDecl) typeNode.get()).mods.flags |= Flags.FINAL;
}
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
// TODO move this to the end OR move it to the top in eclipse.
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleWither().generateWitherForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
}
}