From 849aa4fddadb40439a30967131d2847b90fb139f Mon Sep 17 00:00:00 2001
From: Jan Rieke
* Complete documentation is found at the project lombok features page for @SuperBuilder.
- *
+ *
* @see Singular
*/
@Target(TYPE)
@@ -51,16 +51,15 @@
public @interface SuperBuilder {
/** @return Name of the method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
-
+
/** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
String buildMethodName() default "build";
-
- // toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method)
- // to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2.
- /*
- * If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
- *
+
+ /**
+ * If
+ You can use
To ensure type-safety,
@@ -31,10 +33,12 @@
The build() method's name (default: build()
method which returns a completed instance of the original type.
* true
, generate an instance method to obtain a builder that is initialized with the values of this instance.
+ * In this case, all superclasses must also have toBuilder=true
.
+ *
* @return Whether to generate a {@code toBuilder()} method.
*/
-// boolean toBuilder() default false;
+ boolean toBuilder() default false;
}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
new file mode 100644
index 0000000000..2e097c1a5a
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -0,0 +1,157 @@
+import java.util.List;
+
+import manual.model.Superclass.SuperclassBuilder;
+import manual.model.Superclass.SuperclassBuilderImpl;
+public class SuperBuilderBasicToBuilder {
+ public static class Parent {
+ int field1;
+ List
+ * public ParentBuilder<?, ?> toBuilder() {
+ * return new FoobarBuilderImpl().$fillValuesFrom(this);
+ * }
+ *
+ */
+ private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, ListtoBuilder()
method in the annotated class that looks like this:
*
* public ParentBuilder<?, ?> toBuilder() {
* return new FoobarBuilderImpl().$fillValuesFrom(this);
@@ -514,7 +519,9 @@ private JCMethodDecl generateToBuilderMethod(String builderClassName, String bui
for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
JCExpression newClass = maker.NewClass(null, List.
*/
- private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, String classGenericName, TypeParameter[] typeParams, java.util.List$fillValuesFrom()
method in the abstract builder class that looks
+ * like this:
+ *
+ * protected B $fillValuesFrom(final C instance) {
+ * super.$fillValuesFrom(instance);
+ * this.field(instance.field);
+ * return self();
+ * }
+ *
+ */
+ private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, java.util.List$fillValuesFrom()
method in the abstract builder class that looks
+ * like this:
+ *
+ * protected B $fillValuesFrom(final C instance) {
+ * super.$fillValuesFrom(instance);
+ * this.field(instance.field);
+ * return self();
+ * }
+ *
+ */
+ private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, TypeParameter[] typeParams, java.util.ListtoBuilder()
method in the annotated class that looks like this:
+ *
+ * public ParentBuilder<?, ?> toBuilder() {
+ * return new FoobarBuilderImpl().$fillValuesFrom(this);
+ * }
+ *
+ */
+ private MethodDeclaration generateToBuilderMethod(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 = TO_BUILDER_METHOD_NAME;
+ out.modifiers = ClassFileConstants.AccPublic;
+ 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 newClass = new AllocationExpression();
+ newClass.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p);
+ MessageSend invokeFillMethod = new MessageSend();
+ invokeFillMethod.receiver = newClass;
+ invokeFillMethod.selector = FILL_VALUES_METHOD_NAME;
+ invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)};
+ out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+ }
+
/**
* Generates a $fillValuesFrom()
method in the abstract builder class that looks
* like this:
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index c8727f93a2..bdcd043a36 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -523,7 +523,6 @@ private JCMethodDecl generateToBuilderMethod(String builderClassName, String bui
JCExpression newClass = maker.NewClass(null, List.
* protected B $fillValuesFrom(final C instance) {
* super.$fillValuesFrom(instance);
- * this.field(instance.field);
+ * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
* return self();
* }
*
*/
- private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, TypeParameter[] typeParams, java.util.List$fillValuesFromInstanceIntoBuilder()
method in
+ * the builder implementation class that copies all fields from the instance
+ * to the builder. It looks like this:
+ *
+ *
+ * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder<?, ?> b) {
+ * b.field(instance.field);
+ * }
+ *
+ */
+ private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderClassName, String classGenericName, TypeParameter[] typeParams, java.util.List
* protected B $fillValuesFrom(final C instance) {
* super.$fillValuesFrom(instance);
- * this.field(instance.field);
+ * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
* return self();
* }
*
*/
- private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, java.util.List$fillValuesFromInstanceIntoBuilder()
method in
+ * the builder implementation class that copies all fields from the instance
+ * to the builder. It looks like this:
+ *
+ *
+ * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder<?, ?> b) {
+ * b.field(instance.field);
+ * }
+ *
+ */
+ private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, boolean inherited, String builderClassname, String classGenericName, List@SuperBuilder
generates a private constructor on the class that takes a builder instance as a parameter. This constructor sets the fields of the new instance to the values from the builder.
@SuperBuilder
is not compatible with @Builder
.
+ @SuperBuilder(toBuilder = true)
to also generate an instance method in your class called toBuilder()
; it creates a new builder that starts out with all the values of this instance. You can put the @Builder.ObtainVia
annotation on the fields to indicate alternative means by which the value for that field/parameter is obtained from this instance. For example, you can specify a method to be invoked: @Builder.ObtainVia(method = "calculateFoo")
.
@SuperBuilder
generates two inner builder classes for each annotated class, one abstract and one concrete class named FoobarBuilder
and FoobarBuilderImpl
(where Foobar is the name of the annotated class).
"build"
)
"builder"
)
+ toBuilder()
(default: no)
- @SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld")
+ @SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true)