aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2018-04-04 23:26:01 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2018-04-04 23:26:20 +0200
commit3eea3b958946dd813197f00a1292b7a72380878a (patch)
tree9f8e16f6d1544e28be713d1c57f3666f1ced9494
parent6b0b1a6a7ba73a2c43bb8ab413375505d61aacc6 (diff)
downloadlombok-3eea3b958946dd813197f00a1292b7a72380878a.tar.gz
lombok-3eea3b958946dd813197f00a1292b7a72380878a.tar.bz2
lombok-3eea3b958946dd813197f00a1292b7a72380878a.zip
introduction of `@SuperBuilder` and a testcase.
-rw-r--r--src/core/lombok/Builder.java50
-rw-r--r--src/core/lombok/experimental/SuperBuilder.java66
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderBasic.java112
-rw-r--r--test/transform/resource/before/SuperBuilderBasic.java (renamed from test/transform/resource/before/BuilderWithInherit.java)8
4 files changed, 187 insertions, 49 deletions
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java
index 7ae43bfa..d7a2a109 100644
--- a/src/core/lombok/Builder.java
+++ b/src/core/lombok/Builder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2017 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
@@ -55,8 +55,8 @@ import java.lang.annotation.Target;
*
* <pre>
* &#064;Builder
- * class Example {
- * private int foo;
+ * class Example&lt;T&gt; {
+ * private T foo;
* private final String bar;
* }
* </pre>
@@ -103,6 +103,8 @@ import java.lang.annotation.Target;
* }
* }
* </pre>
+ *
+ * @see Singular
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
@@ -132,48 +134,6 @@ public @interface Builder {
String builderClassName() default "";
/**
- * Should the generated builder extend the parent class's builder?
- *
- * If {@code true}, the generated builder class will extend the builder of the
- * superclass. This means the builder also contains the methods for fields from
- * the superclass.
- * <p>
- * Note that both this builder and the superclass' builder must be a builder
- * for a type (so, not for a static method or a constructor). You must mark
- * the parent extendable: {@code @Builder(extensible = true)}.
- * <p>
- * This builder will also be {@code extensible = true} if you set {@code inherit = true}.
- *
- * @see #extensible()
- */
- boolean inherit() default false;
-
- /**
- * Should the generated builder be extensible by subclasses?
- *
- * If {@code true} the generated builder class will be extensible by
- * {@code @Builder} classes of this class's subclasses, by marking them
- * with {@code @Builder(inherit = true)}.
- * <p>
- * The {@code @Builder} extensible system only works for {@code @Builder}
- * annotations on types (so, not on a static method or a constructor).
- *
- * @see #inherit()
- */
- boolean extensible() default false;
-
- /**
- * Name of the builder class in the superclass.
- *
- * Only relevant if {@code inherit = true}
- *
- * Default: {@code (SuperclassTypeName)Builder}.
- *
- * @see #inherit()
- */
- String superclassBuilderClassName() default "";
-
- /**
* If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
* Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns
* an instance of the declaring type.
diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java
new file mode 100644
index 00000000..26127a5f
--- /dev/null
+++ b/src/core/lombok/experimental/SuperBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 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.experimental;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import lombok.Singular;
+
+/**
+ * The SuperBuilder annotation creates a so-called 'builder' aspect to the class that is annotated with {@code @SuperBuilder}, but which works well when extending.
+ * It is similar to {@code @Builder}, except it is only legal on types, is less configurable, but allows you to {@code extends} other builder-able classes.
+ * <p>
+ * All classes in the hierarchy must be annotated with {@code @SuperBuilder}.
+ * <p>
+ * Lombok generates 2 inner 'builder' classes, which extend the parent class' builder class (unless your class doesn't have an extends clause).
+ * Lombok also generates a static method named {@code builder()}, and a protected constructor that takes 1 argument of the builderclass type.
+ * <p>
+ * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
+ * constructor / method (each field, when annotating a class), which returns the builder itself.
+ * The builder also has a <code>build()</code> method which returns a completed instance of the original type.
+ * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for &#64;SuperBuilder</a>.
+ *
+ * @see Singular
+ */
+@Target(TYPE)
+@Retention(SOURCE)
+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.
+ *
+ * @return Whether to generate a {@code toBuilder()} method.
+ */
+// boolean toBuilder() default false;
+}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java
new file mode 100644
index 00000000..ffb56e0e
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java
@@ -0,0 +1,112 @@
+import java.util.List;
+
+public class SuperBuilderBasic {
+ public static class Parent {
+ int field1;
+ List<String> items;
+ protected Parent(final ParentBuilder<?, ?> b) {
+ this.field1 = b.field1;
+ java.util.List<String> items;
+ switch (b.items == null ? 0 : b.items.size()) {
+ case 0:
+ items = java.util.Collections.emptyList();
+ break;
+ case 1:
+ items = java.util.Collections.singletonList(b.items.get(0));
+ break;
+ default:
+ items = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(b.items));
+ }
+ this.items = items;
+ }
+ private static class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ @Override
+ public Parent build() {
+ return new Parent(this);
+ }
+
+ @Override
+ public ParentBuilderImpl self() {
+ return this;
+ }
+ }
+ public abstract static class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ @java.lang.SuppressWarnings("all")
+ private int field1;
+ @java.lang.SuppressWarnings("all")
+ private java.util.ArrayList<String> items;
+ @java.lang.SuppressWarnings("all")
+ ParentBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public B field1(final int field1) {
+ this.field1 = field1;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B item(final String item) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.add(item);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B items(final java.util.Collection<? extends String> items) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B clearItems() {
+ if (this.items != null) this.items.clear();
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ }
+ public static ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static class Child extends Parent {
+ double field3;
+ protected Child(ChildBuilder<?, ?> b) {
+ super(b);
+ this.field3 = b.field3;
+ }
+ private static class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ @Override
+ public Child build() {
+ return new Child(this);
+ }
+
+ @Override
+ public ChildBuilderImpl self() {
+ return this;
+ }
+ }
+ public abstract static class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<Child, B> {
+ @java.lang.SuppressWarnings("all")
+ private double field3;
+ @java.lang.SuppressWarnings("all")
+ ChildBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public B field3(final double field3) {
+ this.field3 = field3;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ }
+ public static ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl();
+ }
+ }
+ public static void test() {
+ Child x = Child.builder().field3(0.0).field1(5).item("").build();
+ }
+}
diff --git a/test/transform/resource/before/BuilderWithInherit.java b/test/transform/resource/before/SuperBuilderBasic.java
index dcc7b5ad..bf3eea64 100644
--- a/test/transform/resource/before/BuilderWithInherit.java
+++ b/test/transform/resource/before/SuperBuilderBasic.java
@@ -1,15 +1,15 @@
-import lombok.Builder;
+import lombok.experimental.SuperBuilder;
import lombok.Singular;
import java.util.List;
-public class BuilderWithInherit {
- @Builder(extensible = true)
+public class SuperBuilderBasic {
+ @SuperBuilder
public static class Parent {
int field1;
@Singular List<String> items;
}
- @Builder(inherit = true)
+ @SuperBuilder
public static class Child extends Parent {
double field3;
}