From 1dff81a0e0841f15a6ba805e022eb668e07335b4 Mon Sep 17 00:00:00 2001
From: Jan Rieke
Date: Sun, 22 Apr 2018 23:43:13 +0200
Subject: @SuperBuilder documentation for website
---
.../features/experimental/SuperBuilder.html | 69 ++++++++++++++++++++++
website/templates/features/experimental/index.html | 4 ++
2 files changed, 73 insertions(+)
create mode 100644 website/templates/features/experimental/SuperBuilder.html
(limited to 'website')
diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html
new file mode 100644
index 00000000..a1e8cb4d
--- /dev/null
+++ b/website/templates/features/experimental/SuperBuilder.html
@@ -0,0 +1,69 @@
+<#import "_features.html" as f>
+
+<@f.scaffold title="@SuperBuilder" logline="Bob now knows his ancestors: Builders with fields from superclasses, too. ">
+ <@f.history>
+
+ @SuperBuilder
was introduced as experimental feature in lombok v0.16.21.
+
+ @f.history>
+
+ <@f.overview>
+
+ The @SuperBuilder
annotation produces complex builder APIs for your classes.
+ In contrast to @Builder
, @SuperBuilder
also works with fields from superclasses.
+ However, it only works for types, and cannot be customized by providing a partial builder implementation.
+ Most importantly, it requires that all superclasses also have the @SuperBuilder
annotation.
+
+ @SuperBuilder
lets you automatically produce the code required to have your class be instantiable with code such as:
+ Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
+
+ @SuperBuilder
can generate so-called 'singular' methods for collection parameters/fields. For details, see the @Singular
documentation in @Builder
.
+
+ As lombok has no access to the fields of superclasses when generating the builder code, the methods for setting those superclass fields can only be in the builder of the superclass.
+ Therefore, a @SuperBuilder
must extend the @SuperBuilder
of the superclass in order to include those methods.
+ Furthermore, the generated builder code heavily relies on generics to avoid class casting when using the builder.
+ @SuperBuilder
generates a private constructor on the class that takes a builder instances as a parameter. This constructor sets the fields of the new instance to the values from the builder.
+
+ To ensure type-safety, @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).
+
+ The configurable aspects of builder are:
+
+ -
+ The build() method's name (default:
"build"
)
+ -
+ The builder() method's name (default:
"builder"
)
+
+
+ Example usage where all options are changed from their defaults:
+ @SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld")
+
+ @f.overview>
+
+ <@f.snippets name="Builder" />
+
+ <@f.confKeys>
+
+ lombok.builder.flagUsage
= [warning
| error
] (default: not set)
+
+ Lombok will flag any usage of @SuperBuilder
as a warning or error if configured.
+
+ lombok.singular.useGuava
= [true
| false
] (default: false)
+
+ If true
, lombok will use guava's ImmutableXxx
builders and types to implement java.util
collection interfaces, instead of creating implementations based on Collections.unmodifiableXxx
. You must ensure that guava is actually available on the classpath and buildpath if you use this setting. Guava is used automatically if your field/parameter has one of the guava ImmutableXxx
types.
+
+ lombok.singular.auto
= [true
| false
] (default: true)
+
+ If true
(which is the default), lombok automatically tries to singularize your identifier name by assuming that it is a common english plural. If false
, you must always explicitly specify the singular name, and lombok will generate an error if you don't (useful if you write your code in a language other than english).
+
+ @f.confKeys>
+
+ <@f.smallPrint>
+
+ @Singular support for java.util.NavigableMap/Set
only works if you are compiling with JDK1.8 or higher.
+
+ The sorted collections (java.util: SortedSet
, NavigableSet
, SortedMap
, NavigableMap
and guava: ImmutableSortedSet
, ImmutableSortedMap
) require that the type argument of the collection has natural order (implements java.util.Comparable
). There is no way to pass an explicit Comparator
to use in the builder.
+
+ An ArrayList
is used to store added elements as call methods of a @Singular
marked field, if the target collection is from the java.util
package, even if the collection is a set or map. Because lombok ensures that generated collections are compacted, a new backing instance of a set or map must be constructed anyway, and storing the data as an ArrayList
during the build process is more efficient that storing it as a map or set. This behaviour is not externally visible, an implementation detail of the current implementation of the java.util
recipes for @Singular
.
+
+ @f.smallPrint>
+@f.scaffold>
diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html
index 21e8fceb..309303a0 100644
--- a/website/templates/features/experimental/index.html
+++ b/website/templates/features/experimental/index.html
@@ -62,6 +62,10 @@
<@main.feature title="@Helper" href="Helper">
With a little help from my friends... Helper methods for java.
@main.feature>
+
+ <@main.feature title="@SuperBuilder" href="SuperBuilder">
+ Bob now knows his ancestors: Builders with fields from superclasses, too.
+ @main.feature>
<@f.confKeys>
--
cgit
From d11263a6c60008894213a1b465f2b16be3a03468 Mon Sep 17 00:00:00 2001
From: Jan Rieke
Date: Fri, 1 Jun 2018 13:48:38 +0200
Subject: support @Builder.Default
---
.../eclipse/handlers/HandleBuilderDefault.java | 6 +-
.../eclipse/handlers/HandleSuperBuilder.java | 40 ++++---
.../javac/handlers/HandleBuilderDefault.java | 6 +-
.../lombok/javac/handlers/HandleSuperBuilder.java | 42 ++++----
.../after-delombok/SuperBuilderWithDefaults.java | 119 +++++++++++++++++++++
.../after-ecj/SuperBuilderWithDefaults.java | 97 +++++++++++++++++
.../resource/before/SuperBuilderWithDefaults.java | 18 ++++
.../features/experimental/SuperBuilder.html | 4 +-
8 files changed, 290 insertions(+), 42 deletions(-)
create mode 100644 test/transform/resource/after-delombok/SuperBuilderWithDefaults.java
create mode 100644 test/transform/resource/after-ecj/SuperBuilderWithDefaults.java
create mode 100644 test/transform/resource/before/SuperBuilderWithDefaults.java
(limited to 'website')
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
index be2b986d..d0c597fd 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
@@ -31,6 +31,7 @@ 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.
@@ -39,8 +40,9 @@ public class HandleBuilderDefault extends EclipseAnnotationHandler {
private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
- 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 String SELF_METHOD = "self";
+ private static final char[] SET_PREFIX = "$set".toCharArray();
+ private static final char[] SELF_METHOD_NAME = "self".toCharArray();
private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
@@ -103,7 +103,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
TypeReference type;
char[] rawName;
char[] name;
- char[] nameOfDefaultProvider;
char[] nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
@@ -185,13 +184,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
if (isDefault != null) {
- bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
-
- MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast);
- if (md != null) {
- injectMethod(tdParent, md);
- }
+ // 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);
@@ -481,9 +483,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
long[] positions = new long[] {p, p};
assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
}
-
- Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p);
+ 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);
@@ -527,7 +537,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
- out.selector = SELF_METHOD.toCharArray();
+ out.selector = SELF_METHOD_NAME;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody;
if (override) {
@@ -539,7 +549,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
private MethodDeclaration generateSelfMethod(EclipseNode builderImplType) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult);
- out.selector = SELF_METHOD.toCharArray();
+ 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())};
@@ -665,7 +675,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
@Override public ReturnStatement get() {
MessageSend returnCall = new MessageSend();
returnCall.receiver = ThisReference.implicitThis();
- returnCall.selector = SELF_METHOD.toCharArray();
+ returnCall.selector = SELF_METHOD_NAME;
return new ReturnStatement(returnCall, 0, 0);
}
};
diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
index 4c4ba0e8..af45a620 100644
--- a/src/core/lombok/javac/handlers/HandleBuilderDefault.java
+++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
@@ -31,6 +31,7 @@ import lombok.Builder;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
+import lombok.experimental.SuperBuilder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
@@ -41,8 +42,9 @@ public class HandleBuilderDefault extends JavacAnnotationHandler {
JCExpression type;
Name rawName;
Name name;
- Name nameOfDefaultProvider;
Name nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
@@ -164,13 +164,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
}
if (isDefault != null) {
- bfd.nameOfDefaultProvider = tdParent.toName("$default$" + bfd.name);
bfd.nameOfSetFlag = tdParent.toName(bfd.name + "$set");
- JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams);
- recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
- if (md != null) {
- injectMethod(tdParent, md);
- }
+ // 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);
@@ -435,9 +438,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
}
JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName);
- JCExpression assign = maker.Assign(thisX, rhs);
-
- statements.append(maker.Exec(assign));
+ JCStatement assign = maker.Exec(maker.Assign(thisX, rhs));
+
+ // In case of @Builder.Default, only set the value if it really was set in the builder.
+ if (bfd.nameOfSetFlag != null) {
+ JCFieldAccess setField = maker.Select(maker.Ident(builderVariableName), bfd.nameOfSetFlag);
+ JCIf ifSet = maker.If(setField, assign, null);
+ statements.append(ifSet);
+ } else {
+ statements.append(assign);
+ }
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil());
@@ -575,18 +585,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.nil(), List.nil(), List.nil(), body, null);
}
- private JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List params) {
- JavacTreeMaker maker = fieldNode.getTreeMaker();
- JCVariableDecl field = (JCVariableDecl) fieldNode.get();
-
- JCStatement statement = maker.Return(field.init);
- field.init = null;
-
- JCBlock body = maker.Block(0, List.of(statement));
- int modifiers = Flags.PRIVATE | Flags.STATIC;
- return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.nil(), List.nil(), body, null);
- }
-
private void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) {
int len = builderFields.size();
java.util.List existing = new ArrayList();
diff --git a/test/transform/resource/after-delombok/SuperBuilderWithDefaults.java b/test/transform/resource/after-delombok/SuperBuilderWithDefaults.java
new file mode 100644
index 00000000..7b6b4578
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderWithDefaults.java
@@ -0,0 +1,119 @@
+import java.util.List;
+public class SuperBuilderWithDefaults {
+ public static class Parent {
+ private long millis = System.currentTimeMillis();
+ private N numberField = null;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ParentBuilder, B extends ParentBuilder> {
+ @java.lang.SuppressWarnings("all")
+ private boolean millis$set;
+ @java.lang.SuppressWarnings("all")
+ private long millis;
+ @java.lang.SuppressWarnings("all")
+ private boolean numberField$set;
+ @java.lang.SuppressWarnings("all")
+ private N numberField;
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B millis(final long millis) {
+ this.millis = millis;
+ millis$set = true;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B numberField(final N numberField) {
+ this.numberField = numberField;
+ numberField$set = true;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderWithDefaults.Parent.ParentBuilder(millis=" + this.millis + ", numberField=" + this.numberField + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private ParentBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ParentBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Parent build() {
+ return new Parent(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Parent(final ParentBuilder b) {
+ if (b.millis$set) this.millis = b.millis;
+ if (b.numberField$set) this.numberField = b.numberField;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ParentBuilder builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static class Child extends Parent {
+ private double doubleField = Math.PI;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ChildBuilder> extends Parent.ParentBuilder {
+ @java.lang.SuppressWarnings("all")
+ private boolean doubleField$set;
+ @java.lang.SuppressWarnings("all")
+ private double doubleField;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B doubleField(final double doubleField) {
+ this.doubleField = doubleField;
+ doubleField$set = true;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderWithDefaults.Child.ChildBuilder(super=" + super.toString() + ", doubleField=" + this.doubleField + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ChildBuilderImpl extends ChildBuilder {
+ @java.lang.SuppressWarnings("all")
+ private ChildBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ChildBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Child build() {
+ return new Child(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Child(final ChildBuilder, ?> b) {
+ super(b);
+ if (b.doubleField$set) this.doubleField = b.doubleField;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ChildBuilder, ?> builder() {
+ return new ChildBuilderImpl();
+ }
+ }
+ public static void test() {
+ Child x = Child.builder().doubleField(0.1).numberField(5).millis(1234567890L).build();
+ }
+}
diff --git a/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java b/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java
new file mode 100644
index 00000000..f0e3d8be
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderWithDefaults.java
@@ -0,0 +1,97 @@
+import java.util.List;
+public class SuperBuilderWithDefaults {
+ public static @lombok.experimental.SuperBuilder class Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> {
+ private @java.lang.SuppressWarnings("all") long millis;
+ private @java.lang.SuppressWarnings("all") boolean millis$set;
+ private @java.lang.SuppressWarnings("all") N numberField;
+ private @java.lang.SuppressWarnings("all") boolean numberField$set;
+ public ParentBuilder() {
+ super();
+ }
+ protected abstract @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B millis(final long millis) {
+ this.millis = millis;
+ millis$set = true;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B numberField(final N numberField) {
+ this.numberField = numberField;
+ numberField$set = true;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderWithDefaults.Parent.ParentBuilder(millis=" + this.millis) + ", numberField=") + this.numberField) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> {
+ private ParentBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() {
+ return new Parent(this);
+ }
+ }
+ private @lombok.Builder.Default long millis = System.currentTimeMillis();
+ private @lombok.Builder.Default N numberField = null;
+ protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) {
+ super();
+ if (b.millis$set)
+ this.millis = b.millis;
+ if (b.numberField$set)
+ this.numberField = b.numberField;
+ }
+ public static @java.lang.SuppressWarnings("all") ParentBuilder builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static @lombok.experimental.SuperBuilder class Child extends Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder> extends Parent.ParentBuilder {
+ private @java.lang.SuppressWarnings("all") double doubleField;
+ private @java.lang.SuppressWarnings("all") boolean doubleField$set;
+ public ChildBuilder() {
+ super();
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B doubleField(final double doubleField) {
+ this.doubleField = doubleField;
+ doubleField$set = true;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderWithDefaults.Child.ChildBuilder(super=" + super.toString()) + ", doubleField=") + this.doubleField) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder {
+ private ChildBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Child build() {
+ return new Child(this);
+ }
+ }
+ private @lombok.Builder.Default double doubleField = Math.PI;
+ protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder, ?> b) {
+ super(b);
+ if (b.doubleField$set)
+ this.doubleField = b.doubleField;
+ }
+ public static @java.lang.SuppressWarnings("all") ChildBuilder, ?> builder() {
+ return new ChildBuilderImpl();
+ }
+ }
+ public SuperBuilderWithDefaults() {
+ super();
+ }
+ public static void test() {
+ Child x = Child.builder().doubleField(0.1).numberField(5).millis(1234567890L).build();
+ }
+}
diff --git a/test/transform/resource/before/SuperBuilderWithDefaults.java b/test/transform/resource/before/SuperBuilderWithDefaults.java
new file mode 100644
index 00000000..d6859a78
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderWithDefaults.java
@@ -0,0 +1,18 @@
+import java.util.List;
+
+public class SuperBuilderWithDefaults {
+ @lombok.experimental.SuperBuilder
+ public static class Parent {
+ @lombok.Builder.Default private long millis = System.currentTimeMillis();
+ @lombok.Builder.Default private N numberField = null;
+ }
+
+ @lombok.experimental.SuperBuilder
+ public static class Child extends Parent {
+ @lombok.Builder.Default private double doubleField = Math.PI;
+ }
+
+ public static void test() {
+ Child x = Child.builder().doubleField(0.1).numberField(5).millis(1234567890L).build();
+ }
+}
diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html
index a1e8cb4d..a36ae499 100644
--- a/website/templates/features/experimental/SuperBuilder.html
+++ b/website/templates/features/experimental/SuperBuilder.html
@@ -23,6 +23,8 @@
Therefore, a @SuperBuilder
must extend the @SuperBuilder
of the superclass in order to include those methods.
Furthermore, the generated builder code heavily relies on generics to avoid class casting when using the builder.
@SuperBuilder
generates a private constructor on the class that takes a builder instances as a parameter. This constructor sets the fields of the new instance to the values from the builder.
+
+ @SuperBuilder
is not compatible with @Builder
.
To ensure type-safety, @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).
@@ -43,7 +45,7 @@
<@f.confKeys>
- lombok.builder.flagUsage
= [warning
| error
] (default: not set)
+ lombok.superBuilder.flagUsage
= [warning
| error
] (default: not set)
Lombok will flag any usage of @SuperBuilder
as a warning or error if configured.
--
cgit
From d4fa7c583bcef17663a7b0259c01ce0792cba29c Mon Sep 17 00:00:00 2001
From: Jan Rieke
Date: Fri, 1 Jun 2018 16:31:52 +0200
Subject: next lombok version is 0.16.23
---
website/templates/features/experimental/SuperBuilder.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'website')
diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html
index a36ae499..2ddbccf3 100644
--- a/website/templates/features/experimental/SuperBuilder.html
+++ b/website/templates/features/experimental/SuperBuilder.html
@@ -3,7 +3,7 @@
<@f.scaffold title="@SuperBuilder" logline="Bob now knows his ancestors: Builders with fields from superclasses, too. ">
<@f.history>
- @SuperBuilder
was introduced as experimental feature in lombok v0.16.21.
+ @SuperBuilder
was introduced as experimental feature in lombok v0.16.23.
@f.history>
--
cgit
From 3256ab6fc71b2b52fe2aedcbf0f59f8f95a0d7e5 Mon Sep 17 00:00:00 2001
From: Jan Rieke
Date: Wed, 6 Jun 2018 09:04:55 +0200
Subject: next lombok version is 1.18.1
---
website/templates/features/experimental/SuperBuilder.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'website')
diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html
index 2ddbccf3..4801d373 100644
--- a/website/templates/features/experimental/SuperBuilder.html
+++ b/website/templates/features/experimental/SuperBuilder.html
@@ -3,7 +3,7 @@
<@f.scaffold title="@SuperBuilder" logline="Bob now knows his ancestors: Builders with fields from superclasses, too. ">
<@f.history>
- @SuperBuilder
was introduced as experimental feature in lombok v0.16.23.
+ @SuperBuilder
was introduced as experimental feature in lombok v1.18.1.
@f.history>
--
cgit
From 19ad4fd57d32afad1a33f20613fbb2e7607cfee0 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Wed, 13 Jun 2018 23:21:00 +0200
Subject: [SuperBuilder] code review, style update, making it JDK1.6
compatible, documentation review, bugfix for setter auto-presuming chaining
even when you didn’t configure it to chain.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../eclipse/handlers/EclipseSingularsRecipes.java | 45 +--
src/core/lombok/eclipse/handlers/HandleSetter.java | 2 +-
.../eclipse/handlers/HandleSuperBuilder.java | 322 +++++++++------------
.../singulars/EclipseGuavaSingularizer.java | 14 +-
.../EclipseJavaUtilListSetSingularizer.java | 16 +-
.../singulars/EclipseJavaUtilMapSingularizer.java | 16 +-
src/core/lombok/javac/handlers/HandleSetter.java | 17 +-
.../lombok/javac/handlers/HandleSuperBuilder.java | 312 +++++++++-----------
.../javac/handlers/JavacSingularsRecipes.java | 43 +--
.../handlers/singulars/JavacGuavaSingularizer.java | 13 +-
.../JavacJavaUtilListSetSingularizer.java | 15 +-
.../singulars/JavacJavaUtilMapSingularizer.java | 15 +-
.../features/experimental/SuperBuilder.html | 11 +-
website/templates/features/experimental/index.html | 2 +-
14 files changed, 379 insertions(+), 464 deletions(-)
(limited to 'website')
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
index e24cd5b5..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-2017 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
@@ -30,7 +30,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Supplier;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -65,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 singularizers = new HashMap();
private final TypeLibrary singularizableTypes = new TypeLibrary();
@@ -220,37 +227,35 @@ public class EclipseSingularsRecipes {
}
public abstract List generateFields(SingularData data, EclipseNode builderType);
-
+
/**
- * Generates the singular, plural, and clear methods for the given
- * {@link SingularData}.
- * Uses the given builderType
as return type if
- * chain == true
, void
otherwise. If you need more
- * control over the return type and value, use
- * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, Supplier, Supplier)}.
+ * 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) {
- // TODO: Make these lambdas when switching to a source level >= 1.8.
- Supplier returnType = new Supplier() {
- @Override public TypeReference get() {
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
}
};
- Supplier returnStatement = new Supplier() {
- @Override public ReturnStatement get() {
+
+ StatementMaker returnStatementMaker = new StatementMaker() {
+ @Override public ReturnStatement make() {
return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
}
};
- generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement);
+
+ generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
}
/**
- * Generates the singular, plural, and clear methods for the given
- * {@link SingularData}.
- * Uses the given returnType
and returnStatement
for the generated methods.
+ * 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, Supplier returnType, Supplier returnStatement);
-
+ 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 statements, char[] targetVariableName, String builderVariable);
public boolean requiresCleaning() {
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 434939a6..92489c09 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -212,7 +212,7 @@ public class HandleSetter extends EclipseAnnotationHandler {
return createSetter(parent, deprecate, fieldNode, name, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam);
}
- static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, TypeReference returnType, ReturnStatement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) {
+ static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
ASTNode source = sourceNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 1100cbf0..12fc4761 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -30,7 +30,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -85,20 +84,21 @@ import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
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 {
-
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;
@@ -108,49 +108,41 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
ObtainVia obtainVia;
EclipseNode obtainViaNode;
EclipseNode originalFieldNode;
-
+
List createdFields = new ArrayList();
}
-
+
@Override
public void handle(AnnotationValues 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;
- }
-
+
+ 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 builderFields = new ArrayList();
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 allFields = new ArrayList();
boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent));
@@ -158,31 +150,31 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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
@@ -199,26 +191,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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);
-
+
// 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 on the annotated class, use "C2" and "B3" for our builder.
java.util.List typeParamStrings = new ArrayList();
- for (TypeParameter typeParam : typeParams) {
- typeParamStrings.add(typeParam.toString());
- }
+ 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[] {
@@ -229,12 +219,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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
@@ -246,7 +236,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
} 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);
@@ -262,21 +252,20 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
// 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);
-
+ superclassBuilderClass != null);
+
// Create the abstract builder class.
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass,
- typeParams, ast, classGenericName, builderGenericName);
-
+ 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) {
@@ -296,7 +285,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
}
}
-
+
// Generate the fields in the abstract builder class that hold the values for the instance.
generateBuilderFields(builderType, builderFields, ast);
if (addCleaning) {
@@ -306,16 +295,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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) {
java.util.List fieldNodes = new ArrayList();
@@ -328,16 +317,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
injectMethod(builderType, md);
}
}
-
- if (addCleaning) {
- injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
- }
-
+
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
- // Abstract classes to not get the Builder implementation nor the builder() method.
+ // Only non-abstract classes get the Builder implementation.
return;
}
-
+
// Create the builder implementation class.
EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName);
if (builderImplType == null) {
@@ -346,30 +333,28 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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);
- }
+ 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.
@@ -384,15 +369,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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.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);
@@ -401,9 +386,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
builder.name = builderImplClass.toCharArray();
// Add type params if there are any.
- if (typeParams != null && typeParams.length > 0) {
- builder.typeParameters = copyTypeParams(typeParams, source);
- }
+ 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.
@@ -423,7 +407,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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.
@@ -441,14 +425,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
*/
private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List 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) {
@@ -468,17 +452,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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 statements = new ArrayList();
List nullChecks = new ArrayList();
-
+
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");
@@ -507,51 +491,48 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
}
}
-
+
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);
- }
+ 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())};
- }
+ 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;
@@ -562,35 +543,33 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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)};
- }
+ 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) {
+ 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 statements = new ArrayList();
-
+
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.
@@ -600,16 +579,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return out;
}
-
+
private MethodDeclaration generateCleanMethod(List builderFields, EclipseNode builderType, ASTNode source) {
List statements = new ArrayList();
-
+
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));
@@ -622,15 +601,13 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return decl;
}
-
+
private void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) {
List existing = new ArrayList();
for (EclipseNode child : builderType.down()) {
- if (child.getKind() == Kind.FIELD) {
- existing.add(child);
- }
+ 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));
@@ -638,14 +615,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
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 (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;
@@ -666,70 +639,63 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
}
}
-
+
private void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) {
boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
-
- // TODO: Make these lambdas when switching to a source level >= 1.8.
- Supplier returnType = new Supplier() {
- @Override public TypeReference get() {
+
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
return new SingleTypeReference(builderGenericName.toCharArray(), 0);
}
};
- Supplier returnStatement = new Supplier() {
- @Override public ReturnStatement get() {
+
+ 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, returnType.get(), returnStatement.get(), sourceNode);
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode);
} else {
- bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnType, returnStatement);
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker);
}
}
- private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, ReturnStatement returnStatement, EclipseNode sourceNode) {
+ 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;
- }
+ 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;
- }
+ if (!(existing[i] instanceof MethodDeclaration)) continue;
char[] existingName = existing[i].selector;
- if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) {
- return;
- }
+ 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.emptyList(), Collections.emptyList());
injectMethod(builderType, setter);
}
-
+
private void addObtainVia(BuilderFieldData bfd, EclipseNode node) {
for (EclipseNode child : node.down()) {
- if (!annotationTypeMatches(ObtainVia.class, child)) {
- continue;
- }
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
AnnotationValues 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.
@@ -738,9 +704,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
*/
private SingularData getSingularData(EclipseNode node, ASTNode source) {
for (EclipseNode child : node.down()) {
- if (!annotationTypeMatches(Singular.class, child)) {
- continue;
- }
+ if (!annotationTypeMatches(Singular.class, child)) continue;
+
char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
AnnotationValues ann = createAnnotation(Singular.class, child);
String explicitSingular = ann.getInstance().value();
@@ -757,7 +722,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
}
char[] singularName = explicitSingular.toCharArray();
-
+
TypeReference type = ((AbstractVariableDeclaration) node.get()).type;
TypeReference[] typeArgs = null;
String typeName;
@@ -766,59 +731,51 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
typeName = new String(((ParameterizedSingleTypeReference) type).token);
} else if (type instanceof ParameterizedQualifiedTypeReference) {
TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments;
- if (tr != null) {
- typeArgs = tr[tr.length - 1];
- }
+ 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(".");
- }
+ 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.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
}
-
+
return null;
}
-
+
private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) {
- if (!typeParamStrings.contains(classGenericName)) {
- return classGenericName;
- }
+ if (!typeParamStrings.contains(classGenericName)) return classGenericName;
int counter = 2;
- while (typeParamStrings.contains(classGenericName + counter)) {
- counter++;
- }
+ 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;
+ typeArgss = ((ParameterizedQualifiedTypeReference) typeRef).typeArguments;
} else if (typeRef instanceof ParameterizedSingleTypeReference) {
- typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference)typeRef).typeArguments};
+ typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference) typeRef).typeArguments};
}
TypeReference[] typeArgs = new TypeReference[0];
if (typeArgss != null && typeArgss.length > 0) {
@@ -826,7 +783,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
return typeArgs;
}
-
+
private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) {
if (typeParams.length > 0) {
TypeReference[] typerefs = new TypeReference[typeParams.length];
@@ -837,7 +794,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
} else {
return new SingleTypeReference(referenceName.toCharArray(), 0);
}
-
}
private TypeReference[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) {
@@ -850,32 +806,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler {
}
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];
- }
+ 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;
- }
+ if (child.getKind() != Kind.TYPE) continue;
TypeDeclaration td = (TypeDeclaration) child.get();
- if (Arrays.equals(td.name, c)) {
- return child;
- }
+ 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);
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
index d6c63944..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-2017 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
@@ -27,7 +27,6 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
import lombok.core.GuavaTypeMap;
import lombok.core.LombokImmutableList;
@@ -35,6 +34,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;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
@@ -53,7 +54,6 @@ 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;
@@ -97,10 +97,10 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) {
- generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generateClearMethod(deprecate, returnType.get(), returnStatement.get(), 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(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
index 0851757f..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-2017 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
@@ -27,11 +27,12 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
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;
@@ -46,7 +47,6 @@ 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;
@@ -88,15 +88,15 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement);
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generateClearMethod(deprecate, returnType.get(), returnStatement.get(), 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(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
index 976d9265..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-2017 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
@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
@@ -46,7 +45,6 @@ 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;
@@ -59,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 {
@@ -133,15 +133,15 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
return Arrays.asList(keyFieldNode, valueFieldNode);
}
- @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement);
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent);
- generateClearMethod(deprecate, returnType.get(), returnStatement.get(), 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(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 50967121..be3337db 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -210,12 +210,17 @@ public class HandleSetter extends JavacAnnotationHandler {
}
public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List onMethod, List onParam) {
- JCExpression returnType = cloneSelfType(field);
- JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
+ JCExpression returnType = null;
+ JCReturn returnStatement = null;
+ if (shouldReturnThis) {
+ returnType = cloneSelfType(field);
+ returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
+ }
+
return createSetter(access, deprecate, field, treeMaker, setterName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam);
}
-
- public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, JCExpression methodType, JCReturn returnStatement, JavacNode source, List onMethod, List onParam) {
+
+ public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List onMethod, List onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -252,9 +257,7 @@ public class HandleSetter extends JavacAnnotationHandler {
returnStatement = null;
}
- if (returnStatement != null) {
- statements.append(returnStatement);
- }
+ if (returnStatement != null) statements.append(returnStatement);
JCBlock methodBody = treeMaker.Block(0, statements.toList());
List methodGenericParams = List.nil();
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 1835bd48..1707fb0e 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -26,7 +26,6 @@ import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
-import java.util.function.Supplier;
import org.mangosdk.spi.ProviderFor;
@@ -43,7 +42,6 @@ import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
-import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -70,15 +68,16 @@ import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
+import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
@ProviderFor(JavacAnnotationHandler.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 JavacAnnotationHandler {
-
private static final String SELF_METHOD = "self";
-
+
private static class BuilderFieldData {
JCExpression type;
Name rawName;
@@ -88,49 +87,41 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
ObtainVia obtainVia;
JavacNode obtainViaNode;
JavacNode originalFieldNode;
-
+
java.util.List createdFields = new ArrayList();
}
-
+
@Override
public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
SuperBuilder superbuilderAnnotation = annotation.getInstance();
deleteAnnotationIfNeccessary(annotationNode, SuperBuilder.class);
-
+
String builderMethodName = superbuilderAnnotation.builderMethodName();
String buildMethodName = superbuilderAnnotation.buildMethodName();
-
- if (builderMethodName == null) {
- builderMethodName = "builder";
- }
- if (buildMethodName == null) {
- buildMethodName = "build";
- }
-
- if (!checkName("builderMethodName", builderMethodName, annotationNode)) {
- return;
- }
- if (!checkName("buildMethodName", buildMethodName, annotationNode)) {
- return;
- }
-
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+
JavacNode tdParent = annotationNode.up();
-
+
java.util.List builderFields = new ArrayList();
JCExpression returnType;
List typeParams = List.nil();
List thrownExceptions = List.nil();
List superclassTypeParams = List.nil();
-
+
boolean addCleaning = false;
-
+
if (!(tdParent.get() instanceof JCClassDecl)) {
annotationNode.addError("@SuperBuilder is only supported on types.");
return;
}
-
+
// Gather all fields of the class that should be set by the builder.
JCClassDecl td = (JCClassDecl) tdParent.get();
ListBuffer allFields = new ListBuffer();
@@ -145,24 +136,22 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
bfd.originalFieldNode = fieldNode;
-
+
if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
isDefault = null;
}
-
+
if (fd.init == null && isDefault != null) {
isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
isDefault = null;
}
-
+
if (fd.init != null && isDefault == null) {
- if (isFinal) {
- continue;
- }
+ if (isFinal) continue;
fieldNode.addWarning("@SuperBuilder 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 = tdParent.toName(bfd.name + "$set");
// The @Builder annotation removes the initializing expression on the field and moves
@@ -179,7 +168,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
builderFields.add(bfd);
allFields.append(fieldNode);
}
-
+
// Set the names of the builder classes.
String builderClassName = td.name.toString() + "Builder";
String builderImplClassName = builderClassName + "Impl";
@@ -187,14 +176,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
JCExpression superclassBuilderClassExpression = null;
if (extendsClause instanceof JCTypeApply) {
// Remember the type arguments, because we need them for the extends clause of our abstract builder class.
- superclassTypeParams = ((JCTypeApply)extendsClause).getTypeArguments();
+ superclassTypeParams = ((JCTypeApply) extendsClause).getTypeArguments();
// A class name with a generics type, e.g., "Superclass".
- extendsClause = ((JCTypeApply)extendsClause).getType();
+ extendsClause = ((JCTypeApply) extendsClause).getType();
}
if (extendsClause instanceof JCFieldAccess) {
Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier();
String superclassBuilderClassName = superclassClassName + "Builder";
- superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess)extendsClause,
+ superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess) extendsClause,
tdParent.toName(superclassBuilderClassName));
} else if (extendsClause != null) {
String superclassBuilderClassName = extendsClause.toString() + "Builder";
@@ -202,24 +191,22 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
}
// If there is no superclass, superclassBuilderClassExpression is still == null at this point.
// You can use it to check whether to inherit or not.
-
+
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
-
+
// 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 on the annotated class, use "C2" and "B3" for our builder.
java.util.List typeParamStrings = new ArrayList();
- for (JCTypeParameter typeParam : typeParams) {
- typeParamStrings.add(typeParam.getName().toString());
- }
+ for (JCTypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.getName().toString());
classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);
-
+
thrownExceptions = List.nil();
-
+
// 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) {
@@ -239,7 +226,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
}
}
}
-
+
// Create the abstract builder class.
JavacNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
@@ -249,7 +236,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
return;
}
-
+
// Generate the fields in the abstract builder class that hold the values for the instance.
generateBuilderFields(builderType, builderFields, ast);
if (addCleaning) {
@@ -257,37 +244,32 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
injectFieldAndMarkGenerated(builderType, uncleanField);
}
-
+
// Generate abstract self() and build() methods in the abstract builder.
injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName));
injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName));
-
+
// 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.
java.util.List fieldNodes = new ArrayList();
- for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
- }
+ for (BuilderFieldData bfd : builderFields) fieldNodes.addAll(bfd.createdFields);
+
// Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
JCMethodDecl toStringMethod = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast);
- if (toStringMethod != null) {
- injectMethod(builderType, toStringMethod);
- }
-
+ if (toStringMethod != null) injectMethod(builderType, toStringMethod);
+
// If clean methods are requested, add them now.
- if (addCleaning) {
- injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
- }
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
-
+
if ((td.mods.flags & Flags.ABSTRACT) == 0) {
// Only non-abstract classes get the Builder implementation.
-
+
// Create the builder implementation class.
JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
if (builderImplType == null) {
@@ -296,48 +278,45 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
return;
}
-
+
// Create a simple constructor for the BuilderImpl class.
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode);
- if (cd != null) {
- injectMethod(builderImplType, cd);
- }
-
+ if (cd != null) injectMethod(builderImplType, cd);
+
// Create the self() and build() methods in the BuilderImpl.
injectMethod(builderImplType, generateSelfMethod(builderImplType));
injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
-
+
recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
}
-
+
// Generate a constructor in the annotated class that takes a builder as argument.
generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
superclassBuilderClassExpression != null);
-
+
if ((td.mods.flags & Flags.ABSTRACT) == 0) {
// Only non-abstract classes get the Builder implementation and the builder() method.
-
+
// Add the builder() method to the annotated class.
// Allow users to specify their own builder() methods, e.g., to provide default values.
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
- if (builderMethod != null) {
- injectMethod(tdParent, builderMethod);
- }
+ if (builderMethod != null) injectMethod(tdParent, builderMethod);
}
}
}
-
+
/**
* Creates and returns the abstract builder class and injects it into the annotated class.
*/
private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
JCExpression superclassBuilderClassExpression, List typeParams,
List superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
+
JavacTreeMaker maker = tdParent.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
-
+
// Keep any type params of the annotated class.
ListBuffer allTypeParams = new ListBuffer();
allTypeParams.addAll(copyTypeParams(source, typeParams));
@@ -356,7 +335,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList());
allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.of(typeApply)));
-
+
JCExpression extending = null;
if (superclassBuilderClassExpression != null) {
// If the annotated class extends another class, we want this builder to extend the builder of the superclass.
@@ -367,18 +346,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList());
}
-
+
JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.nil());
return injectType(tdParent, builder);
}
-
+
/**
* Creates and returns the concrete builder implementation class and injects it into the annotated class.
*/
private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List typeParams, JCAnnotation ast) {
JavacTreeMaker maker = tdParent.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
-
+
// Extend the abstract builder.
JCExpression extending = maker.Ident(tdParent.toName(builderAbstractClass));
// Add any type params of the annotated class.
@@ -400,7 +379,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
typeParamsForBuilder.add(annotatedClass);
typeParamsForBuilder.add(builderImplClassExpression);
extending = maker.TypeApply(extending, typeParamsForBuilder.toList());
-
+
JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.nil(), List.nil());
return injectType(tdParent, builder);
}
@@ -422,22 +401,20 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
*/
private void generateBuilderBasedConstructor(JavacNode typeNode, List typeParams, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) {
JavacTreeMaker maker = typeNode.getTreeMaker();
-
+
AccessLevel level = AccessLevel.PROTECTED;
-
+
ListBuffer nullChecks = new ListBuffer();
ListBuffer statements = new ListBuffer();
-
+
Name builderVariableName = typeNode.toName("b");
for (BuilderFieldData bfd : builderFields) {
List nonNulls = findAnnotations(bfd.originalFieldNode, NON_NULL_PATTERN);
if (!nonNulls.isEmpty()) {
JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, source);
- if (nullCheck != null) {
- nullChecks.append(nullCheck);
- }
+ if (nullCheck != null) nullChecks.append(nullCheck);
}
-
+
JCExpression rhs;
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name, "b");
@@ -446,7 +423,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName);
}
JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName);
-
+
JCStatement assign = maker.Exec(maker.Assign(thisX, rhs));
// In case of @Builder.Default, only set the value if it really was set in the builder.
@@ -458,9 +435,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
statements.append(assign);
}
}
-
+
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil());
-
+
// Create a constructor that has just the builder as parameter.
ListBuffer params = new ListBuffer();
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
@@ -475,7 +452,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParamsForBuilderParameter.toList());
JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null);
params.append(param);
-
+
if (callBuilderBasedSuperConstructor) {
// The first statement must be the call to the super constructor.
JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(),
@@ -483,29 +460,27 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
List.of(maker.Ident(builderVariableName)));
statements.prepend(maker.Exec(callToSuperConstructor));
}
-
+
JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""),
null, List.nil(), params.toList(), List.nil(),
maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext());
-
+
injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
}
-
+
private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) {
JavacTreeMaker maker = type.getTreeMaker();
-
+
ListBuffer typeArgs = new ListBuffer();
- for (JCTypeParameter typeParam : typeParams) {
- typeArgs.append(maker.Ident(typeParam.name));
- }
-
+ for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
+
JCExpression call = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.nil(), null);
JCStatement statement = maker.Return(call);
-
+
JCBlock body = maker.Block(0, List.of(statement));
int modifiers = Flags.PUBLIC;
modifiers |= Flags.STATIC;
-
+
// Add any type params of the annotated class to the return type.
ListBuffer typeParameterNames = new ListBuffer();
typeParameterNames.addAll(typeParameterNames(maker, typeParams));
@@ -514,10 +489,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
typeParameterNames.add(wildcard);
typeParameterNames.add(wildcard);
JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
-
+
return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null);
}
-
+
private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
JavacTreeMaker maker = type.getTreeMaker();
List annotations = List.nil();
@@ -528,24 +503,24 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations);
Name name = type.toName(SELF_METHOD);
JCExpression returnType = maker.Ident(type.toName(builderGenericName));
-
+
return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null);
}
-
+
private JCMethodDecl generateSelfMethod(JavacNode builderImplType) {
JavacTreeMaker maker = builderImplType.getTreeMaker();
-
+
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.nil());
JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, List.of(overrideAnnotation));
Name name = builderImplType.toName(SELF_METHOD);
JCExpression returnType = maker.Ident(builderImplType.toName(builderImplType.getName()));
-
+
JCStatement statement = maker.Return(maker.Ident(builderImplType.toName("this")));
JCBlock body = maker.Block(0, List.of(statement));
-
+
return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), body, null);
}
-
+
private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) {
JavacTreeMaker maker = type.getTreeMaker();
List annotations = List.nil();
@@ -556,53 +531,51 @@ public class HandleSuperBuilder extends JavacAnnotationHandler {
JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations);
Name name = type.toName(methodName);
JCExpression returnType = maker.Ident(type.toName(classGenericName));
-
+
return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null);
}
private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List thrownExceptions) {
JavacTreeMaker maker = type.getTreeMaker();
-
+
JCExpression call;
ListBuffer statements = new ListBuffer();
-
+
// Use a constructor that only has this builder as parameter.
List builderArg = List.of(maker.Ident(type.toName("this")));
call = maker.NewClass(null, List.nil(), returnType, builderArg, null);
statements.append(maker.Return(call));
-
+
JCBlock body = maker.Block(0, statements.toList());
-
+
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil());
JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
-
+
return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null);
}
-
+
private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) {
JavacTreeMaker maker = type.getTreeMaker();
ListBuffer