diff options
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 53 | ||||
-rw-r--r-- | test/transform/resource/after-ecj/SuperBuilderWithDefaults.java | 30 |
2 files changed, 57 insertions, 26 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 6b0275e4..8badb04c 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -48,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.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; @@ -60,6 +61,7 @@ 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.UnaryExpression; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; @@ -95,6 +97,7 @@ import lombok.experimental.SuperBuilder; 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[] DEFAULT_PREFIX = "$default$".toCharArray(); private static final char[] SET_PREFIX = "$set".toCharArray(); private static final char[] SELF_METHOD_NAME = "self".toCharArray(); @@ -104,6 +107,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { TypeReference type; char[] rawName; char[] name; + char[] nameOfDefaultProvider; char[] nameOfSetFlag; SingularData singularData; ObtainVia obtainVia; @@ -177,16 +181,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } if (isDefault != null) { + bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name); 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. + + MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); + if (md != null) injectMethod(tdParent, md); } addObtainVia(bfd, fieldNode); builderFields.add(bfd); @@ -461,10 +460,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { for (BuilderFieldData fieldNode : builderFields) { char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode); - FieldReference thisX = new FieldReference(fieldNode.rawName, p); + FieldReference fieldInThis = new FieldReference(fieldNode.rawName, p); int s = (int) (p >> 32); int e = (int) p; - thisX.receiver = new ThisReference(s, e); + fieldInThis.receiver = new ThisReference(s, e); Expression assignmentExpr; if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { @@ -475,16 +474,26 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { long[] positions = new long[] {p, p}; assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); } - Statement assignment = new Assignment(thisX, assignmentExpr, (int) p); + Statement assignment = new Assignment(fieldInThis, assignmentExpr, (int) p); + statements.add(assignment); - // In case of @Builder.Default, only set the value if it really was set in the builder. + // In case of @Builder.Default, set the value to the default if it was NOT set in the builder. if (fieldNode.nameOfSetFlag != null) { - char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag}; + char[][] setVariableInBuilder = 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); + QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e); + + MessageSend inv = new MessageSend(); + inv.sourceStart = source.sourceStart; + inv.sourceEnd = source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) typeNode.get()).name, 0L); + inv.selector = fieldNode.nameOfDefaultProvider; + inv.typeArguments = typeParameterNames(((TypeDeclaration) typeNode.get()).typeParameters); + + assignment = new Assignment(fieldInThis, inv, (int) p); + IfStatement ifBlockForDefault = new IfStatement(new UnaryExpression(setVariableInBuilderRef, OperatorIds.NOT), assignment, s, e); + statements.add(ifBlockForDefault); } - statements.add(assignment); Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.originalFieldNode.get(), NON_NULL_PATTERN); if (nonNulls.length != 0) { @@ -817,6 +826,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { return result; } + 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; + } + private EclipseNode findInnerClass(EclipseNode parent, String name) { char[] c = name.toCharArray(); for (EclipseNode child : parent.down()) { diff --git a/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java b/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java index f0e3d8be..731bd9f2 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java @@ -36,14 +36,22 @@ public class SuperBuilderWithDefaults { return new Parent<N>(this);
}
}
- private @lombok.Builder.Default long millis = System.currentTimeMillis();
- private @lombok.Builder.Default N numberField = null;
+ private @lombok.Builder.Default long millis;
+ private @lombok.Builder.Default N numberField;
+ private static @java.lang.SuppressWarnings("all") <N extends Number>long $default$millis() {
+ return System.currentTimeMillis();
+ }
+ private static @java.lang.SuppressWarnings("all") <N extends Number>N $default$numberField() {
+ return null;
+ }
protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<N, ?, ?> b) {
super();
- if (b.millis$set)
- this.millis = b.millis;
- if (b.numberField$set)
- this.numberField = b.numberField;
+ this.millis = b.millis;
+ if ((! b.millis$set))
+ this.millis = Parent.<N>$default$millis();
+ this.numberField = b.numberField;
+ if ((! b.numberField$set))
+ this.numberField = Parent.<N>$default$numberField();
}
public static @java.lang.SuppressWarnings("all") <N extends Number>ParentBuilder<N, ?, ?> builder() {
return new ParentBuilderImpl<N>();
@@ -78,11 +86,15 @@ public class SuperBuilderWithDefaults { return new Child(this);
}
}
- private @lombok.Builder.Default double doubleField = Math.PI;
+ private @lombok.Builder.Default double doubleField;
+ private static @java.lang.SuppressWarnings("all") double $default$doubleField() {
+ return Math.PI;
+ }
protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
super(b);
- if (b.doubleField$set)
- this.doubleField = b.doubleField;
+ this.doubleField = b.doubleField;
+ if ((! b.doubleField$set))
+ this.doubleField = Child.$default$doubleField();
}
public static @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> builder() {
return new ChildBuilderImpl();
|