aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2017-03-20 22:54:36 +0100
committerReinier Zwitserloot <reinier@zwitserloot.com>2017-03-20 22:54:36 +0100
commitbf54986e8a08d246877fae902c58dc41ca2f559b (patch)
treef1f05e8d65e35aa0627c69c9fc8ad0d1a69e38b0 /src/core/lombok
parentc63c1528843a3ac591c9fbd2db3732af8824d097 (diff)
downloadlombok-bf54986e8a08d246877fae902c58dc41ca2f559b.tar.gz
lombok-bf54986e8a08d246877fae902c58dc41ca2f559b.tar.bz2
lombok-bf54986e8a08d246877fae902c58dc41ca2f559b.zip
Fixing issue #1201: Builder now supports defaults!
Diffstat (limited to 'src/core/lombok')
-rw-r--r--src/core/lombok/Builder.java6
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java18
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java118
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java9
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java11
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java89
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java9
7 files changed, 199 insertions, 61 deletions
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java
index a0b8242f..3076581e 100644
--- a/src/core/lombok/Builder.java
+++ b/src/core/lombok/Builder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -108,11 +108,11 @@ import java.lang.annotation.Target;
@Retention(SOURCE)
public @interface Builder {
/**
- * Marks a given field as being a 'constant' (initialized by the initializing expression during construction and not eligible to be modified as part of {@code @Builder}.
+ * The field annotated with {@code @Default} must have an initializing expression; that expression is taken as the default to be used if not explicitly set during building.
*/
@Target(FIELD)
@Retention(SOURCE)
- public @interface Constant {}
+ public @interface Default {}
/** Name of the method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 4726b17e..e366cff9 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -443,6 +443,24 @@ public class EclipseHandlerUtil {
}
}
+ 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.
*
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 8c200a94..ae080c96 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-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
@@ -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;
@@ -102,6 +103,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
TypeReference type;
char[] rawName;
char[] name;
+ char[] nameOfDefaultProvider;
+ char[] nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
EclipseNode obtainViaNode;
@@ -127,6 +130,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;
@@ -173,17 +186,38 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
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)) {
+ 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));
+
+ 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, add @Builder.Constant.");
+ }
+
BuilderFieldData bfd = new BuilderFieldData();
bfd.rawName = fieldNode.getName().toCharArray();
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast);
+ if (isDefault != null) {
+ if (bfd.singularData != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ } else {
+ bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
+ bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
+
+ MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.add(fieldNode);
@@ -405,7 +439,7 @@ 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);
}
@@ -514,7 +548,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>();
@@ -536,7 +570,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
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;
+
+ 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) {
@@ -583,6 +630,22 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return out;
}
+ public MethodDeclaration generateDefaultProvider(char[] methodName, EclipseNode fieldNode, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult);
+ 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 +671,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);
}
}
}
@@ -635,13 +707,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) {
if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
- makeSimpleSetterMethodForBuilder(builderType, bfd.createdFields.get(0), sourceNode, fluent, chain);
+ makeSimpleSetterMethodForBuilder(builderType, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain);
} else {
bfd.singularData.getSingularizer().generateMethods(bfd.singularData, builderType, fluent, chain);
}
}
- private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
@@ -657,7 +729,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
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, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic,
sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
injectMethod(builderType, setter);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index 49b09231..5978d542 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
@@ -144,14 +144,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);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 7e7ea121..84124d68 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
@@ -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;
@@ -192,11 +193,11 @@ 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(), fieldNode, setterName, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
injectMethod(fieldNode.up(), method);
}
- 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, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, 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;
@@ -243,6 +244,10 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
statements.add(assignment);
}
+ if (booleanFieldToSet != null) {
+ statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE));
+ }
+
if (shouldReturnThis) {
ThisReference thisRef = new ThisReference(pS, pE);
ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE);
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 5dde551d..83388848 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -85,6 +85,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression type;
Name rawName;
Name name;
+ Name nameOfDefaultProvider;
+ Name nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
JavacNode obtainViaNode;
@@ -141,17 +143,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true);
boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
- JavacNode isConstant = findAnnotation(Builder.Constant.class, fieldNode, true);
- if (fd.init != null && isFinal) {
- if (isConstant != null) continue;
+ if (fd.init == null && isDefault != null) {
+ isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
+ isDefault = null;
}
- if (isConstant != null) {
- if (!isFinal && fd.init == null) isConstant.addWarning("@Builder.Constant doesn't do anything unless the field has an initializing expression (' = something;') and is final.");
- else if (!isFinal) isConstant.addWarning("@Builder.Constant doesn't do anything unless the field is final.");
- else if (fd.init == null) isConstant.addWarning("@Builder.Constant doesn't do anything unless the field has an initializing expression (' = something;').");
+ if (fd.init != 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, add @Builder.Constant.");
}
BuilderFieldData bfd = new BuilderFieldData();
@@ -159,6 +161,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
+ if (isDefault != null) {
+ if (bfd.singularData != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ } else {
+ bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name);
+ bfd.nameOfSetFlag = parent.toName(bfd.name + "$set");
+ JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode);
+ recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.append(fieldNode);
@@ -366,7 +380,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
+ JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
if (md != null) injectMethod(builderType, md);
}
@@ -486,7 +500,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
}
- statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false))));
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0))));
JCBlock body = maker.Block(0, statements.toList());
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
/*
@@ -503,7 +517,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
*/
}
- private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
+ private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
JavacTreeMaker maker = type.getTreeMaker();
JCExpression call;
@@ -524,25 +538,28 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
for (BuilderFieldData bfd : builderFields) {
- args.append(maker.Ident(bfd.name));
+ if (bfd.nameOfSetFlag != null) {
+ args.append(maker.Conditional(maker.Ident(bfd.nameOfSetFlag), maker.Ident(bfd.name),
+ maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil())));
+ } else {
+ args.append(maker.Ident(bfd.name));
+ }
}
if (addCleaning) {
- statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1))));
}
if (builderName == null) {
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
statements.append(maker.Return(call));
} else {
-
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
typeParams.append(maker.Ident(tp.name));
}
JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name);
- if (!isStatic)
- callee = maker.Select(callee, type.up().toName("this"));
+ if (!isStatic) callee = maker.Select(callee, type.up().toName("this"));
JCExpression fn = maker.Select(callee, builderName);
call = maker.Apply(typeParams.toList(), fn, args.toList());
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
@@ -557,6 +574,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
+ public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode) {
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+ JCVariableDecl field = (JCVariableDecl) fieldNode.get();
+
+ JCStatement statement = maker.Return(field.init);
+ field.init = null;
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ int modifiers = Flags.PRIVATE | Flags.STATIC;
+ return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
JavacTreeMaker maker = type.getTreeMaker();
@@ -581,36 +610,42 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- top:
for (int i = len - 1; i >= 0; i--) {
BuilderFieldData bfd = builderFields.get(i);
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source));
} else {
+ JavacNode field = null, setFlag = null;
for (JavacNode exists : existing) {
Name n = ((JCVariableDecl) exists.get()).name;
- if (n.equals(bfd.name)) {
- bfd.createdFields.add(exists);
- continue top;
- }
+ if (n.equals(bfd.name)) field = exists;
+ if (n.equals(bfd.nameOfSetFlag)) setFlag = exists;
}
JavacTreeMaker maker = builderType.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
- JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
- bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, newField));
+ if (field == null) {
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
+ field = injectFieldAndMarkGenerated(builderType, newField);
+ }
+ if (setFlag == null && bfd.nameOfSetFlag != null) {
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null);
+ injectFieldAndMarkGenerated(builderType, newField);
+ }
+ bfd.createdFields.add(field);
}
}
}
public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) {
if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
- makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), source, fluent, chain);
+ makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain);
} else {
fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain);
}
}
- private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
@@ -623,7 +658,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
JavacTreeMaker maker = fieldNode.getTreeMaker();
- JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
injectMethod(builderType, newMethod);
}
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index e766127a..6f09ca40 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -206,10 +206,10 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
String setterName = toSetterName(field);
boolean returnThis = shouldReturnThis(field);
- return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam);
+ return createSetter(access, field, treeMaker, setterName, null, returnThis, source, onMethod, onParam);
}
- public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, String setterName, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -235,6 +235,11 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
statements.append(treeMaker.Exec(assign));
}
+ if (booleanFieldToSet != null) {
+ JCAssign setBool = treeMaker.Assign(treeMaker.Ident(booleanFieldToSet), treeMaker.Literal(CTC_BOOLEAN, 1));
+ statements.append(treeMaker.Exec(setBool));
+ }
+
JCExpression methodType = null;
if (shouldReturnThis) {
methodType = cloneSelfType(field);