aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/lombok/core/HandlerPriority.java18
-rw-r--r--src/core/lombok/eclipse/HandlerLibrary.java85
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java23
-rw-r--r--src/core/lombok/eclipse/handlers/HandleExtensionMethod.java4
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleValue.java12
-rw-r--r--src/core/lombok/experimental/Value.java8
-rw-r--r--src/core/lombok/javac/HandlerLibrary.java94
-rw-r--r--src/core/lombok/javac/JavacTransformer.java46
-rw-r--r--src/core/lombok/javac/ResolutionBased.java36
-rw-r--r--src/core/lombok/javac/apt/Processor.java83
-rw-r--r--src/core/lombok/javac/handlers/HandleDelegate.java4
-rw-r--r--src/core/lombok/javac/handlers/HandleExtensionMethod.java7
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java1
-rw-r--r--src/core/lombok/javac/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java4
-rw-r--r--src/core/lombok/javac/handlers/HandleValue.java11
20 files changed, 273 insertions, 173 deletions
diff --git a/src/core/lombok/core/HandlerPriority.java b/src/core/lombok/core/HandlerPriority.java
new file mode 100644
index 00000000..174d02d4
--- /dev/null
+++ b/src/core/lombok/core/HandlerPriority.java
@@ -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;
+}
diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java
index 14102ba1..5f360f6f 100644
--- a/src/core/lombok/eclipse/HandlerLibrary.java
+++ b/src/core/lombok/eclipse/HandlerLibrary.java
@@ -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);
+ }
}
}
}
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index 89248be1..63783734 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -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);
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java
index 30408118..3d30e8fe 100644
--- a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java
+++ b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java
@@ -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;
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index e1dd8460..2cdfdd4d 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -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) {
diff --git a/src/core/lombok/eclipse/handlers/HandlePrintAST.java b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
index a9678f0c..da6fa2a2 100644
--- a/src/core/lombok/eclipse/handlers/HandlePrintAST.java
+++ b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
@@ -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;
diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java
index 56b3effd..e7849952 100644
--- a/src/core/lombok/eclipse/handlers/HandleVal.java
+++ b/src/core/lombok/eclipse/handlers/HandleVal.java
@@ -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;
diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java
index 9b3edabf..a1eb24ff 100644
--- a/src/core/lombok/eclipse/handlers/HandleValue.java
+++ b/src/core/lombok/eclipse/handlers/HandleValue.java
@@ -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);
}
}
diff --git a/src/core/lombok/experimental/Value.java b/src/core/lombok/experimental/Value.java
index f8bd06b2..b3ebc2d8 100644
--- a/src/core/lombok/experimental/Value.java
+++ b/src/core/lombok/experimental/Value.java
@@ -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>
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java
index 9666c9d5..1ce083f3 100644
--- a/src/core/lombok/javac/HandlerLibrary.java
+++ b/src/core/lombok/javac/HandlerLibrary.java
@@ -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;
- }
}
diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java
index 3afdee0a..9cac787c 100644
--- a/src/core/lombok/javac/JavacTransformer.java
+++ b/src/core/lombok/javac/JavacTransformer.java
@@ -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);
}
}
}
diff --git a/src/core/lombok/javac/ResolutionBased.java b/src/core/lombok/javac/ResolutionBased.java
deleted file mode 100644
index 556e3736..00000000
--- a/src/core/lombok/javac/ResolutionBased.java
+++ /dev/null
@@ -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 {
-}
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index d05a3bdf..190a369b 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -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]);
+ }
+
+ // 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);
}
- 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 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;
diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java
index 50a2f1bb..b693a2a3 100644
--- a/src/core/lombok/javac/handlers/HandleDelegate.java
+++ b/src/core/lombok/javac/handlers/HandleDelegate.java
@@ -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()",
diff --git a/src/core/lombok/javac/handlers/HandleExtensionMethod.java b/src/core/lombok/javac/handlers/HandleExtensionMethod.java
index 92d7c0e4..68df38ef 100644
--- a/src/core/lombok/javac/handlers/HandleExtensionMethod.java
+++ b/src/core/lombok/javac/handlers/HandleExtensionMethod.java
@@ -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();
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index a2dfb7ce..c0829172 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -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) {
diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index 4208f3c6..5ccc82d4 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -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();
}
diff --git a/src/core/lombok/javac/handlers/HandlePrintAST.java b/src/core/lombok/javac/handlers/HandlePrintAST.java
index 27aa02ad..7b6d942c 100644
--- a/src/core/lombok/javac/handlers/HandlePrintAST.java
+++ b/src/core/lombok/javac/handlers/HandlePrintAST.java
@@ -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;
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 52d2ed13..0ab9d783 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -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;
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index fac017a8..b52bad7c 100644
--- a/src/core/lombok/javac/handlers/HandleValue.java
+++ b/src/core/lombok/javac/handlers/HandleValue.java
@@ -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);
}
}