aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/eclipse
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok/eclipse')
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java6
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java71
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java282
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java65
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java168
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilderDefault.java48
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java124
-rw-r--r--src/core/lombok/eclipse/handlers/HandleData.java17
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java264
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java8
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java135
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java24
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java36
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java72
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSuperBuilder.java836
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java142
-rw-r--r--src/core/lombok/eclipse/handlers/HandleUtilityClass.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java40
-rw-r--r--src/core/lombok/eclipse/handlers/HandleValue.java17
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java5
-rw-r--r--src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java5
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java39
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java34
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java11
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java43
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java8
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java28
27 files changed, 1992 insertions, 538 deletions
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 6741b33a..dc2c9843 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -62,7 +62,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* @param ast The compilation unit, which serves as the top level node in the tree to be built.
*/
public EclipseAST(CompilationUnitDeclaration ast) {
- super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast));
+ super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast), statementTypes());
this.compilationUnitDeclaration = ast;
setTop(buildCompilationUnit(ast));
this.completeParse = isComplete(ast);
@@ -477,9 +477,9 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
return putInMap(new EclipseNode(this, statement, childNodes, Kind.STATEMENT));
}
- /** For Eclipse, only Statement counts, as Expression is a subclass of it, even though this isn't
+ /* For Eclipse, only Statement counts, as Expression is a subclass of it, even though this isn't
* entirely correct according to the JLS spec (only some expressions can be used as statements, not all of them). */
- @Override protected Collection<Class<? extends ASTNode>> getStatementTypes() {
+ private static Collection<Class<? extends ASTNode>> statementTypes() {
return Collections.<Class<? extends ASTNode>>singleton(Statement.class);
}
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 49867e62..4db1d38d 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -23,7 +23,9 @@ package lombok.eclipse;
import java.util.List;
+import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -36,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
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.classfmt.ClassFileConstants;
/**
* Eclipse specific version of the LombokNode class.
@@ -184,4 +187,72 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
public boolean isCompleteParse() {
return ast.isCompleteParse();
}
+
+ @Override public boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type) {
+ return EclipseHandlerUtil.hasAnnotation(type, this);
+ }
+
+ @Override public <Z extends java.lang.annotation.Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type) {
+ EclipseNode annotation = EclipseHandlerUtil.findAnnotation(type, this);
+ if (annotation == null) return null;
+ return EclipseHandlerUtil.createAnnotation(type, annotation);
+ }
+
+ private Integer getModifiers() {
+ if (node instanceof TypeDeclaration) return ((TypeDeclaration) node).modifiers;
+ if (node instanceof FieldDeclaration) return ((FieldDeclaration) node).modifiers;
+ if (node instanceof LocalDeclaration) return ((LocalDeclaration) node).modifiers;
+ if (node instanceof AbstractMethodDeclaration) return ((AbstractMethodDeclaration) node).modifiers;
+
+ return null;
+ }
+
+ @Override public boolean isStatic() {
+ if (node instanceof TypeDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp == null || directUp.getKind() == Kind.COMPILATION_UNIT) return true;
+ if (!(directUp.get() instanceof TypeDeclaration)) return false;
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ if ((ClassFileConstants.AccEnum & f) != 0) return true;
+ }
+
+ if (node instanceof FieldDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp != null && directUp.get() instanceof TypeDeclaration) {
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ }
+ }
+
+ Integer i = getModifiers();
+ if (i == null) return false;
+ int f = i.intValue();
+ return (ClassFileConstants.AccStatic & f) != 0;
+ }
+
+ @Override public boolean isTransient() {
+ if (getKind() != Kind.FIELD) return false;
+ Integer i = getModifiers();
+ return i != null && (i.intValue() & ClassFileConstants.AccTransient) != 0;
+ }
+
+ @Override public boolean isEnumMember() {
+ if (getKind() != Kind.FIELD) return false;
+ return ((FieldDeclaration) node).getKind() == 3;
+ }
+
+ @Override public int countMethodParameters() {
+ if (getKind() != Kind.METHOD) return 0;
+
+ Argument[] a = ((AbstractMethodDeclaration) node).arguments;
+ if (a == null) return 0;
+ return a.length;
+ }
+
+ @Override public int getStartPos() {
+ return node.sourceStart;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 0ff5a7f6..2dce285c 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 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
@@ -167,6 +167,7 @@ public class EclipseHandlerUtil {
}
public static boolean isFieldDeprecated(EclipseNode fieldNode) {
+ if (!(fieldNode.get() instanceof FieldDeclaration)) return false;
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
if ((field.modifiers & ClassFileConstants.AccDeprecated) != 0) {
return true;
@@ -196,17 +197,36 @@ public class EclipseHandlerUtil {
TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
+ }
+
+ /**
+ * Checks if the given TypeReference node is likely to be a reference to the provided class.
+ *
+ * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
+ * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements).
+ * @param typeRef A type reference to check.
+ */
+ public static boolean typeMatches(String type, EclipseNode node, TypeReference typeRef) {
+ if (typeRef == null || typeRef.getTypeName() == null || typeRef.getTypeName().length == 0) return false;
+ String lastPartA = new String(typeRef.getTypeName()[typeRef.getTypeName().length -1]);
+ int lastIndex = type.lastIndexOf('.');
+ String lastPartB = lastIndex == -1 ? type : type.substring(lastIndex + 1);
+ if (!lastPartA.equals(lastPartB)) return false;
+ String typeName = toQualifiedName(typeRef.getTypeName());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
+ return resolver.typeMatches(node, type, typeName);
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
List<String> disallowed = null;
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.ANNOTATION) continue;
- for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ for (String annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
if (disallowed == null) disallowed = new ArrayList<String>();
- disallowed.add(annType.getSimpleName());
+ int lastIndex = annType.lastIndexOf('.');
+ disallowed.add(lastIndex == -1 ? annType : annType.substring(lastIndex + 1));
}
}
}
@@ -443,6 +463,42 @@ public class EclipseHandlerUtil {
}
}
+ public static boolean hasAnnotation(String type, EclipseNode node) {
+ if (node == null) return false;
+ if (type == null) return false;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) return true;
+ }
+ // intentional fallthrough
+ default:
+ return false;
+ }
+ }
+
+ public static EclipseNode findAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
+ if (node == null) return null;
+ if (type == null) return null;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) return child;
+ }
+ // intentional fallthrough
+ default:
+ return null;
+ }
+ }
+
/**
* Checks if the provided annotation type is likely to be the intended type for the given annotation node.
*
@@ -453,6 +509,16 @@ public class EclipseHandlerUtil {
return typeMatches(type, node, ((Annotation) node.get()).type);
}
+ /**
+ * Checks if the provided annotation type is likely to be the intended type for the given annotation node.
+ *
+ * This is a guess, but a decent one.
+ */
+ public static boolean annotationTypeMatches(String type, EclipseNode node) {
+ if (node.getKind() != Kind.ANNOTATION) return false;
+ return typeMatches(type, node, ((Annotation) node.get()).type);
+ }
+
public static TypeReference cloneSelfType(EclipseNode context) {
return cloneSelfType(context, null);
}
@@ -680,7 +746,7 @@ public class EclipseHandlerUtil {
* Provides AnnotationValues with the data it needs to do its thing.
*/
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
- createAnnotation(Class<A> type, final EclipseNode annotationNode) {
+ createAnnotation(Class<A> type, final EclipseNode annotationNode) {
final Annotation annotation = (Annotation) annotationNode.get();
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
@@ -697,7 +763,7 @@ public class EclipseHandlerUtil {
String mName = (n == null || n.length == 0) ? "value" : new String(pair.name);
final Expression rhs = pair.value;
if (rhs instanceof ArrayInitializer) {
- expressions = ((ArrayInitializer)rhs).expressions;
+ expressions = ((ArrayInitializer) rhs).expressions;
} else if (rhs != null) {
expressions = new Expression[] { rhs };
}
@@ -831,7 +897,7 @@ public class EclipseHandlerUtil {
// Check if the class has a @Getter annotation.
- if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) {
+ if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) {
//Check if the class has @Getter or @Data annotation.
EclipseNode containingType = field.up();
@@ -854,10 +920,6 @@ public class EclipseHandlerUtil {
return null;
}
- public enum FieldAccess {
- GETTER, PREFER_FIELD, ALWAYS_FIELD;
- }
-
static boolean lookForGetter(EclipseNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
@@ -874,11 +936,13 @@ public class EclipseHandlerUtil {
}
static TypeReference getFieldType(EclipseNode field, FieldAccess fieldAccess) {
+ if (field.get() instanceof MethodDeclaration) return ((MethodDeclaration) field.get()).returnType;
+
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
- return ((FieldDeclaration)field.get()).type;
+ return ((FieldDeclaration) field.get()).type;
}
return getter.type;
@@ -886,7 +950,7 @@ public class EclipseHandlerUtil {
static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
boolean lookForGetter = lookForGetter(field, fieldAccess);
@@ -954,6 +1018,43 @@ public class EclipseHandlerUtil {
return call;
}
+ static Expression createMethodAccessor(EclipseNode method, ASTNode source) {
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration methodDecl = (MethodDeclaration) method.get();
+ MessageSend call = new MessageSend();
+ setGeneratedBy(call, source);
+ call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE;
+ if ((methodDecl.modifiers & ClassFileConstants.AccStatic) == 0) {
+ call.receiver = new ThisReference(pS, pE);
+ setGeneratedBy(call.receiver, source);
+ } else {
+ EclipseNode containerNode = method.up();
+ if (containerNode != null && containerNode.get() instanceof TypeDeclaration) {
+ call.receiver = new SingleNameReference(((TypeDeclaration) containerNode.get()).name, p);
+ setGeneratedBy(call.receiver, source);
+ }
+ }
+
+ call.selector = methodDecl.selector;
+ return call;
+ }
+
+ static Expression createMethodAccessor(EclipseNode method, ASTNode source, char[] receiver) {
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration methodDecl = (MethodDeclaration) method.get();
+ MessageSend call = new MessageSend();
+ setGeneratedBy(call, source);
+ call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE;
+ call.receiver = new SingleNameReference(receiver, p);
+ setGeneratedBy(call.receiver, source);
+ call.selector = methodDecl.selector;
+ return call;
+ }
+
/** Serves as return value for the methods that check for the existence of fields and methods. */
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
@@ -1031,7 +1132,7 @@ public class EclipseHandlerUtil {
public static boolean filterField(FieldDeclaration declaration, boolean skipStatic) {
// Skip the fake fields that represent enum constants.
if (declaration.initialization instanceof AllocationExpression &&
- ((AllocationExpression)declaration.initialization).enumConstant != null) return false;
+ ((AllocationExpression) declaration.initialization).enumConstant != null) return false;
if (declaration.type == null) return false;
@@ -1097,6 +1198,12 @@ public class EclipseHandlerUtil {
return AnnotationValues.of(Accessors.class, field);
}
+
+ public static EclipseNode upToTypeNode(EclipseNode node) {
+ if (node == null) throw new NullPointerException("node");
+ while (node != null && !(node.get() instanceof TypeDeclaration)) node = node.up();
+ return node;
+ }
/**
* Checks if there is a field with the provided name.
@@ -1105,10 +1212,7 @@ public class EclipseHandlerUtil {
* @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof.
*/
public static MemberExistsResult fieldExists(String fieldName, EclipseNode node) {
- while (node != null && !(node.get() instanceof TypeDeclaration)) {
- node = node.up();
- }
-
+ node = upToTypeNode(node);
if (node != null && node.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration)node.get();
if (typeDecl.fields != null) for (FieldDeclaration def : typeDecl.fields) {
@@ -1168,9 +1272,8 @@ public class EclipseHandlerUtil {
if (params < minArgs || params > maxArgs) continue;
}
- if (def.annotations != null) for (Annotation anno : def.annotations) {
- if (typeMatches(Tolerate.class, node, anno.type)) continue top;
- }
+
+ if (isTolerate(node, def)) continue top;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
@@ -1181,6 +1284,13 @@ public class EclipseHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isTolerate(EclipseNode node, AbstractMethodDeclaration def) {
+ if (def.annotations != null) for (Annotation anno : def.annotations) {
+ if (typeMatches(Tolerate.class, node, anno.type)) return true;
+ }
+ return false;
+ }
+
/**
* Checks if there is a (non-default) constructor. In case of multiple constructors (overloading), only
* the first constructor decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned.
@@ -1188,19 +1298,14 @@ public class EclipseHandlerUtil {
* @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof.
*/
public static MemberExistsResult constructorExists(EclipseNode node) {
- while (node != null && !(node.get() instanceof TypeDeclaration)) {
- node = node.up();
- }
-
+ node = upToTypeNode(node);
if (node != null && node.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration)node.get();
- if (typeDecl.methods != null) top: for (AbstractMethodDeclaration def : typeDecl.methods) {
+ if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) {
if (def instanceof ConstructorDeclaration) {
if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue;
- if (def.annotations != null) for (Annotation anno : def.annotations) {
- if (typeMatches(Tolerate.class, node, anno.type)) continue top;
- }
+ if (isTolerate(node, def)) continue;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
@@ -1325,6 +1430,7 @@ public class EclipseHandlerUtil {
private static final char[] GENERATED_CODE = "generated code".toCharArray();
private static final char[] LOMBOK = "lombok".toCharArray();
private static final char[][] JAVAX_ANNOTATION_GENERATED = Eclipse.fromQualifiedName("javax.annotation.Generated");
+ private static final char[][] LOMBOK_GENERATED = Eclipse.fromQualifiedName("lombok.Generated");
private static final char[][] EDU_UMD_CS_FINDBUGS_ANNOTATIONS_SUPPRESSFBWARNINGS = Eclipse.fromQualifiedName("edu.umd.cs.findbugs.annotations.SuppressFBWarnings");
public static Annotation[] addSuppressWarningsAll(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
@@ -1339,24 +1445,29 @@ public class EclipseHandlerUtil {
}
public static Annotation[] addGenerated(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
- if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS))) return originalAnnotationArray;
- return addAnnotation(source, originalAnnotationArray, JAVAX_ANNOTATION_GENERATED, new StringLiteral(LOMBOK, 0, 0, 0));
+ Annotation[] result = originalAnnotationArray;
+ if (HandlerUtil.shouldAddGenerated(node)) {
+ result = addAnnotation(source, result, JAVAX_ANNOTATION_GENERATED, new StringLiteral(LOMBOK, 0, 0, 0));
+ }
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_LOMBOK_GENERATED_ANNOTATIONS))) {
+ result = addAnnotation(source, result, LOMBOK_GENERATED, null);
+ }
+ return result;
}
private static Annotation[] addAnnotation(ASTNode source, Annotation[] originalAnnotationArray, char[][] annotationTypeFqn, ASTNode arg) {
char[] simpleName = annotationTypeFqn[annotationTypeFqn.length - 1];
if (originalAnnotationArray != null) for (Annotation ann : originalAnnotationArray) {
- char[] lastToken = null;
-
if (ann.type instanceof QualifiedTypeReference) {
char[][] t = ((QualifiedTypeReference) ann.type).tokens;
- lastToken = t[t.length - 1];
- } else if (ann.type instanceof SingleTypeReference) {
- lastToken = ((SingleTypeReference) ann.type).token;
+ if (Arrays.deepEquals(t, annotationTypeFqn)) return originalAnnotationArray;
}
- if (lastToken != null && Arrays.equals(simpleName, lastToken)) return originalAnnotationArray;
+ if (ann.type instanceof SingleTypeReference) {
+ char[] lastToken = ((SingleTypeReference) ann.type).token;
+ if (Arrays.equals(lastToken, simpleName)) return originalAnnotationArray;
+ }
}
int pS = source.sourceStart, pE = source.sourceEnd;
@@ -1606,6 +1717,14 @@ public class EclipseHandlerUtil {
return true;
}
+ public static void addError(String errorName, EclipseNode node) {
+ if (node.getLatestJavaSpecSupported() < 8) {
+ node.addError("The correct format is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})");
+ } else {
+ node.addError("The correct format is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ }
+ }
+
public static List<Annotation> unboxAndRemoveAnnotationParameter(Annotation annotation, String annotationName, String errorName, EclipseNode errorNode) {
if ("value".equals(annotationName)) {
// We can't unbox this, because SingleMemberAnnotation REQUIRES a value, and this method
@@ -1628,51 +1747,70 @@ public class EclipseHandlerUtil {
char[] nameAsCharArray = annotationName.toCharArray();
+ top:
for (int i = 0; i < pairs.length; i++) {
- if (pairs[i].name == null || !Arrays.equals(nameAsCharArray, pairs[i].name)) continue;
+ boolean allowRaw;
+ char[] name = pairs[i].name;
+ if (name == null) continue;
+ if (name.length < nameAsCharArray.length) continue;
+ for (int j = 0; j < nameAsCharArray.length; j++) {
+ if (name[j] != nameAsCharArray[j]) continue top;
+ }
+ allowRaw = name.length > nameAsCharArray.length;
+ for (int j = nameAsCharArray.length; j < name.length; j++) {
+ if (name[j] != '_') continue top;
+ }
+ // If we're still here it's the targeted annotation param.
Expression value = pairs[i].value;
MemberValuePair[] newPairs = new MemberValuePair[pairs.length - 1];
if (i > 0) System.arraycopy(pairs, 0, newPairs, 0, i);
if (i < pairs.length - 1) System.arraycopy(pairs, i + 1, newPairs, i, pairs.length - i - 1);
normalAnnotation.memberValuePairs = newPairs;
- // We have now removed the annotation parameter and stored '@__({... annotations ...})',
- // which we must now unbox.
- if (!(value instanceof Annotation)) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- return Collections.emptyList();
- }
-
- Annotation atDummyIdentifier = (Annotation) value;
- if (!(atDummyIdentifier.type instanceof SingleTypeReference) ||
- !isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- return Collections.emptyList();
- }
-
- if (atDummyIdentifier instanceof MarkerAnnotation) {
- // It's @Getter(onMethod=@__). This is weird, but fine.
- return Collections.emptyList();
- }
+ // We have now removed the annotation parameter and stored the value,
+ // which we must now unbox. It's either annotations, or @__(annotations).
Expression content = null;
- if (atDummyIdentifier instanceof NormalAnnotation) {
- MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs;
- if (mvps == null || mvps.length == 0) {
- // It's @Getter(onMethod=@__()). This is weird, but fine.
+ if (value instanceof ArrayInitializer) {
+ if (!allowRaw) {
+ addError(errorName, errorNode);
return Collections.emptyList();
}
- if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) {
- content = mvps[0].value;
+ content = value;
+ } else if (!(value instanceof Annotation)) {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ } else {
+ Annotation atDummyIdentifier = (Annotation) value;
+ if (atDummyIdentifier.type instanceof SingleTypeReference && isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) {
+ if (atDummyIdentifier instanceof MarkerAnnotation) {
+ return Collections.emptyList();
+ } else if (atDummyIdentifier instanceof NormalAnnotation) {
+ MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs;
+ if (mvps == null || mvps.length == 0) {
+ return Collections.emptyList();
+ }
+ if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) {
+ content = mvps[0].value;
+ }
+ } else if (atDummyIdentifier instanceof SingleMemberAnnotation) {
+ content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue;
+ } else {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ }
+ } else {
+ if (allowRaw) {
+ content = atDummyIdentifier;
+ } else {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ }
}
}
- if (atDummyIdentifier instanceof SingleMemberAnnotation) {
- content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue;
- }
-
if (content == null) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
@@ -1684,13 +1822,13 @@ public class EclipseHandlerUtil {
if (expressions != null) for (Expression ex : expressions) {
if (ex instanceof Annotation) result.add((Annotation) ex);
else {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
}
return result;
} else {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
}
@@ -1716,4 +1854,12 @@ public class EclipseHandlerUtil {
private static long[] copy(long[] array) {
return array == null ? null : array.clone();
}
+
+ public static boolean isDirectDescendantOfObject(EclipseNode typeNode) {
+ if (!(typeNode.get() instanceof TypeDeclaration)) throw new IllegalArgumentException("not a type node");
+ TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get();
+ if (typeDecl.superclass == null) return true;
+ String p = typeDecl.superclass.toString();
+ return p.equals("Object") || p.equals("java.lang.Object");
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
index 4cb41d4f..b9b9e07d 100644
--- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -45,6 +45,9 @@ 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.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Reference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
@@ -53,6 +56,7 @@ import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import lombok.core.LombokImmutableList;
import lombok.core.SpiLoadUtil;
@@ -60,6 +64,14 @@ import lombok.core.TypeLibrary;
import lombok.eclipse.EclipseNode;
public class EclipseSingularsRecipes {
+ public interface TypeReferenceMaker {
+ TypeReference make();
+ }
+
+ public interface StatementMaker {
+ Statement make();
+ }
+
private static final EclipseSingularsRecipes INSTANCE = new EclipseSingularsRecipes();
private final Map<String, EclipseSingularizer> singularizers = new HashMap<String, EclipseSingularizer>();
private final TypeLibrary singularizableTypes = new TypeLibrary();
@@ -132,6 +144,10 @@ public class EclipseSingularsRecipes {
}
}
+ public ASTNode getSource() {
+ return source;
+ }
+
public EclipseNode getAnnotation() {
return annotation;
}
@@ -211,8 +227,36 @@ public class EclipseSingularsRecipes {
}
public abstract List<EclipseNode> generateFields(SingularData data, EclipseNode builderType);
- public abstract void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain);
- public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName);
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code builderType} as return type if {@code chain == true}, {@code void} otherwise.
+ * If you need more control over the return type and value, use
+ * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}.
+ */
+ public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain) {
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
+ return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ }
+ };
+
+ StatementMaker returnStatementMaker = new StatementMaker() {
+ @Override public ReturnStatement make() {
+ return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ }
+ };
+
+ generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
+ }
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods.
+ */
+ public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker);
+
+ public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable);
public boolean requiresCleaning() {
try {
@@ -303,16 +347,16 @@ public class EclipseSingularsRecipes {
private static final char[] SIZE_TEXT = new char[] {'s', 'i', 'z', 'e'};
/** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
- protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard) {
+ protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard, String builderVariable) {
MessageSend invoke = new MessageSend();
- ThisReference thisRef = new ThisReference(0, 0);
+ Reference thisRef = getBuilderReference(builderVariable);
FieldReference thisDotName = new FieldReference(name, 0L);
thisDotName.receiver = thisRef;
invoke.receiver = thisDotName;
invoke.selector = SIZE_TEXT;
if (!nullGuard) return invoke;
- ThisReference cdnThisRef = new ThisReference(0, 0);
+ Reference cdnThisRef = getBuilderReference(builderVariable);
FieldReference cdnThisDotName = new FieldReference(name, 0L);
cdnThisDotName.receiver = cdnThisRef;
NullLiteral nullLiteral = new NullLiteral(0, 0);
@@ -341,5 +385,14 @@ public class EclipseSingularsRecipes {
return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS);
}
+
+ /** @return a {@code SingleNameReference} to the builder in the variable <code>builderVariable</code>. If {@ code builderVariable == "this"}, a {@code ThisReference} is returned. */
+ protected static Reference getBuilderReference(String builderVariable) {
+ if ("this".equals(builderVariable)) {
+ return new ThisReference(0, 0);
+ } else {
+ return new SingleNameReference(builderVariable.toCharArray(), 0);
+ }
+ }
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 4e0c4218..e1b1af26 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -38,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
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.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
@@ -73,8 +74,10 @@ import lombok.Builder;
import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
+import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
@@ -88,6 +91,8 @@ import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
@@ -102,9 +107,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
TypeReference type;
char[] rawName;
char[] name;
+ char[] nameOfDefaultProvider;
+ char[] nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
EclipseNode obtainViaNode;
+ EclipseNode originalFieldNode;
List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
}
@@ -127,6 +135,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return true;
}
+ private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'};
+ private static final char[] SET_PREFIX = {'$', 's', 'e', 't'};
+
+ private static final char[] prefixWith(char[] prefix, char[] name) {
+ char[] out = new char[prefix.length + name.length];
+ System.arraycopy(prefix, 0, out, 0, prefix.length);
+ System.arraycopy(name, 0, out, prefix.length, name.length);
+ return out;
+ }
+
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
@@ -171,25 +189,47 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
TypeDeclaration td = (TypeDeclaration) tdParent.get();
List<EclipseNode> allFields = new ArrayList<EclipseNode>();
- @SuppressWarnings("deprecation")
- boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
- for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));
+ for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
- // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
- // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
- // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
- if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
+ EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode);
+ boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
+
BuilderFieldData bfd = new BuilderFieldData();
bfd.rawName = fieldNode.getName().toCharArray();
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast);
+ bfd.originalFieldNode = fieldNode;
+
+ if (bfd.singularData != null && isDefault != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ }
+
+ if (fd.initialization == null && isDefault != null) {
+ isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
+ isDefault = null;
+ }
+
+ if (fd.initialization != null && isDefault == null) {
+ if (isFinal) continue;
+ fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
+ }
+
+ if (isDefault != null) {
+ bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
+ bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
+
+ MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.add(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null,
+ handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER,
Collections.<Annotation>emptyList(), annotationNode);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
@@ -334,6 +374,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
bfd.name = arg.name;
bfd.type = arg.type;
bfd.singularData = getSingularData(param, ast);
+ bfd.originalFieldNode = param;
addObtainVia(bfd, param);
builderFields.add(bfd);
}
@@ -395,7 +436,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
ConstructorDeclaration cd = HandleConstructor.createConstructor(
- AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, null,
+ AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false,
annotationNode, Collections.<Annotation>emptyList());
if (cd != null) injectMethod(builderType, cd);
}
@@ -405,14 +446,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
+ MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
- List<EclipseNode> fieldNodes = new ArrayList<EclipseNode>();
+ List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
+ for (EclipseNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true));
+ }
}
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
@@ -514,7 +557,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return decl;
}
- public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
+ public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
List<Statement> statements = new ArrayList<Statement>();
@@ -530,13 +573,27 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
- bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name);
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this");
}
}
List<Expression> args = new ArrayList<Expression>();
for (BuilderFieldData bfd : builderFields) {
- args.add(new SingleNameReference(bfd.name, 0L));
+ if (bfd.nameOfSetFlag != null) {
+ MessageSend inv = new MessageSend();
+ inv.sourceStart = source.sourceStart;
+ inv.sourceEnd = source.sourceEnd;
+ inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L);
+ inv.selector = bfd.nameOfDefaultProvider;
+ inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters);
+
+ args.add(new ConditionalExpression(
+ new SingleNameReference(bfd.nameOfSetFlag, 0L),
+ new SingleNameReference(bfd.name, 0L),
+ inv));
+ } else {
+ args.add(new SingleNameReference(bfd.name, 0L));
+ }
}
if (addCleaning) {
@@ -563,14 +620,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
else
invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0);
- TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
- if (tps != null) {
- TypeReference[] trs = new TypeReference[tps.length];
- for (int i = 0; i < trs.length; i++) {
- trs[i] = new SingleTypeReference(tps[i].name, 0);
- }
- invoke.typeArguments = trs;
- }
+
+ invoke.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters);
invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]);
if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) {
statements.add(invoke);
@@ -583,6 +634,33 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return out;
}
+ private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) {
+ if (typeParameters == null) return null;
+
+ TypeReference[] trs = new TypeReference[typeParameters.length];
+ for (int i = 0; i < trs.length; i++) {
+ trs[i] = new SingleTypeReference(typeParameters[i].name, 0);
+ }
+ return trs;
+ }
+
+ public static MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult);
+ out.typeParameters = copyTypeParams(typeParameters, source);
+ out.selector = methodName;
+ out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ out.returnType = copyType(fd.type, source);
+ out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)};
+ fd.initialization = null;
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope);
+ return out;
+ }
+
public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
@@ -608,25 +686,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- top:
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType));
} else {
+ EclipseNode field = null, setFlag = null;
for (EclipseNode exists : existing) {
char[] n = ((FieldDeclaration) exists.get()).name;
- if (Arrays.equals(n, bfd.name)) {
- bfd.createdFields.add(exists);
- continue top;
- }
+ if (Arrays.equals(n, bfd.name)) field = exists;
+ if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists;
}
- FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
- fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- fd.modifiers = ClassFileConstants.AccPrivate;
- fd.type = copyType(bfd.type);
- fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
- bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, fd));
+ if (field == null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = copyType(bfd.type);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ field = injectFieldAndMarkGenerated(builderType, fd);
+ }
+ if (setFlag == null && bfd.nameOfSetFlag != null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ injectFieldAndMarkGenerated(builderType, fd);
+ }
+ bfd.createdFields.add(field);
}
}
}
@@ -634,14 +721,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
private static final AbstractMethodDeclaration[] EMPTY = {};
public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
- makeSimpleSetterMethodForBuilder(builderType, bfd.createdFields.get(0), sourceNode, fluent, chain);
+ makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain);
} else {
- bfd.singularData.getSingularizer().generateMethods(bfd.singularData, builderType, fluent, chain);
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain);
}
}
- private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
@@ -652,12 +740,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (int i = 0; i < len; i++) {
if (!(existing[i] instanceof MethodDeclaration)) continue;
char[] existingName = existing[i].selector;
- if (Arrays.equals(name, existingName)) return;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
}
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic,
sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
injectMethod(builderType, setter);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
new file mode 100644
index 00000000..d0c597fd
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017-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
+ * 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.eclipse.handlers;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.Builder;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.experimental.SuperBuilder;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+@HandlerPriority(-1025) //HandleBuilder's level, minus one.
+public class HandleBuilderDefault extends EclipseAnnotationHandler<Builder.Default> {
+ @Override public void handle(AnnotationValues<Builder.Default> annotation, Annotation ast, EclipseNode annotationNode) {
+ EclipseNode annotatedField = annotationNode.up();
+ if (annotatedField.getKind() != Kind.FIELD) return;
+ EclipseNode classWithAnnotatedField = annotatedField.up();
+ if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)
+ && !hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) {
+ annotationNode.addWarning("@Builder.Default requires @Builder or @SuperBuilder on the class for it to mean anything.");
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index 85d1d4ed..cab847e6 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2015 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -42,12 +42,15 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
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.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -79,6 +82,8 @@ import org.mangosdk.spi.ProviderFor;
public class HandleConstructor {
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleNoArgsConstructor extends EclipseAnnotationHandler<NoArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -92,14 +97,16 @@ public class HandleConstructor {
boolean force = ann.force();
List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode);
+ handleConstructor.generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode);
}
}
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleRequiredArgsConstructor extends EclipseAnnotationHandler<RequiredArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -109,18 +116,15 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(
+ handleConstructor.generateConstructor(
typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ onConstructor, annotationNode);
}
}
@@ -146,14 +150,17 @@ public class HandleConstructor {
}
static List<EclipseNode> findAllFields(EclipseNode typeNode) {
+ return findAllFields(typeNode, false);
+ }
+
+ static List<EclipseNode> findAllFields(EclipseNode typeNode, boolean evenFinalInitialized) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (!filterField(fieldDecl)) continue;
- // Skip initialized final fields.
- if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue;
+ if (!evenFinalInitialized && ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue;
fields.add(child);
}
@@ -162,6 +169,8 @@ public class HandleConstructor {
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleAllArgsConstructor extends EclipseAnnotationHandler<AllArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -171,18 +180,15 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(
+ handleConstructor.generateConstructor(
typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ onConstructor, annotationNode);
}
}
@@ -200,28 +206,45 @@ public class HandleConstructor {
return true;
}
+ public enum SkipIfConstructorExists {
+ YES, NO, I_AM_BUILDER;
+ }
+
+ public void generateExtraNoArgsConstructor(EclipseNode typeNode, EclipseNode sourceNode) {
+ if (!isDirectDescendantOfObject(typeNode)) return;
+
+ Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE);
+ if (v == null || !v) return;
+
+ List<EclipseNode> fields = findFinalFields(typeNode);
+ generate(typeNode, AccessLevel.PRIVATE, fields, true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true);
+ }
+
public void generateRequiredArgsConstructor(
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
+ generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode);
}
public void generateAllArgsConstructor(
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
- }
-
- public enum SkipIfConstructorExists {
- YES, NO, I_AM_BUILDER;
+ generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode);
}
public void generateConstructor(
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
- Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
+ List<Annotation> onConstructor, EclipseNode sourceNode) {
+ generate(typeNode, level, fields, allToDefault, staticName, skipIfConstructorExists, onConstructor, sourceNode, false);
+ }
+
+ public void generate(
+ EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
+ List<Annotation> onConstructor, EclipseNode sourceNode, boolean noArgs) {
+
ASTNode source = sourceNode.get();
boolean staticConstrRequired = staticName != null && !staticName.equals("");
@@ -253,9 +276,11 @@ public class HandleConstructor {
}
}
+ if (noArgs && noArgsConstructorExists(typeNode)) return;
+
ConstructorDeclaration constr = createConstructor(
staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault,
- suppressConstructorProperties, sourceNode, onConstructor);
+ sourceNode, onConstructor);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source);
@@ -263,6 +288,28 @@ public class HandleConstructor {
}
}
+ private static boolean noArgsConstructorExists(EclipseNode node) {
+ node = EclipseHandlerUtil.upToTypeNode(node);
+
+ if (node != null && node.get() instanceof TypeDeclaration) {
+ TypeDeclaration typeDecl = (TypeDeclaration)node.get();
+ if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) {
+ if (def instanceof ConstructorDeclaration) {
+ Argument[] arguments = ((ConstructorDeclaration) def).arguments;
+ if (arguments == null || arguments.length == 0) return true;
+ }
+ }
+ }
+
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(NoArgsConstructor.class, child)) return true;
+ if (annotationTypeMatches(RequiredArgsConstructor.class, child) && findRequiredFields(node).isEmpty()) return true;
+ if (annotationTypeMatches(AllArgsConstructor.class, child) && findAllFields(node).isEmpty()) return true;
+ }
+
+ return false;
+ }
+
private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() };
public static Annotation[] createConstructorProperties(ASTNode source, Collection<EclipseNode> fields) {
if (fields.isEmpty()) return null;
@@ -295,9 +342,9 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
- public static ConstructorDeclaration createConstructor(
+ @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault,
- Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
+ EclipseNode sourceNode, List<Annotation> onConstructor) {
ASTNode source = sourceNode.get();
TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
@@ -307,12 +354,13 @@ public class HandleConstructor {
if (isEnum) level = AccessLevel.PRIVATE;
- if (suppressConstructorProperties == null) {
- if (fields.isEmpty()) {
- suppressConstructorProperties = false;
- } else {
- suppressConstructorProperties = Boolean.TRUE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
- }
+ boolean addConstructorProperties;
+ if (fields.isEmpty()) {
+ addConstructorProperties = false;
+ } else {
+ Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
+ addConstructorProperties = v != null ? v.booleanValue() :
+ Boolean.FALSE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
}
ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
@@ -353,7 +401,7 @@ public class HandleConstructor {
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
if (nonNulls.length != 0) {
- Statement nullCheck = generateNullCheck(field, sourceNode);
+ Statement nullCheck = generateNullCheck(parameter, sourceNode);
if (nullCheck != null) nullChecks.add(nullCheck);
}
parameter.annotations = copyAnnotations(source, nonNulls, nullables);
@@ -367,7 +415,7 @@ public class HandleConstructor {
/* Generate annotations that must be put on the generated method, and attach them. */ {
Annotation[] constructorProperties = null;
- if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) {
+ if (!allToDefault && addConstructorProperties && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fields);
}
@@ -381,6 +429,8 @@ public class HandleConstructor {
}
private static Expression getDefaultExpr(TypeReference type, int s, int e) {
+ boolean array = type instanceof ArrayTypeReference;
+ if (array) return new NullLiteral(s, e);
char[] lastToken = type.getLastToken();
if (Arrays.equals(TypeConstants.BOOLEAN, lastToken)) return new FalseLiteral(s, e);
if (Arrays.equals(TypeConstants.CHAR, lastToken)) return new CharLiteral(new char[] {'\'', '\\', '0', '\''}, s, e);
diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java
index 0ff65a47..4011890d 100644
--- a/src/core/lombok/eclipse/handlers/HandleData.java
+++ b/src/core/lombok/eclipse/handlers/HandleData.java
@@ -43,6 +43,12 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleData extends EclipseAnnotationHandler<Data> {
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleSetter handleSetter = new HandleSetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data");
@@ -66,12 +72,13 @@ public class HandleData extends EclipseAnnotationHandler<Data> {
//for whatever reason, though you can find callers of that one by focusing on the class name itself
//and hitting 'find callers'.
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateRequiredArgsConstructor(
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, Collections.<Annotation>emptyList());
+ handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateRequiredArgsConstructor(
typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index a56eee89..c99b9b5f 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 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
@@ -38,12 +38,13 @@ import lombok.ConfigurationKeys;
import lombok.EqualsAndHashCode;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.core.AnnotationValues;
import lombok.core.configuration.CallSuperType;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -57,7 +58,6 @@ import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
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;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
@@ -67,6 +67,7 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
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;
@@ -98,59 +99,41 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
public static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<EqualsAndHashCode> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, true)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
- }
-
- public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) {
- if (hasAnnotation(EqualsAndHashCode.class, typeNode)) {
- //The annotation will make it happen, so we can skip it.
- return;
- }
-
- generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER, new ArrayList<Annotation>());
- }
-
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
EqualsAndHashCode ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
+ List<Included<EclipseNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode);
- checkForBogusFieldNames(typeNode, annotation);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
- generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
+ generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam);
}
- public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
- assert excludes == null || includes == null;
+ public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) {
+ if (hasAnnotation(EqualsAndHashCode.class, typeNode)) {
+ //The annotation will make it happen, so we can skip it.
+ return;
+ }
+
+ List<Included<EclipseNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null);
+
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
+
+ generateMethods(typeNode, errorNode, members, null, false, access, new ArrayList<Annotation>());
+ }
+
+ public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members,
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
TypeDeclaration typeDecl = null;
@@ -168,18 +151,13 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
if (callSuper == null) {
try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
}
- boolean isDirectDescendantOfObject = true;
-
- if (typeDecl.superclass != null) {
- String p = typeDecl.superclass.toString();
- isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
- }
+ boolean isDirectDescendantOfObject = isDirectDescendantOfObject(typeNode);
if (isDirectDescendantOfObject && callSuper) {
errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless.");
@@ -205,27 +183,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
- List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
- if (includes != null) {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (includes.contains(new String(fieldDecl.name))) nodesForEquality.add(child);
- }
- } else {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (!filterField(fieldDecl)) continue;
-
- //Skip transient fields.
- if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0) continue;
- //Skip excluded fields.
- if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
- nodesForEquality.add(child);
- }
- }
-
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
@@ -254,7 +211,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
//fallthrough
}
- MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
+ MethodDeclaration equalsMethod = createEquals(typeNode, members, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, equalsMethod);
@@ -264,17 +221,16 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
injectMethod(typeNode, canEqualMethod);
}
- MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess);
hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, hashCodeMethod);
}
- public MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- MethodDeclaration method = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
@@ -291,11 +247,11 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
List<Statement> statements = new ArrayList<Statement>();
- final boolean isEmpty = fields.isEmpty();
+ final boolean isEmpty = members.isEmpty();
/* final int PRIME = X; */ {
- /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */
- if (!isEmpty || callSuper) {
+ /* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */
+ if (!isEmpty) {
LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE);
setGeneratedBy(primeDecl, source);
primeDecl.modifiers |= Modifier.FINAL;
@@ -307,31 +263,38 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
- /* int result = 1; */ {
- LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
+ /* int result = ... */ {
+ LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
setGeneratedBy(resultDecl, source);
- resultDecl.initialization = makeIntLiteral("1".toCharArray(), source);
+ final Expression init;
+ if (callSuper) {
+ /* ... super.hashCode(); */
+ MessageSend callToSuper = new MessageSend();
+ setGeneratedBy(callToSuper, source);
+ callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE;
+ callToSuper.receiver = new SuperReference(pS, pE);
+ setGeneratedBy(callToSuper.receiver, source);
+ callToSuper.selector = "hashCode".toCharArray();
+ init = callToSuper;
+ } else {
+ /* ... 1; */
+ init = makeIntLiteral("1".toCharArray(), source);
+ }
+ resultDecl.initialization = init;
resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE;
setGeneratedBy(resultDecl.type, source);
statements.add(resultDecl);
}
- if (callSuper) {
- MessageSend callToSuper = new MessageSend();
- setGeneratedBy(callToSuper, source);
- callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE;
- callToSuper.receiver = new SuperReference(pS, pE);
- setGeneratedBy(callToSuper.receiver, source);
- callToSuper.selector = "hashCode".toCharArray();
- statements.add(createResultCalculation(source, callToSuper));
- }
-
- for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
- char[] dollarFieldName = ("$" + field.getName()).toCharArray();
+ for (Included<EclipseNode, EqualsAndHashCode.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ TypeReference fType = getFieldType(memberNode, fieldAccess);
+ char[] dollarFieldName = ((isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
char[] token = fType.getLastToken();
- Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ Expression fieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.BOOLEAN, token)) {
/* booleanField ? X : Y */
@@ -458,17 +421,46 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return assignment;
}
- public TypeReference createTypeReference(EclipseNode type, long p) {
+ /**
+ * @param type Type to 'copy' into a typeref
+ * @param p position
+ * @param addWildcards If false, all generics are cut off. If true, replaces all genericparams with a ?.
+ * @return
+ */
+ public TypeReference createTypeReference(EclipseNode type, long p, ASTNode source, boolean addWildcards) {
+ int pS = source.sourceStart; int pE = source.sourceEnd;
List<String> list = new ArrayList<String>();
+ List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null;
+
list.add(type.getName());
+ if (addWildcards) genericsCount.add(arraySizeOf(((TypeDeclaration) type.get()).typeParameters));
+ boolean staticContext = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccStatic) != 0;
EclipseNode tNode = type.up();
+ if (!staticContext && tNode.getKind() == Kind.TYPE && (((TypeDeclaration) tNode.get()).modifiers & ClassFileConstants.AccInterface) != 0) staticContext = true;
+
while (tNode != null && tNode.getKind() == Kind.TYPE) {
list.add(tNode.getName());
+ if (addWildcards) genericsCount.add(staticContext ? 0 : arraySizeOf(((TypeDeclaration) tNode.get()).typeParameters));
+ if (!staticContext) staticContext = (((TypeDeclaration) tNode.get()).modifiers & Modifier.STATIC) != 0;
tNode = tNode.up();
+ if (!staticContext && tNode.getKind() == Kind.TYPE && (((TypeDeclaration) tNode.get()).modifiers & ClassFileConstants.AccInterface) != 0) staticContext = true;
}
Collections.reverse(list);
+ if (addWildcards) Collections.reverse(genericsCount);
+
+ if (list.size() == 1) {
+ if (!addWildcards || genericsCount.get(0) == 0) {
+ return new SingleTypeReference(list.get(0).toCharArray(), p);
+ } else {
+ return new ParameterizedSingleTypeReference(list.get(0).toCharArray(), wildcardify(pS, pE, source, genericsCount.get(0)), 0, p);
+ }
+ }
+
+ if (addWildcards) {
+ addWildcards = false;
+ for (int i : genericsCount) if (i > 0) addWildcards = true;
+ }
- if (list.size() == 1) return new SingleTypeReference(list.get(0).toCharArray(), p);
long[] ps = new long[list.size()];
char[][] tokens = new char[list.size()][];
for (int i = 0; i < list.size(); i++) {
@@ -476,16 +468,33 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
tokens[i] = list.get(i).toCharArray();
}
- return new QualifiedTypeReference(tokens, ps);
+ if (!addWildcards) return new QualifiedTypeReference(tokens, ps);
+ TypeReference[][] typeArgs2 = new TypeReference[tokens.length][];
+ for (int i = 0; i < tokens.length; i++) typeArgs2[i] = wildcardify(pS, pE, source, genericsCount.get(i));
+ return new ParameterizedQualifiedTypeReference(tokens, typeArgs2, 0, ps);
+ }
+
+ private TypeReference[] wildcardify(int pS, int pE, ASTNode source, int count) {
+ if (count == 0) return null;
+ TypeReference[] typeArgs = new TypeReference[count];
+ for (int i = 0; i < count; i++) {
+ typeArgs[i] = new Wildcard(Wildcard.UNBOUND);
+ typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE;
+ setGeneratedBy(typeArgs[i], source);
+ }
+
+ return typeArgs;
}
- public MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
+ private int arraySizeOf(Object[] arr) {
+ return arr == null ? 0 : arr.length;
+ }
+
+ public MethodDeclaration createEquals(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
int pS = source.sourceStart; int pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- TypeDeclaration typeDecl = (TypeDeclaration)type.get();
- MethodDeclaration method = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
@@ -528,7 +537,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
SingleNameReference oRef = new SingleNameReference(new char[] { 'o' }, p);
setGeneratedBy(oRef, source);
- TypeReference typeReference = createTypeReference(type, p);
+ TypeReference typeReference = createTypeReference(type, p, source, false);
setGeneratedBy(typeReference, source);
InstanceOfExpression instanceOf = new InstanceOfExpression(oRef, typeReference);
@@ -551,30 +560,15 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- /* MyType<?> other = (MyType<?>) o; */ {
- if (!fields.isEmpty() || needsCanEqual) {
+ /* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */ {
+ if (!members.isEmpty() || needsCanEqual) {
LocalDeclaration other = new LocalDeclaration(otherName, pS, pE);
other.modifiers |= ClassFileConstants.AccFinal;
setGeneratedBy(other, source);
- char[] typeName = typeDecl.name;
- TypeReference targetType;
- if (typeDecl.typeParameters == null || typeDecl.typeParameters.length == 0) {
- targetType = new SingleTypeReference(typeName, p);
- setGeneratedBy(targetType, source);
- other.type = new SingleTypeReference(typeName, p);
- setGeneratedBy(other.type, source);
- } else {
- TypeReference[] typeArgs = new TypeReference[typeDecl.typeParameters.length];
- for (int i = 0; i < typeArgs.length; i++) {
- typeArgs[i] = new Wildcard(Wildcard.UNBOUND);
- typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE;
- setGeneratedBy(typeArgs[i], source);
- }
- targetType = new ParameterizedSingleTypeReference(typeName, typeArgs, 0, p);
- setGeneratedBy(targetType, source);
- other.type = new ParameterizedSingleTypeReference(typeName, copyTypes(typeArgs, source), 0, p);
- setGeneratedBy(other.type, source);
- }
+ TypeReference targetType = createTypeReference(type, p, source, true);
+ setGeneratedBy(targetType, source);
+ other.type = createTypeReference(type, p, source, true);
+ setGeneratedBy(other.type, source);
NameReference oRef = new SingleNameReference(new char[] { 'o' }, p);
setGeneratedBy(oRef, source);
other.initialization = makeCastExpression(oRef, targetType, source);
@@ -636,11 +630,14 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
statements.add(ifSuperEquals);
}
- for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
+ for (Included<EclipseNode, EqualsAndHashCode.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ TypeReference fType = getFieldType(memberNode, fieldAccess);
char[] token = fType.getLastToken();
- Expression thisFieldAccessor = createFieldAccessor(field, fieldAccess, source);
- Expression otherFieldAccessor = createFieldAccessor(field, fieldAccess, source, otherName);
+ Expression thisFieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source);
+ Expression otherFieldAccessor = isMethod ? createMethodAccessor(memberNode, source, otherName) : createFieldAccessor(memberNode, fieldAccess, source, otherName);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.FLOAT, token)) {
@@ -660,9 +657,9 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
} else /* objects */ {
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
- /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
- char[] thisDollarFieldName = ("this$" + field.getName()).toCharArray();
- char[] otherDollarFieldName = ("other$" + field.getName()).toCharArray();
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
+ char[] thisDollarFieldName = ("this" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
+ char[] otherDollarFieldName = ("other" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
statements.add(createLocalDeclaration(source, thisDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), thisFieldAccessor));
statements.add(createLocalDeclaration(source, otherDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), otherFieldAccessor));
@@ -675,7 +672,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
setGeneratedBy(other1, source);
SingleNameReference other2 = new SingleNameReference(otherDollarFieldName, p);
setGeneratedBy(other2, source);
-
NullLiteral nullLiteral = new NullLiteral(pS, pE);
setGeneratedBy(nullLiteral, source);
@@ -738,7 +734,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return method;
}
-
public MethodDeclaration createCanEqual(EclipseNode type, ASTNode source, List<Annotation> onParam) {
/* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
@@ -749,8 +744,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- MethodDeclaration method = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PROTECTED);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
@@ -772,7 +766,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
SingleNameReference otherRef = new SingleNameReference(otherName, p);
setGeneratedBy(otherRef, source);
- TypeReference typeReference = createTypeReference(type, p);
+ TypeReference typeReference = createTypeReference(type, p, source, false);
setGeneratedBy(typeReference, source);
InstanceOfExpression instanceOf = new InstanceOfExpression(otherRef, typeReference);
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 5ea5a210..702713fe 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-2016 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
@@ -97,14 +97,16 @@ public class HandleFieldDefaults extends EclipseASTAdapter {
if (level != null && level != AccessLevel.NONE) {
if ((field.modifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected)) == 0) {
if (!hasAnnotation(PackagePrivate.class, fieldNode)) {
- field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level);
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0) {
+ field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level);
+ }
}
}
}
if (makeFinal && (field.modifiers & ClassFileConstants.AccFinal) == 0) {
if (!hasAnnotation(NonFinal.class, fieldNode)) {
- if ((field.modifiers & ClassFileConstants.AccStatic) == 0 || field.initialization != null) {
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0) {
field.modifiers |= ClassFileConstants.AccFinal;
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
new file mode 100644
index 00000000..c3a28f7f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014-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
+ * 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.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 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.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+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.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) {
+ TypeDeclaration typeDecl = null;
+ if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
+
+ int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
+ 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.");
+ return;
+ }
+
+ for (EclipseNode field : typeNode.down()) {
+ if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix);
+ }
+ }
+
+ 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) {
+ if (field.getKind() != Kind.FIELD) return false;
+ FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
+ return filterField(fieldDecl);
+ }
+
+ public void handle(AnnotationValues<FieldNameConstants> annotation, Annotation ast, EclipseNode annotationNode) {
+ 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");
+ 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.");
+ 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);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 14f2fb72..d0c2cc23 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -41,7 +41,6 @@ import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.agent.PatchDelegate;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
@@ -80,7 +79,7 @@ import org.mangosdk.spi.ProviderFor;
public class HandleGetter extends EclipseAnnotationHandler<Getter> {
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
- public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter) {
+ public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter, List<Annotation> onMethod) {
if (checkForTypeLevelGetter) {
if (hasAnnotation(Getter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -100,12 +99,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
for (EclipseNode field : typeNode.down()) {
- if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, false);
+ if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, false, onMethod);
}
return true;
}
- public boolean fieldQualifiesForGetterGeneration(EclipseNode field) {
+ public static boolean fieldQualifiesForGetterGeneration(EclipseNode field) {
if (field.getKind() != Kind.FIELD) return false;
FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
return filterField(fieldDecl);
@@ -123,13 +122,13 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
* If not, the getter is still generated if it isn't already there, though there will not
* be a warning if its already there. The default access level is used.
*/
- public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean lazy) {
+ public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean lazy, List<Annotation> onMethod) {
if (hasAnnotation(Getter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
- createGetterForField(level, fieldNode, fieldNode, pos, false, lazy, Collections.<Annotation>emptyList());
+ createGetterForField(level, fieldNode, fieldNode, pos, false, lazy, onMethod);
}
public void handle(AnnotationValues<Getter> annotation, Annotation ast, EclipseNode annotationNode) {
@@ -148,18 +147,15 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
if (node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);
switch (node.getKind()) {
case FIELD:
createGetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true, lazy, onMethod);
break;
case TYPE:
- if (!onMethod.isEmpty()) {
- annotationNode.addError("'onMethod' is not supported for @Getter on a type.");
- }
if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");
- generateGetterForType(node, annotationNode, level, false);
+ generateGetterForType(node, annotationNode, level, false, onMethod);
break;
}
}
@@ -183,6 +179,10 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
errorNode.addError("'lazy' requires the field to be private and final.");
return;
}
+ if ((field.modifiers & ClassFileConstants.AccTransient) != 0) {
+ errorNode.addError("'lazy' is not supported on transient fields.");
+ return;
+ }
if (field.initialization == null) {
errorNode.addError("'lazy' requires field initialization.");
return;
diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java
index c49030d8..8c7f7971 100644
--- a/src/core/lombok/eclipse/handlers/HandleLog.java
+++ b/src/core/lombok/eclipse/handlers/HandleLog.java
@@ -109,7 +109,7 @@ public class HandleLog {
private static FieldDeclaration createField(LoggingFramework framework, Annotation source, ClassLiteralAccess loggingType, String logFieldName, boolean useStatic, String loggerTopic) {
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
// private static final <loggerType> log = <factoryMethod>(<parameter>);
FieldDeclaration fieldDecl = new FieldDeclaration(logFieldName.toCharArray(), 0, -1);
@@ -126,13 +126,15 @@ public class HandleLog {
factoryMethodCall.selector = framework.getLoggerFactoryMethodName().toCharArray();
Expression parameter;
- if (loggerTopic == null || loggerTopic.trim().length() == 0) {
+ if (!framework.passTypeName) {
+ parameter = null;
+ } else if (loggerTopic == null || loggerTopic.trim().length() == 0) {
parameter = framework.createFactoryParameter(loggingType, source);
} else {
parameter = new StringLiteral(loggerTopic.toCharArray(), pS, pE, 0);
}
- factoryMethodCall.arguments = new Expression[] { parameter };
+ factoryMethodCall.arguments = parameter != null ? new Expression[] { parameter } : null;
factoryMethodCall.nameSourcePosition = p;
factoryMethodCall.sourceStart = pS;
factoryMethodCall.sourceEnd = factoryMethodCall.statementEnd = pE;
@@ -240,6 +242,17 @@ public class HandleLog {
}
}
+ /**
+ * Handles the {@link lombok.extern.flogger.Flogger} annotation for Eclipse.
+ */
+ @ProviderFor(EclipseAnnotationHandler.class)
+ public static class HandleFloggerLog extends EclipseAnnotationHandler<lombok.extern.flogger.Flogger> {
+ @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, Annotation source, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
+ processAnnotation(LoggingFramework.FLOGGER, annotation, source, annotationNode, "");
+ }
+ }
+
enum LoggingFramework {
// private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(TargetType.class);
COMMONS("org.apache.commons.logging.Log", "org.apache.commons.logging.LogFactory", "getLog", "@CommonsLog"),
@@ -278,18 +291,31 @@ public class HandleLog {
// private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(TargetType.class);
JBOSSLOG("org.jboss.logging.Logger", "org.jboss.logging.Logger", "getLogger", "@JBossLog"),
+
+ // private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
+ FLOGGER("com.google.common.flogger.FluentLogger", "com.google.common.flogger.FluentLogger", "forEnclosingClass", "@Flogger", false),
;
private final String loggerTypeName;
private final String loggerFactoryTypeName;
private final String loggerFactoryMethodName;
private final String annotationAsString;
-
+ private final boolean passTypeName;
+
+ LoggingFramework(String loggerTypeName, String loggerFactoryTypeName, String loggerFactoryMethodName, String annotationAsString, boolean passTypeName) {
+ this.loggerTypeName = loggerTypeName;
+ this.loggerFactoryTypeName = loggerFactoryTypeName;
+ this.loggerFactoryMethodName = loggerFactoryMethodName;
+ this.annotationAsString = annotationAsString;
+ this.passTypeName = passTypeName;
+ }
+
LoggingFramework(String loggerTypeName, String loggerFactoryTypeName, String loggerFactoryMethodName, String annotationAsString) {
this.loggerTypeName = loggerTypeName;
this.loggerFactoryTypeName = loggerFactoryTypeName;
this.loggerFactoryMethodName = loggerFactoryMethodName;
this.annotationAsString = annotationAsString;
+ this.passTypeName = true;
}
final String getAnnotationAsString() {
@@ -308,7 +334,7 @@ public class HandleLog {
return loggerFactoryMethodName;
}
- Expression createFactoryParameter(ClassLiteralAccess loggingType, Annotation source){
+ Expression createFactoryParameter(ClassLiteralAccess loggingType, Annotation source) {
TypeReference copy = copyType(loggingType.type, source);
ClassLiteralAccess result = new ClassLiteralAccess(source.sourceEnd, copy);
setGeneratedBy(result, source);
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 1fcf751d..d4df0deb 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -24,11 +24,12 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.toAllSetterNames;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.toSetterName;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import lombok.AccessLevel;
@@ -38,7 +39,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
@@ -52,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -63,7 +64,7 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleSetter extends EclipseAnnotationHandler<Setter> {
- public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter) {
+ public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter, List<Annotation> onMethod, List<Annotation> onParam) {
if (checkForTypeLevelSetter) {
if (hasAnnotation(Setter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -90,7 +91,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
//Skip final fields.
if ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) continue;
- generateSetterForField(field, pos, level);
+ generateSetterForField(field, pos, level, onMethod, onParam);
}
return true;
}
@@ -107,39 +108,30 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
* If not, the setter is still generated if it isn't already there, though there will not
* be a warning if its already there. The default access level is used.
*/
- public void generateSetterForField(EclipseNode fieldNode, EclipseNode sourceNode, AccessLevel level) {
+ public void generateSetterForField(EclipseNode fieldNode, EclipseNode sourceNode, AccessLevel level, List<Annotation> onMethod, List<Annotation> onParam) {
if (hasAnnotation(Setter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
-
- List<Annotation> empty = Collections.emptyList();
-
- createSetterForField(level, fieldNode, sourceNode, false, empty, empty);
+ createSetterForField(level, fieldNode, sourceNode, false, onMethod, onParam);
}
- public void handle(AnnotationValues<Setter> annotation, Annotation ast, EclipseNode annotationNode) {
+ @Override public void handle(AnnotationValues<Setter> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.SETTER_FLAG_USAGE, "@Setter");
EclipseNode node = annotationNode.up();
AccessLevel level = annotation.getInstance().value();
if (level == AccessLevel.NONE || node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod", annotationNode);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
createSetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, true, onMethod, onParam);
break;
case TYPE:
- if (!onMethod.isEmpty()) {
- annotationNode.addError("'onMethod' is not supported for @Setter on a type.");
- }
- if (!onParam.isEmpty()) {
- annotationNode.addError("'onParam' is not supported for @Setter on a type.");
- }
- generateSetterForType(node, annotationNode, level, false);
+ generateSetterForType(node, annotationNode, level, false, onMethod, onParam);
break;
}
}
@@ -192,28 +184,40 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
}
}
- MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), fieldNode, setterName, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
+ MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
injectMethod(fieldNode.up(), method);
}
+
+ static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
+ ASTNode source = sourceNode.get();
+ int pS = source.sourceStart, pE = source.sourceEnd;
+
+ TypeReference returnType = null;
+ ReturnStatement returnThis = null;
+ if (shouldReturnThis) {
+ returnType = cloneSelfType(fieldNode, source);
+ ThisReference thisRef = new ThisReference(pS, pE);
+ returnThis = new ReturnStatement(thisRef, pS, pE);
+ }
+
+ return createSetter(parent, deprecate, fieldNode, name, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam);
+ }
- static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
+ static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
ASTNode source = sourceNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
method.modifiers = modifier;
- if (shouldReturnThis) {
- method.returnType = cloneSelfType(fieldNode, source);
- }
-
- if (method.returnType == null) {
+ if (returnType != null) {
+ method.returnType = returnType;
+ } else {
method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
method.returnType.sourceStart = pS; method.returnType.sourceEnd = pE;
- shouldReturnThis = false;
}
Annotation[] deprecated = null;
- if (isFieldDeprecated(fieldNode)) {
+ if (isFieldDeprecated(fieldNode) || deprecate) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
@@ -243,10 +247,12 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
statements.add(assignment);
}
- if (shouldReturnThis) {
- ThisReference thisRef = new ThisReference(pS, pE);
- ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE);
- statements.add(returnThis);
+ if (booleanFieldToSet != null) {
+ statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE));
+ }
+
+ if (returnType != null && returnStatement != null) {
+ statements.add(returnStatement);
}
method.statements = statements.toArray(new Statement[0]);
param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
new file mode 100644
index 00000000..6b0275e4
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2013-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
+ * 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.eclipse.handlers;
+
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+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.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+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.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;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Builder.ObtainVia;
+import lombok.ConfigurationKeys;
+import lombok.Singular;
+import lombok.ToString;
+import lombok.core.AST.Kind;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import lombok.experimental.NonFinal;
+import lombok.experimental.SuperBuilder;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
+public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
+ private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
+ private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
+ private static final char[] SET_PREFIX = "$set".toCharArray();
+ private static final char[] SELF_METHOD_NAME = "self".toCharArray();
+
+ private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
+
+ private static class BuilderFieldData {
+ TypeReference type;
+ char[] rawName;
+ char[] name;
+ char[] nameOfSetFlag;
+ SingularData singularData;
+ ObtainVia obtainVia;
+ EclipseNode obtainViaNode;
+ EclipseNode originalFieldNode;
+
+ List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
+ }
+
+ @Override
+ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
+
+ long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
+
+ SuperBuilder builderInstance = annotation.getInstance();
+
+ String builderMethodName = builderInstance.builderMethodName();
+ String buildMethodName = builderInstance.buildMethodName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+
+ EclipseNode tdParent = annotationNode.up();
+
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
+ TypeReference returnType;
+ TypeParameter[] typeParams;
+
+ boolean addCleaning = false;
+
+ if (!(tdParent.get() instanceof TypeDeclaration)) {
+ annotationNode.addError("@SuperBuilder is only supported on types.");
+ return;
+ }
+ TypeDeclaration td = (TypeDeclaration) tdParent.get();
+
+ // Gather all fields of the class that should be set by the builder.
+ List<EclipseNode> allFields = new ArrayList<EclipseNode>();
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent));
+ for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode);
+ boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
+
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fieldNode.getName().toCharArray();
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.type;
+ bfd.singularData = getSingularData(fieldNode, ast);
+ bfd.originalFieldNode = fieldNode;
+
+ if (bfd.singularData != null && isDefault != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ }
+
+ if (fd.initialization == null && isDefault != null) {
+ isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
+ isDefault = null;
+ }
+
+ if (fd.initialization != null && isDefault == null) {
+ if (isFinal) {
+ continue;
+ }
+ fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
+ }
+
+ if (isDefault != null) {
+ bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
+ // The @Builder annotation removes the initializing expression on the field and moves
+ // it to a method called "$default$FIELDNAME". This method is then called upon building.
+ // We do NOT do this, because this is unexpected and may lead to bugs when using other
+ // constructors (see, e.g., issue #1347).
+ // Instead, we keep the init expression and only set a new value in the builder-based
+ // constructor if it was set in the builder. Drawback is that the init expression is
+ // always executed, even if it was unnecessary because its value is overwritten by the
+ // builder.
+ // TODO: Once the issue is resolved in @Builder, we can adapt the solution here.
+ }
+ addObtainVia(bfd, fieldNode);
+ builderFields.add(bfd);
+ allFields.add(fieldNode);
+ }
+
+ // Set the names of the builder classes.
+ String builderClassName = String.valueOf(td.name) + "Builder";
+ String builderImplClassName = builderClassName + "Impl";
+
+ typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0];
+ returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p);
+
+ // <C, B> are the generics for our builder.
+ String classGenericName = "C";
+ String builderGenericName = "B";
+ // If these generics' names collide with any generics on the annotated class, modify them.
+ // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder.
+ java.util.List<String> typeParamStrings = new ArrayList<String>();
+ for (TypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.toString());
+ classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
+ builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);
+
+ TypeReference extendsClause = td.superclass;
+ TypeReference superclassBuilderClass = null;
+ TypeReference[] typeArguments = new TypeReference[] {
+ new SingleTypeReference(classGenericName.toCharArray(), 0),
+ new SingleTypeReference(builderGenericName.toCharArray(), 0)
+ };
+ if (extendsClause instanceof QualifiedTypeReference) {
+ QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause;
+ String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken());
+ String superclassBuilderClassName = superclassClassName + "Builder";
+
+ char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1);
+ tokens[tokens.length] = superclassBuilderClassName.toCharArray();
+ long[] poss = new long[tokens.length];
+ Arrays.fill(poss, p);
+
+ TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);
+
+ // Every token may potentially have type args. Here, we only have
+ // type args for the last token, the superclass' builder.
+ TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
+ typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments);
+
+ superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
+ } else if (extendsClause != null) {
+ String superClass = String.valueOf(extendsClause.getTypeName()[0]);
+ String superclassBuilderClassName = superClass + "Builder";
+
+ char[][] tokens = new char[][] {superClass.toCharArray(), superclassBuilderClassName.toCharArray()};
+ long[] poss = new long[tokens.length];
+ Arrays.fill(poss, p);
+
+ TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);
+
+ // Every token may potentially have type args. Here, we only have
+ // type args for the last token, the superclass' builder.
+ TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
+ typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments);
+
+ superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
+ }
+ // If there is no superclass, superclassBuilderClassExpression is still == null at this point.
+ // You can use it to check whether to inherit or not.
+
+ generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
+ superclassBuilderClass != null);
+
+ // Create the abstract builder class.
+ EclipseNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass,
+ typeParams, ast, classGenericName, builderGenericName);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
+ }
+
+ // Generate the fields in the abstract builder class that hold the values for the instance.
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1);
+ cleanDecl.declarationSourceEnd = -1;
+ cleanDecl.modifiers = ClassFileConstants.AccPrivate;
+ cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ injectFieldAndMarkGenerated(builderType, cleanDecl);
+ }
+
+ // Generate abstract self() and build() methods in the abstract builder.
+ injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName));
+ injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast));
+
+ // Create the setter methods in the abstract builder.
+ for (BuilderFieldData bfd : builderFields) {
+ generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
+ }
+
+ // Create the toString() method for the abstract builder.
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
+ for (BuilderFieldData bfd : builderFields) {
+ for (EclipseNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true));
+ }
+ }
+ // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
+ MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD);
+ if (md != null) {
+ injectMethod(builderType, md);
+ }
+ }
+
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
+ if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
+ // Only non-abstract classes get the Builder implementation.
+ return;
+ }
+
+ // Create the builder implementation class.
+ EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName);
+ if (builderImplType == null) {
+ builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Create the self() and build() methods in the BuilderImpl.
+ injectMethod(builderImplType, generateSelfMethod(builderImplType));
+ injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
+
+ // Add the builder() method to the annotated class.
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass,
+ TypeReference superclassBuilderClass, TypeParameter[] typeParams,
+ ASTNode source, String classGenericName, String builderGenericName) {
+
+ TypeDeclaration parent = (TypeDeclaration) tdParent.get();
+ TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
+ builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract;
+ builder.name = builderClass.toCharArray();
+
+ // Keep any type params of the annotated class.
+ builder.typeParameters = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2);
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method, named "C", which extends the annotated class.
+ TypeParameter o = new TypeParameter();
+ o.name = classGenericName.toCharArray();
+ o.type = cloneSelfType(tdParent, source);
+ builder.typeParameters[builder.typeParameters.length - 2] = o;
+ // 2. The return type for all setter methods, named "B", which extends this builder class.
+ o = new TypeParameter();
+ o.name = builderGenericName.toCharArray();
+ TypeReference[] typerefs = appendBuilderTypeReferences(typeParams, classGenericName, builderGenericName);
+ o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0);
+ builder.typeParameters[builder.typeParameters.length - 1] = o;
+
+ builder.superclass = copyType(superclassBuilderClass, source);
+
+ builder.createDefaultConstructor(false, true);
+
+ builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return injectType(tdParent, builder);
+ }
+
+ private EclipseNode generateBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) {
+ TypeDeclaration parent = (TypeDeclaration) tdParent.get();
+ TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
+ builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal;
+ builder.name = builderImplClass.toCharArray();
+
+ // Add type params if there are any.
+ if (typeParams != null && typeParams.length > 0) builder.typeParameters = copyTypeParams(typeParams, source);
+
+ if (builderAbstractClass != null) {
+ // Extend the abstract builder.
+ // 1. Add any type params of the annotated class.
+ TypeReference[] typeArgs = new TypeReference[typeParams.length + 2];
+ for (int i = 0; i < typeParams.length; i++) {
+ typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class.
+ // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class.
+ typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source);
+ typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams);
+ builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0);
+ }
+
+ builder.createDefaultConstructor(false, true);
+
+ builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return injectType(tdParent, builder);
+ }
+
+ /**
+ * Generates a constructor that has a builder as the only parameter.
+ * The values from the builder are used to initialize the fields of new instances.
+ *
+ * @param typeNode
+ * the type (with the {@code @Builder} annotation) for which a
+ * constructor should be generated.
+ * @param typeParams
+ * @param builderFields a list of fields in the builder which should be assigned to new instances.
+ * @param source the annotation (used for setting source code locations for the generated code).
+ * @param callBuilderBasedSuperConstructor
+ * If {@code true}, the constructor will explicitly call a super
+ * constructor with the builder as argument. Requires
+ * {@code builderClassAsParameter != null}.
+ */
+ private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List<BuilderFieldData> builderFields,
+ EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) {
+
+ ASTNode source = sourceNode.get();
+
+ TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get());
+ long p = (long) source.sourceStart << 32 | source.sourceEnd;
+
+ ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult);
+
+ constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED);
+ constructor.selector = typeDeclaration.name;
+ if (callBuilderBasedSuperConstructor) {
+ constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
+ constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)};
+ } else {
+ constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
+ }
+ constructor.constructorCall.sourceStart = source.sourceStart;
+ constructor.constructorCall.sourceEnd = source.sourceEnd;
+ constructor.thrownExceptions = null;
+ constructor.typeParameters = null;
+ constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
+ constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
+ TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+ constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)};
+
+ List<Statement> statements = new ArrayList<Statement>();
+ List<Statement> nullChecks = new ArrayList<Statement>();
+
+ for (BuilderFieldData fieldNode : builderFields) {
+ char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode);
+ FieldReference thisX = new FieldReference(fieldNode.rawName, p);
+ int s = (int) (p >> 32);
+ int e = (int) p;
+ thisX.receiver = new ThisReference(s, e);
+
+ Expression assignmentExpr;
+ if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
+ fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b");
+ assignmentExpr = new SingleNameReference(fieldNode.name, p);
+ } else {
+ char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName};
+ long[] positions = new long[] {p, p};
+ assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
+ }
+ Statement assignment = new Assignment(thisX, assignmentExpr, (int) p);
+
+ // In case of @Builder.Default, only set the value if it really was set in the builder.
+ if (fieldNode.nameOfSetFlag != null) {
+ char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
+ long[] positions = new long[] {p, p};
+ QualifiedNameReference builderRef = new QualifiedNameReference(variableInBuilder, positions, s, e);
+ assignment = new IfStatement(builderRef, assignment, s, e);
+ }
+ statements.add(assignment);
+
+ Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.originalFieldNode.get(), NON_NULL_PATTERN);
+ if (nonNulls.length != 0) {
+ Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.originalFieldNode.get(), sourceNode);
+ if (nullCheck != null) {
+ nullChecks.add(nullCheck);
+ }
+ }
+ }
+
+ nullChecks.addAll(statements);
+ constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]);
+
+ constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
+
+ injectMethod(typeNode, constructor);
+ }
+
+ private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = builderMethodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ // Add type params if there are any.
+ if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) };
+ out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p);
+ out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+ }
+
+ private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.selector = SELF_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody;
+ if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
+ out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ return out;
+ }
+
+ private MethodDeclaration generateSelfMethod(EclipseNode builderImplType) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult);
+ out.selector = SELF_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccProtected;
+ out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get())};
+ out.returnType = new SingleTypeReference(builderImplType.getName().toCharArray(), 0);
+ out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)};
+ return out;
+ }
+
+ private MethodDeclaration generateAbstractBuildMethod(EclipseNode tdParent, String methodName, boolean override,
+ String classGenericName, ASTNode source) {
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody;
+ out.selector = methodName.toCharArray();
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0);
+ if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)};
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ private MethodDeclaration generateBuildMethod(EclipseNode tdParent, String name, TypeReference returnType, ASTNode source) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ List<Statement> statements = new ArrayList<Statement>();
+
+ out.modifiers = ClassFileConstants.AccPublic;
+ out.selector = name.toCharArray();
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = returnType;
+ out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)};
+
+ AllocationExpression allocationStatement = new AllocationExpression();
+ allocationStatement.type = copyType(out.returnType);
+ // Use a constructor that only has this builder as parameter.
+ allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)};
+ statements.add(new ReturnStatement(allocationStatement, 0, 0));
+ out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]);
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
+ List<Statement> statements = new ArrayList<Statement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements);
+ }
+ }
+
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0));
+ MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ decl.selector = CLEAN_METHOD_NAME;
+ decl.modifiers = ClassFileConstants.AccPrivate;
+ decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ decl.statements = statements.toArray(new Statement[0]);
+ decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return decl;
+ }
+
+ private void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) {
+ List<EclipseNode> existing = new ArrayList<EclipseNode>();
+ for (EclipseNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType));
+ } else {
+ EclipseNode field = null, setFlag = null;
+ for (EclipseNode exists : existing) {
+ char[] n = ((FieldDeclaration) exists.get()).name;
+ if (Arrays.equals(n, bfd.name)) field = exists;
+ if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists;
+ }
+
+ if (field == null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = copyType(bfd.type);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ field = injectFieldAndMarkGenerated(builderType, fd);
+ }
+ if (setFlag == null && bfd.nameOfSetFlag != null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ injectFieldAndMarkGenerated(builderType, fd);
+ }
+ bfd.createdFields.add(field);
+ }
+ }
+ }
+
+ private void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) {
+ boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
+
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
+ return new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ }
+ };
+
+ StatementMaker returnStatementMaker = new StatementMaker() {
+ @Override public ReturnStatement make() {
+ MessageSend returnCall = new MessageSend();
+ returnCall.receiver = ThisReference.implicitThis();
+ returnCall.selector = SELF_METHOD_NAME;
+ return new ReturnStatement(returnCall, 0, 0);
+ }
+ };
+
+ if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode);
+ } else {
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker);
+ }
+ }
+
+ private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode) {
+ TypeDeclaration td = (TypeDeclaration) builderType.get();
+ AbstractMethodDeclaration[] existing = td.methods;
+ if (existing == null) existing = EMPTY_METHODS;
+ int len = existing.length;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ char[] name = fd.name;
+
+ for (int i = 0; i < len; i++) {
+ if (!(existing[i] instanceof MethodDeclaration)) continue;
+ char[] existingName = existing[i].selector;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
+ }
+
+ String setterName = fieldNode.getName();
+
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic,
+ sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ injectMethod(builderType, setter);
+ }
+
+ private void addObtainVia(BuilderFieldData bfd, EclipseNode node) {
+ for (EclipseNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ return;
+ }
+ }
+
+ /**
+ * Returns the explicitly requested singular annotation on this node (field
+ * or parameter), or null if there's no {@code @Singular} annotation on it.
+ *
+ * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
+ */
+ private SingularData getSingularData(EclipseNode node, ASTNode source) {
+ for (EclipseNode child : node.down()) {
+ if (!annotationTypeMatches(Singular.class, child)) continue;
+
+ char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = new String(pluralName);
+ } else {
+ explicitSingular = autoSingularize(new String(pluralName));
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
+ explicitSingular = new String(pluralName);
+ }
+ }
+ }
+ char[] singularName = explicitSingular.toCharArray();
+
+ TypeReference type = ((AbstractVariableDeclaration) node.get()).type;
+ TypeReference[] typeArgs = null;
+ String typeName;
+ if (type instanceof ParameterizedSingleTypeReference) {
+ typeArgs = ((ParameterizedSingleTypeReference) type).typeArguments;
+ typeName = new String(((ParameterizedSingleTypeReference) type).token);
+ } else if (type instanceof ParameterizedQualifiedTypeReference) {
+ TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments;
+ if (tr != null) typeArgs = tr[tr.length - 1];
+ char[][] tokens = ((ParameterizedQualifiedTypeReference) type).tokens;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < tokens.length; i++) {
+ if (i > 0) sb.append(".");
+ sb.append(tokens[i]);
+ }
+ typeName = sb.toString();
+ } else {
+ typeName = type.toString();
+ }
+
+ String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName);
+ EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
+ }
+
+ return null;
+ }
+
+ private String generateNonclashingNameFor(String classGenericName, java.util.List<String> typeParamStrings) {
+ if (!typeParamStrings.contains(classGenericName)) return classGenericName;
+ int counter = 2;
+ while (typeParamStrings.contains(classGenericName + counter)) counter++;
+ return classGenericName + counter;
+ }
+
+ private TypeReference[] appendBuilderTypeReferences(TypeParameter[] typeParams, String classGenericName, String builderGenericName) {
+ TypeReference[] typeReferencesToAppend = new TypeReference[2];
+ typeReferencesToAppend[typeReferencesToAppend.length - 2] = new SingleTypeReference(classGenericName.toCharArray(), 0);
+ typeReferencesToAppend[typeReferencesToAppend.length - 1] = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ return mergeToTypeReferences(typeParams, typeReferencesToAppend);
+ }
+
+ private TypeReference[] getTypeParametersFrom(TypeReference typeRef) {
+ TypeReference[][] typeArgss = null;
+ if (typeRef instanceof ParameterizedQualifiedTypeReference) {
+ typeArgss = ((ParameterizedQualifiedTypeReference) typeRef).typeArguments;
+ } else if (typeRef instanceof ParameterizedSingleTypeReference) {
+ typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference) typeRef).typeArguments};
+ }
+ TypeReference[] typeArgs = new TypeReference[0];
+ if (typeArgss != null && typeArgss.length > 0) {
+ typeArgs = typeArgss[typeArgss.length - 1];
+ }
+ return typeArgs;
+ }
+
+ private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) {
+ if (typeParams.length > 0) {
+ TypeReference[] typerefs = new TypeReference[typeParams.length];
+ for (int i = 0; i < typeParams.length; i++) {
+ typerefs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ return new ParameterizedSingleTypeReference(referenceName.toCharArray(), typerefs, 0, 0);
+ } else {
+ return new SingleTypeReference(referenceName.toCharArray(), 0);
+ }
+ }
+
+ private TypeReference[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) {
+ TypeReference[] typerefs = new TypeReference[typeParams.length + typeReferencesToAppend.length];
+ for (int i = 0; i < typeParams.length; i++) {
+ typerefs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ for (int i = 0; i < typeReferencesToAppend.length; i++) {
+ typerefs[typeParams.length + i] = typeReferencesToAppend[i];
+ }
+ return typerefs;
+ }
+
+ private TypeReference[] mergeTypeReferences(TypeReference[] refs1, TypeReference[] refs2) {
+ TypeReference[] result = new TypeReference[refs1.length + refs2.length];
+ for (int i = 0; i < refs1.length; i++) result[i] = refs1[i];
+ for (int i = 0; i < refs2.length; i++) result[refs1.length + i] = refs2[i];
+ return result;
+ }
+
+ private EclipseNode findInnerClass(EclipseNode parent, String name) {
+ char[] c = name.toCharArray();
+ for (EclipseNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ TypeDeclaration td = (TypeDeclaration) child.get();
+ if (Arrays.equals(td.name, c)) return child;
+ }
+ return null;
+ }
+
+ private static final char[] prefixWith(char[] prefix, char[] name) {
+ char[] out = new char[prefix.length + name.length];
+ System.arraycopy(prefix, 0, out, 0, prefix.length);
+ System.arraycopy(name, 0, out, prefix.length, name.length);
+ return out;
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index a4ed254a..02a19f8d 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.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
@@ -24,7 +24,6 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -37,17 +36,17 @@ import lombok.ConfigurationKeys;
import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
@@ -70,17 +69,25 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleToString extends EclipseAnnotationHandler<ToString> {
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<ToString> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, false)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
+ public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
+
+ ToString ann = annotation.getInstance();
+ List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
+
+ Boolean callSuper = ann.callSuper();
+
+ if (!annotation.isExplicit("callSuper")) callSuper = null;
+
+ Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
+ FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
+ boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
+
+ generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess);
}
public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode) {
@@ -94,41 +101,17 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER);
- }
-
- public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
- handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
- ToString ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
- Boolean callSuper = ann.callSuper();
-
- if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
-
- checkForBogusFieldNames(typeNode, annotation);
-
- Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
- boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
- FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
-
- Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
- boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
-
- generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess);
+ List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null);
+ generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access);
}
- public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+ public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, ToString.Include>> members,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+
TypeDeclaration typeDecl = null;
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
@@ -136,39 +119,20 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
boolean notAClass = (modifiers &
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
- if (typeDecl == null || notAClass) {
- errorNode.addError("@ToString is only supported on a class or enum.");
- }
-
if (callSuper == null) {
try {
callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
}
- List<EclipseNode> nodesForToString = new ArrayList<EclipseNode>();
- if (includes != null) {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (includes.contains(new String(fieldDecl.name))) nodesForToString.add(child);
- }
- } else {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (!filterField(fieldDecl)) continue;
-
- //Skip excluded fields.
- if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
-
- nodesForToString.add(child);
- }
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@ToString is only supported on a class or enum.");
+ return;
}
switch (methodExists("toString", typeNode, 0)) {
case NOT_EXISTS:
- MethodDeclaration toString = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration toString = createToString(typeNode, members, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, toString);
break;
case EXISTS_BY_LOMBOK:
@@ -181,8 +145,9 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
- public static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
- boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public static MethodDeclaration createToString(EclipseNode type, Collection<Included<EclipseNode, ToString.Include>> members,
+ boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
String infixS = ", ";
@@ -195,10 +160,13 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
if (callSuper) {
prefix = (typeName + "(super=").toCharArray();
- } else if (fields.isEmpty()) {
+ } else if (members.isEmpty()) {
prefix = (typeName + "()").toCharArray();
- } else if (includeFieldNames) {
- prefix = (typeName + "(" + new String(((FieldDeclaration)fields.iterator().next().get()).name) + "=").toCharArray();
+ } else if (includeNames) {
+ Included<EclipseNode, ToString.Include> firstMember = members.iterator().next();
+ String name = firstMember.getInc() == null ? "" : firstMember.getInc().name();
+ if (name.isEmpty()) name = firstMember.getNode().getName();
+ prefix = (typeName + "(" + name + "=").toCharArray();
} else {
prefix = (typeName + "(").toCharArray();
}
@@ -219,29 +187,35 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
first = false;
}
- for (EclipseNode field : fields) {
- TypeReference fieldType = getFieldType(field, fieldAccess);
- Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ for (Included<EclipseNode, ToString.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+
+ TypeReference fieldType = getFieldType(memberNode, fieldAccess);
+ Expression memberAccessor;
+ if (memberNode.getKind() == Kind.METHOD) {
+ memberAccessor = createMethodAccessor(memberNode, source);
+ } else {
+ memberAccessor = createFieldAccessor(memberNode, fieldAccess, source);
+ }
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
boolean fieldBaseTypeIsPrimitive = BUILT_IN_TYPES.contains(new String(fieldType.getLastToken()));
+ @SuppressWarnings("unused")
boolean fieldIsPrimitive = fieldType.dimensions() == 0 && fieldBaseTypeIsPrimitive;
boolean fieldIsPrimitiveArray = fieldType.dimensions() == 1 && fieldBaseTypeIsPrimitive;
boolean fieldIsObjectArray = fieldType.dimensions() > 0 && !fieldIsPrimitiveArray;
- @SuppressWarnings("unused")
- boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
Expression ex;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
MessageSend arrayToString = new MessageSend();
arrayToString.sourceStart = pS; arrayToString.sourceEnd = pE;
arrayToString.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
- arrayToString.arguments = new Expression[] { fieldAccessor };
+ arrayToString.arguments = new Expression[] { memberAccessor };
setGeneratedBy(arrayToString.arguments[0], source);
arrayToString.selector = (fieldIsObjectArray ? "deepToString" : "toString").toCharArray();
ex = arrayToString;
} else {
- ex = fieldAccessor;
+ ex = memberAccessor;
}
setGeneratedBy(ex, source);
@@ -254,8 +228,10 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
StringLiteral fieldNameLiteral;
- if (includeFieldNames) {
- char[] namePlusEqualsSign = (infixS + field.getName() + "=").toCharArray();
+ if (includeNames) {
+ String n = member.getInc() == null ? "" : member.getInc().name();
+ if (n.isEmpty()) n = memberNode.getName();
+ char[] namePlusEqualsSign = (infixS + n + "=").toCharArray();
fieldNameLiteral = new StringLiteral(namePlusEqualsSign, pS, pE, 0);
} else {
fieldNameLiteral = new StringLiteral(infix, pS, pE, 0);
diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
index 199ce102..959c1d20 100644
--- a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
@@ -49,6 +49,7 @@ import org.mangosdk.spi.ProviderFor;
import lombok.ConfigurationKeys;
import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
import lombok.core.AST.Kind;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
@@ -57,6 +58,7 @@ import lombok.experimental.UtilityClass;
/**
* Handles the {@code lombok.experimental.UtilityClass} annotation for eclipse.
*/
+@HandlerPriority(-4096) //-2^12; to ensure @FieldDefaults picks up on the 'static' we set here.
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
@Override public void handle(AnnotationValues<UtilityClass> annotation, Annotation ast, EclipseNode annotationNode) {
diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java
index d4ae417c..3742ac00 100644
--- a/src/core/lombok/eclipse/handlers/HandleVal.java
+++ b/src/core/lombok/eclipse/handlers/HandleVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -21,19 +21,24 @@
*/
package lombok.eclipse.handlers;
-import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches;
import lombok.ConfigurationKeys;
import lombok.val;
+import lombok.var;
import lombok.core.HandlerPriority;
import lombok.eclipse.DeferUntilPostDiet;
import lombok.eclipse.EclipseASTAdapter;
import lombok.eclipse.EclipseASTVisitor;
import lombok.eclipse.EclipseNode;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ForStatement;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.mangosdk.spi.ProviderFor;
/*
@@ -44,8 +49,13 @@ import org.mangosdk.spi.ProviderFor;
@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;
- handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ TypeReference type = local.type;
+ boolean isVal = typeMatches(val.class, localNode, type);
+ boolean isVar = typeMatches(var.class, localNode, type);
+ if (!(isVal || isVar)) return;
+
+ if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var");
boolean variableOfForEach = false;
@@ -54,23 +64,37 @@ public class HandleVal extends EclipseASTAdapter {
variableOfForEach = fs.elementVariable == local;
}
+ String annotation = isVal ? "val" : "var";
if (local.initialization == null && !variableOfForEach) {
- localNode.addError("'val' on a local variable requires an initializer expression");
+ localNode.addError("'" + annotation + "' on a local variable requires an initializer expression");
return;
}
if (local.initialization instanceof ArrayInitializer) {
- localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
+ localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
return;
}
- if (localNode.directUp().get() instanceof ForStatement) {
+ ASTNode parentRaw = localNode.directUp().get();
+
+ if (isVal && parentRaw instanceof ForStatement) {
localNode.addError("'val' is not allowed in old-style for loops");
return;
}
+ if (parentRaw instanceof ForStatement && ((ForStatement) parentRaw).initializations != null && ((ForStatement) parentRaw).initializations.length > 1) {
+ localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer");
+ return;
+ }
+
if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) {
- localNode.addError("'val' is not allowed with lambda expressions.");
+ localNode.addError("'" + annotation + "' is not allowed with lambda expressions.");
+ return;
+ }
+
+ if(isVar && local.initialization instanceof NullLiteral) {
+ localNode.addError("variable initializer is 'null'");
+ return;
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java
index 79c11771..2e0338a8 100644
--- a/src/core/lombok/eclipse/handlers/HandleValue.java
+++ b/src/core/lombok/eclipse/handlers/HandleValue.java
@@ -47,6 +47,12 @@ import org.mangosdk.spi.ProviderFor;
@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> {
+ private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults();
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
public void handle(AnnotationValues<Value> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value");
@@ -72,7 +78,7 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
}
}
- new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
+ 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
@@ -80,10 +86,11 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
//for whatever reason, though you can find callers of that one by focusing on the class name itself
//and hitting 'find callers'.
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, Collections.<Annotation>emptyList());
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index d8ac8df6..c035fc26 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -36,7 +36,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.experimental.Wither;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -127,8 +126,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
AccessLevel level = annotation.getInstance().value();
if (level == AccessLevel.NONE || node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod=", annotationNode);
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod", annotationNode);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
diff --git a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
index b42761f4..89964914 100644
--- a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
+++ b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
@@ -369,11 +369,6 @@ public final class SetGeneratedByVisitor extends ASTVisitor {
return super.visit(node, scope);
}
- @Override public boolean visit(ArrayInitializer node, ClassScope scope) {
- fixPositions(setGeneratedBy(node, source));
- return super.visit(node, scope);
- }
-
@Override public boolean visit(ArrayQualifiedTypeReference node, BlockScope scope) {
fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
index 242bde1f..f1687c9c 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -34,7 +34,10 @@ import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -51,13 +54,11 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
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;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
protected String getSimpleTargetTypeName(SingularData data) {
@@ -96,21 +97,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -121,10 +114,12 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray();
md.statements = returnStatement != null ? new Statement[] {a, returnStatement} : new Statement[] {a};
md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
injectMethod(builderType, md);
}
- void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
LombokImmutableList<String> suffixes = getArgumentSuffixes();
char[][] names = new char[suffixes.size()][];
for (int i = 0; i < suffixes.size(); i++) {
@@ -159,12 +154,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
}
md.returnType = returnType;
md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
- void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -190,12 +186,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
md.arguments = new Argument[] {param};
md.returnType = returnType;
md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
- @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
String simpleTypeName = getSimpleTargetTypeName(data);
int agrumentsCount = getTypeArgumentsCount();
@@ -214,14 +211,14 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
invokeBuild = new MessageSend();
invokeBuild.selector = new char[] {'b', 'u', 'i', 'l', 'd'};
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
invokeBuild.receiver = thisDotField;
}
Expression isNull; {
//this.pluralName == null
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
index 2d8083d3..5f86a4dc 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -31,7 +31,10 @@ import java.util.List;
import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
@@ -44,14 +47,12 @@ 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.QualifiedTypeReference;
-import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingularizer {
@Override public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
@@ -87,26 +88,18 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.generateMethods(data, builderType, fluent, chain);
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -122,10 +115,11 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsg, 0, 0);
md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement};
md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
injectMethod(builderType, md);
}
- void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -148,12 +142,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
md.arguments = new Argument[] {param};
md.returnType = returnType;
md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
- void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -178,6 +173,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
md.arguments = new Argument[] {param};
md.returnType = returnType;
md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
index 576115b0..f512bacf 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
@@ -45,7 +45,6 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
-import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.mangosdk.spi.ProviderFor;
@@ -56,9 +55,9 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable");
}
- @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
@@ -76,7 +75,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
/* case 1: (singleton) break; */ {
switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0));
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
MessageSend thisDotFieldGet0 = new MessageSend();
thisDotFieldGet0.receiver = thisDotField;
thisDotFieldGet0.selector = new char[] {'g', 'e', 't'};
@@ -97,7 +96,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
Expression argToUnmodifiable;
/* new j.u.ArrayList<Generics>(this.pluralName); */ {
FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L);
- thisDotPluralName.receiver = new ThisReference(0, 0);
+ thisDotPluralName.receiver = getBuilderReference(builderVariable);
TypeReference targetTypeExpr = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs());
AllocationExpression constructorCall = new AllocationExpression();
@@ -117,7 +116,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
SwitchStatement switchStat = new SwitchStatement();
switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
- switchStat.expression = getSize(builderType, data.getPluralName(), true);
+ switchStat.expression = getSize(builderType, data.getPluralName(), true, builderVariable);
TypeReference localShadowerType = new QualifiedTypeReference(Eclipse.fromQualifiedName(data.getTargetFqn()), NULL_POSS);
localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs());
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
index ef9e2a76..69c2186a 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -29,6 +29,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -44,13 +45,11 @@ 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.QualifiedTypeReference;
-import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
import lombok.core.LombokImmutableList;
@@ -58,6 +57,8 @@ import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
@ProviderFor(EclipseSingularizer.class)
public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer {
@@ -132,26 +133,18 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
return Arrays.asList(keyFieldNode, valueFieldNode);
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.generateMethods(data, builderType, fluent, chain);
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -178,10 +171,12 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsgs, 0, 0);
md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement};
md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
injectMethod(builderType, md);
}
- private void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ private void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -225,12 +220,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
md.arguments = new Argument[] {keyParam, valueParam};
md.returnType = returnType;
md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
- private void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ private void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
@@ -288,21 +284,22 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
md.arguments = new Argument[] {param};
md.returnType = returnType;
md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("putAll", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
- @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
if (data.getTargetFqn().equals("java.util.Map")) {
- statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap"));
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", builderVariable));
} else {
- statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap"));
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap", builderVariable));
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
index 2d16eae0..200e615e 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
@@ -37,16 +37,16 @@ public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingul
return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
}
- @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
if (data.getTargetFqn().equals("java.util.Set")) {
- statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet"));
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet", builderVariable));
} else {
- statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet"));
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet", builderVariable));
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
index 6661f4af..8bcfa65d 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
@@ -90,7 +90,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA));
}
- protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) {
+ protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, String builderVariable) {
List<Statement> switchContents = new ArrayList<Statement>();
char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
@@ -112,7 +112,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
/* !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); */ {
FieldReference thisDotKey = new FieldReference(keyName, 0L);
- thisDotKey.receiver = new ThisReference(0, 0);
+ thisDotKey.receiver = getBuilderReference(builderVariable);
MessageSend thisDotKeyGet0 = new MessageSend();
thisDotKeyGet0.receiver = thisDotKey;
thisDotKeyGet0.selector = new char[] {'g', 'e', 't'};
@@ -122,7 +122,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
if (mapMode) {
char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
FieldReference thisDotValue = new FieldReference(valueName, 0L);
- thisDotValue.receiver = new ThisReference(0, 0);
+ thisDotValue.receiver = getBuilderReference(builderVariable);
MessageSend thisDotValueGet0 = new MessageSend();
thisDotValueGet0.receiver = thisDotValue;
thisDotValueGet0.selector = new char[] {'g', 'e', 't'};
@@ -143,12 +143,12 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
{ // default:
switchContents.add(new CaseStatement(null, 0, 0));
- switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType));
+ switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, builderVariable));
}
SwitchStatement switchStat = new SwitchStatement();
switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
- switchStat.expression = getSize(builderType, keyName, true);
+ switchStat.expression = getSize(builderType, keyName, true, builderVariable);
TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
@@ -157,7 +157,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
return Arrays.asList(varDefStat, switchStat);
}
- protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) {
+ protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, String builderVariable) {
char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
Statement createStat; {
@@ -166,11 +166,11 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
if (addInitialCapacityArg) {
// this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
// lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
- Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS);
+ Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS);
FieldReference integerMaxValue = new FieldReference("MAX_VALUE".toCharArray(), 0L);
integerMaxValue.receiver = new QualifiedNameReference(TypeConstants.JAVA_LANG_INTEGER, NULL_POSS, 0, 0);
- Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard), OperatorIds.PLUS);
- Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS);
+ Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard, builderVariable), OperatorIds.PLUS);
+ Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS);
Expression sizeFormulaRight = new BinaryExpression(sizeFormulaRightLeft, makeIntLiteral(new char[] {'3'}, null), OperatorIds.DIVIDE);
Expression sizeFormula = new BinaryExpression(sizeFormulaLeft, sizeFormulaRight, OperatorIds.PLUS);
Expression cond = new ConditionalExpression(lessThanCutoff, sizeFormula, integerMaxValue);
@@ -203,9 +203,9 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
pluralnameDotPut.selector = new char[] {'p', 'u', 't'};
pluralnameDotPut.receiver = new SingleNameReference(data.getPluralName(), 0L);
FieldReference thisDotKey = new FieldReference(varName, 0L);
- thisDotKey.receiver = new ThisReference(0, 0);
+ thisDotKey.receiver = getBuilderReference(builderVariable);
FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L);
- thisDotValue.receiver = new ThisReference(0, 0);
+ thisDotValue.receiver = getBuilderReference(builderVariable);
MessageSend keyArg = new MessageSend();
keyArg.receiver = thisDotKey;
keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
@@ -219,7 +219,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
LocalDeclaration forInit = new LocalDeclaration(iVar, 0, 0);
forInit.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
forInit.initialization = makeIntLiteral(new char[] {'0'}, null);
- Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard), OperatorIds.LESS);
+ Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard, builderVariable), OperatorIds.LESS);
Expression incrementExpr = new PostfixExpression(new SingleNameReference(iVar, 0L), IntLiteral.One, OperatorIds.PLUS, 0);
fillStat = new ForStatement(new Statement[] {forInit}, checkExpr, new Statement[] {incrementExpr}, pluralnameDotPut, true, 0, 0);
} else {
@@ -228,14 +228,14 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
pluralnameDotAddAll.selector = new char[] {'a', 'd', 'd', 'A', 'l', 'l'};
pluralnameDotAddAll.receiver = new SingleNameReference(data.getPluralName(), 0L);
FieldReference thisDotPluralname = new FieldReference(varName, 0L);
- thisDotPluralname.receiver = new ThisReference(0, 0);
+ thisDotPluralname.receiver = getBuilderReference(builderVariable);
pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname};
fillStat = pluralnameDotAddAll;
}
if (nullGuard) {
FieldReference thisDotField = new FieldReference(varName, 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL);
fillStat = new IfStatement(cond, fillStat, 0, 0);
}