aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/eclipse
diff options
context:
space:
mode:
authorPhilippe Charles <charphi@users.noreply.github.com>2018-08-22 09:54:00 +0200
committerGitHub <noreply@github.com>2018-08-22 09:54:00 +0200
commit3ffac6642456e2c7d32952c62df8a565e2d4728b (patch)
treeb0a28ac208dcb8af9add494c4caffe4f11bb287b /src/core/lombok/eclipse
parent35c7c6bda2e71da2e6e06cec5b0fb012c348f694 (diff)
parent769185e123dfd4a073161eafb58ce50bb79d6201 (diff)
downloadlombok-3ffac6642456e2c7d32952c62df8a565e2d4728b.tar.gz
lombok-3ffac6642456e2c7d32952c62df8a565e2d4728b.tar.bz2
lombok-3ffac6642456e2c7d32952c62df8a565e2d4728b.zip
Merge branch 'master' into master
Diffstat (limited to 'src/core/lombok/eclipse')
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java12
-rw-r--r--src/core/lombok/eclipse/EclipseASTAdapter.java7
-rw-r--r--src/core/lombok/eclipse/EclipseASTVisitor.java111
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java10
-rw-r--r--src/core/lombok/eclipse/HandlerLibrary.java24
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java32
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java26
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java36
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java26
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java144
10 files changed, 309 insertions, 119 deletions
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index dc2c9843..7cd2e400 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -73,16 +73,20 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated");
private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER;
+ public static URI getAbsoluteFileLocation(CompilationUnitDeclaration ast) {
+ return getAbsoluteFileLocation0(ast);
+ }
+
public URI getAbsoluteFileLocation() {
if (memoizedAbsoluteFileLocation != NOT_CALCULATED_MARKER) return memoizedAbsoluteFileLocation;
- memoizedAbsoluteFileLocation = getAbsoluteFileLocation0();
+ memoizedAbsoluteFileLocation = getAbsoluteFileLocation0(this.compilationUnitDeclaration);
return memoizedAbsoluteFileLocation;
}
/** This is the call, but we wrapped it to memoize this. */
- private URI getAbsoluteFileLocation0() {
- String fileName = getFileName();
+ private static URI getAbsoluteFileLocation0(CompilationUnitDeclaration ast) {
+ String fileName = toFileName(ast);
if (fileName != null && (fileName.startsWith("file:") || fileName.startsWith("sourcecontrol:"))) {
// Some exotic build systems get real fancy with filenames. Known culprits:
// The 'jazz' source control system _probably_ (not confirmed yet) uses sourcecontrol://jazz: urls.
diff --git a/src/core/lombok/eclipse/EclipseASTAdapter.java b/src/core/lombok/eclipse/EclipseASTAdapter.java
index 2a78c270..61807fff 100644
--- a/src/core/lombok/eclipse/EclipseASTAdapter.java
+++ b/src/core/lombok/eclipse/EclipseASTAdapter.java
@@ -36,6 +36,9 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
* has been implemented with an empty body. Override whichever methods you need.
*/
public abstract class EclipseASTAdapter implements EclipseASTVisitor {
+
+ private final boolean deferUntilPostDiet = getClass().isAnnotationPresent(DeferUntilPostDiet.class);
+
/** {@inheritDoc} */
public void visitCompilationUnit(EclipseNode top, CompilationUnitDeclaration unit) {}
@@ -98,4 +101,8 @@ public abstract class EclipseASTAdapter implements EclipseASTVisitor {
/** {@inheritDoc} */
public void endVisitStatement(EclipseNode statementNode, Statement statement) {}
+
+ public boolean isDeferUntilPostDiet() {
+ return deferUntilPostDiet ;
+ }
}
diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java
index f5b49cbb..b2fd4b2f 100644
--- a/src/core/lombok/eclipse/EclipseASTVisitor.java
+++ b/src/core/lombok/eclipse/EclipseASTVisitor.java
@@ -26,7 +26,9 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
@@ -38,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/**
* Implement so you can ask any EclipseAST.Node to traverse depth-first through all children,
@@ -199,8 +202,82 @@ public interface EclipseASTVisitor {
print("</CUD>");
}
+ private String printFlags(int flags, ASTNode node) {
+ StringBuilder out = new StringBuilder();
+ if ((flags & ClassFileConstants.AccPublic) != 0) {
+ flags &= ~ClassFileConstants.AccPublic;
+ out.append("public ");
+ }
+ if ((flags & ClassFileConstants.AccPrivate) != 0) {
+ flags &= ~ClassFileConstants.AccPrivate;
+ out.append("private ");
+ }
+ if ((flags & ClassFileConstants.AccProtected) != 0) {
+ flags &= ~ClassFileConstants.AccProtected;
+ out.append("protected ");
+ }
+ if ((flags & ClassFileConstants.AccStatic) != 0) {
+ flags &= ~ClassFileConstants.AccStatic;
+ out.append("static ");
+ }
+ if ((flags & ClassFileConstants.AccFinal) != 0) {
+ flags &= ~ClassFileConstants.AccFinal;
+ out.append("final ");
+ }
+ if ((flags & ClassFileConstants.AccSynchronized) != 0) {
+ flags &= ~ClassFileConstants.AccSynchronized;
+ out.append("synchronized ");
+ }
+ if ((flags & ClassFileConstants.AccNative) != 0) {
+ flags &= ~ClassFileConstants.AccNative;
+ out.append("native ");
+ }
+ if ((flags & ClassFileConstants.AccInterface) != 0) {
+ flags &= ~ClassFileConstants.AccInterface;
+ out.append("interface ");
+ }
+ if ((flags & ClassFileConstants.AccAbstract) != 0) {
+ flags &= ~ClassFileConstants.AccAbstract;
+ out.append("abstract ");
+ }
+ if ((flags & ClassFileConstants.AccStrictfp) != 0) {
+ flags &= ~ClassFileConstants.AccStrictfp;
+ out.append("strictfp ");
+ }
+ if ((flags & ClassFileConstants.AccSynthetic) != 0) {
+ flags &= ~ClassFileConstants.AccSynthetic;
+ out.append("synthetic ");
+ }
+ if ((flags & ClassFileConstants.AccAnnotation) != 0) {
+ flags &= ~ClassFileConstants.AccAnnotation;
+ out.append("annotation ");
+ }
+ if ((flags & ClassFileConstants.AccEnum) != 0) {
+ flags &= ~ClassFileConstants.AccEnum;
+ out.append("enum ");
+ }
+
+ if ((flags & ClassFileConstants.AccVolatile) != 0) {
+ flags &= ~ClassFileConstants.AccVolatile;
+ if (node instanceof FieldDeclaration) out.append("volatile ");
+ else out.append("volatile/bridge ");
+ }
+ if ((flags & ClassFileConstants.AccTransient) != 0) {
+ flags &= ~ClassFileConstants.AccTransient;
+ if (node instanceof Argument) out.append("varargs ");
+ else if (node instanceof FieldDeclaration) out.append("transient ");
+ else out.append("transient/varargs ");
+ }
+
+ if (flags != 0) {
+ out.append(String.format(" 0x%08X ", flags));
+ }
+
+ return out.toString().trim();
+ }
+
public void visitType(EclipseNode node, TypeDeclaration type) {
- print("<TYPE %s%s%s>", str(type.name), isGenerated(type) ? " (GENERATED)" : "", position(node));
+ print("<TYPE %s%s%s> %s", str(type.name), isGenerated(type) ? " (GENERATED)" : "", position(node), printFlags(type.modifiers, type));
indent++;
if (printContent) {
print("%s", type);
@@ -239,8 +316,8 @@ public interface EclipseASTVisitor {
}
public void visitField(EclipseNode node, FieldDeclaration field) {
- print("<FIELD%s %s %s = %s%s>", isGenerated(field) ? " (GENERATED)" : "",
- str(field.type), str(field.name), field.initialization, position(node));
+ print("<FIELD%s %s %s = %s%s> %s", isGenerated(field) ? " (GENERATED)" : "",
+ str(field.type), str(field.name), field.initialization, position(node), printFlags(field.modifiers, field));
indent++;
if (printContent) {
if (field.initialization != null) print("%s", field.initialization);
@@ -260,9 +337,13 @@ public interface EclipseASTVisitor {
public void visitMethod(EclipseNode node, AbstractMethodDeclaration method) {
String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD";
- print("<%s %s: %s%s%s>", type, str(method.selector), method.statements != null ? "filled" : "blank",
- isGenerated(method) ? " (GENERATED)" : "", position(node));
+ print("<%s %s: %s%s%s> %s", type, str(method.selector), method.statements != null ? ("filled(" + method.statements.length + ")") : "blank",
+ isGenerated(method) ? " (GENERATED)" : "", position(node), printFlags(method.modifiers, method));
indent++;
+ if (method instanceof ConstructorDeclaration) {
+ ConstructorDeclaration cd = (ConstructorDeclaration) method;
+ print("--> constructorCall: %s", cd.constructorCall == null ? "-NONE-" : cd.constructorCall);
+ }
if (printContent) {
if (method.statements != null) print("%s", method);
disablePrinting++;
@@ -281,7 +362,8 @@ public interface EclipseASTVisitor {
}
public void visitMethodArgument(EclipseNode node, Argument arg, AbstractMethodDeclaration method) {
- print("<METHODARG%s %s %s = %s%s>", isGenerated(arg) ? " (GENERATED)" : "", str(arg.type), str(arg.name), arg.initialization, position(node));
+ print("<METHODARG%s %s %s = %s%s> %s", isGenerated(arg) ? " (GENERATED)" : "",
+ str(arg.type), str(arg.name), arg.initialization, position(node), printFlags(arg.modifiers, arg));
indent++;
}
@@ -295,7 +377,8 @@ public interface EclipseASTVisitor {
}
public void visitLocal(EclipseNode node, LocalDeclaration local) {
- print("<LOCAL%s %s %s = %s%s>", isGenerated(local) ? " (GENERATED)" : "", str(local.type), str(local.name), local.initialization, position(node));
+ print("<LOCAL%s %s %s = %s%s> %s", isGenerated(local) ? " (GENERATED)" : "",
+ str(local.type), str(local.name), local.initialization, position(node), printFlags(local.modifiers, local));
indent++;
}
@@ -310,6 +393,14 @@ public interface EclipseASTVisitor {
public void visitStatement(EclipseNode node, Statement statement) {
print("<%s%s%s>", statement.getClass(), isGenerated(statement) ? " (GENERATED)" : "", position(node));
+ if (statement instanceof AllocationExpression) {
+ AllocationExpression alloc = (AllocationExpression) statement;
+ print(" --> arguments: %s", alloc.arguments == null ? "NULL" : alloc.arguments.length);
+ print(" --> genericTypeArguments: %s", alloc.genericTypeArguments == null ? "NULL" : alloc.genericTypeArguments.length);
+ print(" --> typeArguments: %s", alloc.typeArguments == null ? "NULL" : alloc.typeArguments.length);
+ print(" --> enumConstant: %s", alloc.enumConstant);
+ print(" --> inferredReturnType: %s", alloc.inferredReturnType);
+ }
indent++;
print("%s", statement);
}
@@ -325,5 +416,11 @@ public interface EclipseASTVisitor {
int end = node.get().sourceEnd();
return String.format(" [%d, %d]", start, end);
}
+
+ public boolean isDeferUntilPostDiet() {
+ return false;
+ }
}
+
+ boolean isDeferUntilPostDiet();
}
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 4db1d38d..1738c770 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -44,16 +44,22 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
* Eclipse specific version of the LombokNode class.
*/
public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode, ASTNode> {
+ private EclipseAST ast;
/** {@inheritDoc} */
EclipseNode(EclipseAST ast, ASTNode node, List<EclipseNode> children, Kind kind) {
- super(ast, node, children, kind);
+ super(node, children, kind);
+ this.ast = ast;
}
+ @Override
+ public EclipseAST getAst() {
+ return ast;
+ }
/**
* Visits this node and all child nodes depth-first, calling the provided visitor's visit methods.
*/
public void traverse(EclipseASTVisitor visitor) {
- if (!this.isCompleteParse() && visitor.getClass().isAnnotationPresent(DeferUntilPostDiet.class)) return;
+ if (visitor.isDeferUntilPostDiet() && !isCompleteParse()) return;
switch (getKind()) {
case COMPILATION_UNIT:
diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java
index 07c6f97b..0e72fb38 100644
--- a/src/core/lombok/eclipse/HandlerLibrary.java
+++ b/src/core/lombok/eclipse/HandlerLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -212,21 +212,25 @@ public class HandlerLibrary {
* @param ast The Compilation Unit that contains the Annotation AST Node.
* @param annotationNode The Lombok AST Node representing the Annotation AST Node.
* @param annotation 'node.get()' - convenience parameter.
+ * @param priority current prioritiy
+ * @return the priority we want to run - MAX_VALUE means never
*/
- public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) {
+ public long handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) {
TypeResolver resolver = new TypeResolver(annotationNode.getImportList());
TypeReference rawType = annotation.type;
- if (rawType == null) return;
+ if (rawType == null) return Long.MAX_VALUE;
String fqn = resolver.typeRefToFullyQualifiedName(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()));
- if (fqn == null) return;
+ if (fqn == null) return Long.MAX_VALUE;
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
- if (container == null) return;
- if (priority != container.getPriority()) return;
+ if (container == null) return Long.MAX_VALUE;
+
+ if (priority < container.getPriority()) return container.getPriority(); // we want to run at this priority
+ if (priority > container.getPriority()) return Long.MAX_VALUE; // it's over- we do not want to run again
if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
- return;
+ return Long.MAX_VALUE;
}
try {
@@ -236,13 +240,16 @@ public class HandlerLibrary {
} catch (Throwable t) {
error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
}
+ return Long.MAX_VALUE;
}
/**
* Will call all registered {@link EclipseASTVisitor} instances.
*/
- public void callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) {
+ public long callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) {
+ long nearestPriority = Long.MAX_VALUE;
for (VisitorContainer container : visitorHandlers) {
+ if (priority < container.getPriority()) nearestPriority = Math.min(container.getPriority(), nearestPriority);
if (!isCompleteParse && container.deferUntilPostDiet()) continue;
if (priority != container.getPriority()) continue;
try {
@@ -252,5 +259,6 @@ public class HandlerLibrary {
String.format("Lombok visitor handler %s failed", container.visitor.getClass()), t);
}
}
+ return nearestPriority;
}
}
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index 683465c9..323fc171 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -25,6 +25,8 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.lang.reflect.Field;
+import lombok.ConfigurationKeys;
+import lombok.core.LombokConfiguration;
import lombok.core.debug.DebugSnapshotStore;
import lombok.core.debug.HistogramTracker;
import lombok.patcher.Symbols;
@@ -143,6 +145,8 @@ public class TransformEclipseAST {
// Do NOT abort if (ast.bits & ASTNode.HasAllMethodBodies) != 0 - that doesn't work.
+ if (Boolean.TRUE.equals(LombokConfiguration.read(ConfigurationKeys.LOMBOK_DISABLE, EclipseAST.getAbsoluteFileLocation(ast)))) return;
+
try {
DebugSnapshotStore.INSTANCE.snapshot(ast, "transform entry");
long histoToken = lombokTracker == null ? 0L : lombokTracker.start();
@@ -182,42 +186,54 @@ public class TransformEclipseAST {
* then handles any PrintASTs.
*/
public void go() {
+ long nextPriority = Long.MIN_VALUE;
for (Long d : handlers.getPriorities()) {
- ast.traverse(new AnnotationVisitor(d));
- handlers.callASTVisitors(ast, d, ast.isCompleteParse());
+ if (nextPriority > d) continue;
+ AnnotationVisitor visitor = new AnnotationVisitor(d);
+ ast.traverse(visitor);
+ // if no visitor interested for this AST, nextPriority would be MAX_VALUE and we bail out immediatetly
+ nextPriority = visitor.getNextPriority();
+ nextPriority = Math.min(nextPriority, handlers.callASTVisitors(ast, d, ast.isCompleteParse()));
}
}
private static class AnnotationVisitor extends EclipseASTAdapter {
private final long priority;
+ // this is the next priority we continue to visit.
+ // Long.MAX_VALUE means never. Each visit method will potentially reduce the next priority
+ private long nextPriority = Long.MAX_VALUE;
public AnnotationVisitor(long priority) {
this.priority = priority;
}
+ public long getNextPriority() {
+ return nextPriority;
+ }
+
@Override public void visitAnnotationOnField(FieldDeclaration field, EclipseNode annotationNode, Annotation annotation) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
- handlers.handleAnnotation(top, annotationNode, annotation, priority);
+ nextPriority = Math.min(nextPriority, 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, priority);
+ nextPriority = Math.min(nextPriority, 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, priority);
+ nextPriority = Math.min(nextPriority, 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, priority);
+ nextPriority = Math.min(nextPriority, 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, priority);
+ nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority));
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 2dce285c..87df6d1b 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -24,6 +24,7 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.EclipseAugments.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.setGeneratedBy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -1862,4 +1863,29 @@ public class EclipseHandlerUtil {
String p = typeDecl.superclass.toString();
return p.equals("Object") || p.equals("java.lang.Object");
}
+
+ public static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long)pS << 32 | pE;
+
+ NameReference ref;
+
+ if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE);
+ else ref = new SingleNameReference(varNames[0], p);
+ setGeneratedBy(ref, source);
+ return ref;
+ }
+
+ public static TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long)pS << 32 | pE;
+
+ TypeReference ref;
+
+ long[] poss = Eclipse.poss(source, varNames.length);
+ if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss);
+ else ref = new SingleTypeReference(varNames[0], p);
+ setGeneratedBy(ref, source);
+ return ref;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index e1b1af26..9a069c58 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -40,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -47,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
@@ -146,6 +148,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
+
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
Builder builderInstance = annotation.getInstance();
@@ -490,6 +494,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
}
+ private static final char[] EMPTY_LIST = "emptyList".toCharArray();
private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) {
// return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
@@ -509,19 +514,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (BuilderFieldData bfd : builderFields) {
char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray();
MessageSend ms = new MessageSend();
+ Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
+
if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
- FieldReference fr = new FieldReference(fieldName, 0);
- fr.receiver = new ThisReference(0, 0);
- ms.arguments = new Expression[] {fr};
+ for (int i = 0; i < tgt.length; i++) {
+ FieldReference fr = new FieldReference(fieldName, 0);
+ fr.receiver = new ThisReference(0, 0);
+ tgt[i] = fr;
+ }
} else {
String obtainName = bfd.obtainVia.method();
boolean obtainIsStatic = bfd.obtainVia.isStatic();
- MessageSend obtainExpr = new MessageSend();
- obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
- obtainExpr.selector = obtainName.toCharArray();
- if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
- ms.arguments = new Expression[] {obtainExpr};
+ for (int i = 0; i < tgt.length; i++) {
+ MessageSend obtainExpr = new MessageSend();
+ obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
+ obtainExpr.selector = obtainName.toCharArray();
+ if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
+ tgt[i] = obtainExpr;
+ }
+ }
+ if (bfd.singularData == null) {
+ ms.arguments = tgt;
+ } else {
+ Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+ MessageSend emptyList = new MessageSend();
+ emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
+ emptyList.selector = EMPTY_LIST;
+ ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
}
ms.receiver = receiver;
ms.selector = setterName;
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index c99b9b5f..6945e5d9 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -69,7 +69,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
-import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
@@ -819,29 +818,4 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
expr.sourceStart = pS; expr.sourceEnd = pE;
return expr;
}
-
- public NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
-
- NameReference ref;
-
- if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE);
- else ref = new SingleNameReference(varNames[0], p);
- setGeneratedBy(ref, source);
- return ref;
- }
-
- public TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) {
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
-
- TypeReference ref;
-
- long[] poss = Eclipse.poss(source, varNames.length);
- if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss);
- else ref = new SingleTypeReference(varNames[0], p);
- setGeneratedBy(ref, source);
- return ref;
- }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
index c3a28f7f..15650490 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
@@ -24,32 +24,37 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
+import java.util.ArrayList;
+import java.util.List;
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
-import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.FieldNameConstants;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Clinit;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldNameConstants> {
- public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, String prefix, String suffix) {
+ public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, boolean asEnum, String innerTypeName, boolean onlyExplicit) {
TypeDeclaration typeDecl = null;
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
@@ -57,22 +62,29 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName
boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
if (typeDecl == null || notAClass) {
- errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field.");
+ errorNode.addError("@FieldNameConstants is only supported on a class or an enum.");
return;
}
+ List<EclipseNode> qualified = new ArrayList<EclipseNode>();
+
for (EclipseNode field : typeNode.down()) {
- if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix);
+ if (fieldQualifiesForFieldNameConstantsGeneration(field, onlyExplicit)) qualified.add(field);
+ }
+
+ if (qualified.isEmpty()) {
+ errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing");
+ } else {
+ createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName);
}
}
- private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, String prefix, String suffix) {
- if (hasAnnotation(FieldNameConstants.class, fieldNode)) return;
- createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, pos, false);
- }
-
- private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) {
+ private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field, boolean onlyExplicit) {
if (field.getKind() != Kind.FIELD) return false;
+ if (hasAnnotation(FieldNameConstants.Exclude.class, field)) return false;
+ if (hasAnnotation(FieldNameConstants.Include.class, field)) return true;
+ if (onlyExplicit) return false;
+
FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
return filterField(fieldDecl);
}
@@ -81,55 +93,75 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants");
EclipseNode node = annotationNode.up();
- FieldNameConstants annotatationInstance = annotation.getInstance();
- AccessLevel level = annotatationInstance.level();
- String prefix = annotatationInstance.prefix();
- String suffix = annotatationInstance.suffix();
- if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX);
- if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX);
- if (prefix == null) prefix = "FIELD_";
- if (suffix == null) suffix = "";
- if (node == null) return;
-
- switch (node.getKind()) {
- case FIELD:
- if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true);
- break;
- case TYPE:
- if (level == AccessLevel.NONE) {
- annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE.");
- return;
- }
- generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix);
- break;
- }
- }
-
- private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection<EclipseNode> fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) {
- for (EclipseNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, source, whineIfExists);
- }
-
- private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) {
- if (fieldNode.getKind() != Kind.FIELD) {
- errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field");
+ FieldNameConstants annotationInstance = annotation.getInstance();
+ AccessLevel level = annotationInstance.level();
+ boolean asEnum = annotationInstance.asEnum();
+ boolean usingLombokv1_18_2 = annotation.isExplicit("prefix") || annotation.isExplicit("suffix") || node.getKind() == Kind.FIELD;
+
+ if (usingLombokv1_18_2) {
+ annotationNode.addError("@FieldNameConstants has been redesigned in lombok v1.18.4; please upgrade your project dependency on lombok. See https://projectlombok.org/features/experimental/FieldNameConstants for more information.");
return;
}
- FieldDeclaration field = (FieldDeclaration) fieldNode.get();
- String fieldName = new String(field.name);
- String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix;
- if (constantName.equals(fieldName)) {
- fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field.");
+ if (level == AccessLevel.NONE) {
+ annotationNode.addWarning("AccessLevel.NONE is not compatible with @FieldNameConstants. If you don't want the inner type, simply remove FieldNameConstants.");
return;
}
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long) pS << 32 | pE;
- FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE);
- fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL;
- fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p,p,p});
- fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0);
- injectField(fieldNode.up(), fieldConstant);
+ String innerTypeName = annotationInstance.innerTypeName();
+ if (innerTypeName.isEmpty()) innerTypeName = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_INNER_TYPE_NAME);
+ if (innerTypeName == null || innerTypeName.isEmpty()) innerTypeName = "Fields";
+
+ generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded());
+ }
+
+ private void createInnerTypeFieldNameConstants(EclipseNode typeNode, ASTNode source, AccessLevel level, List<EclipseNode> fields, boolean asEnum, String innerTypeName) {
+ if (fields.isEmpty()) return;
+
+ TypeDeclaration parent = (TypeDeclaration) typeNode.get();
+ TypeDeclaration innerType = new TypeDeclaration(parent.compilationResult);
+ innerType.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ innerType.modifiers = toEclipseModifier(level) | (asEnum ? ClassFileConstants.AccEnum : ClassFileConstants.AccStatic | ClassFileConstants.AccFinal);
+ char[] name = innerTypeName.toCharArray();
+ innerType.name = name;
+ innerType.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ EclipseNode innerNode = injectType(typeNode, innerType);
+
+ ConstructorDeclaration constructor = new ConstructorDeclaration(parent.compilationResult);
+ constructor.selector = name;
+ constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
+ constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
+ constructor.modifiers = ClassFileConstants.AccPrivate;
+ ExplicitConstructorCall superCall = new ExplicitConstructorCall(0);
+ superCall.sourceStart = source.sourceStart;
+ superCall.sourceEnd = source.sourceEnd;
+ superCall.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ constructor.constructorCall = superCall;
+ if (!asEnum) constructor.statements = new Statement[0];
+
+ injectMethod(innerNode, constructor);
+
+ if (asEnum) injectMethod(innerNode, new Clinit(parent.compilationResult));
+
+ for (EclipseNode fieldNode : fields) {
+ FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ char[] fName = field.name;
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+ FieldDeclaration fieldConstant = new FieldDeclaration(fName, pS, pE);
+ fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fieldConstant.modifiers = asEnum ? 0 : ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal;
+ fieldConstant.type = asEnum ? null : new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p});
+ if (asEnum) {
+ AllocationExpression ac = new AllocationExpression();
+ ac.enumConstant = fieldConstant;
+ ac.sourceStart = source.sourceStart;
+ ac.sourceEnd = source.sourceEnd;
+ fieldConstant.initialization = ac;
+ } else {
+ fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0);
+ }
+ injectField(innerNode, fieldConstant);
+ }
}
}