diff options
4 files changed, 121 insertions, 30 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 9a069c58..5d417bde 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -105,7 +105,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return ((Boolean) expr).booleanValue(); } - private static class BuilderFieldData { + public static class BuilderFieldData { TypeReference type; char[] rawName; char[] name; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3c07ac55..6c1b2cac 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -39,7 +39,9 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; @@ -48,6 +50,8 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; @@ -56,6 +60,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; @@ -87,6 +92,7 @@ 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.eclipse.handlers.HandleBuilder.BuilderFieldData; import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; @@ -98,40 +104,31 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { private static final char[] DEFAULT_PREFIX = "$default$".toCharArray(); private static final char[] SET_PREFIX = "$set".toCharArray(); private static final char[] SELF_METHOD_NAME = "self".toCharArray(); + private static final char[] TO_BUILDER_METHOD_NAME = "toBuilder".toCharArray(); + private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray(); + private static final char[] EMPTY_LIST = "emptyList".toCharArray(); private static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; - private static class BuilderFieldData { - TypeReference type; - char[] rawName; - char[] name; - char[] nameOfDefaultProvider; - char[] nameOfSetFlag; - SingularData singularData; - ObtainVia obtainVia; - EclipseNode obtainViaNode; - EclipseNode originalFieldNode; - - List<EclipseNode> createdFields = new ArrayList<EclipseNode>(); - } - @Override public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); long p = (long) ast.sourceStart << 32 | ast.sourceEnd; - SuperBuilder builderInstance = annotation.getInstance(); + SuperBuilder superbuilderAnnotation = annotation.getInstance(); - String builderMethodName = builderInstance.builderMethodName(); - String buildMethodName = builderInstance.buildMethodName(); + 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; - + + boolean toBuilder = superbuilderAnnotation.toBuilder(); + EclipseNode tdParent = annotationNode.up(); java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); @@ -293,7 +290,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); injectFieldAndMarkGenerated(builderType, cleanDecl); } - + + if (toBuilder) { + // Generate $fillValuesFrom() method in the abstract builder. + injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, typeParams, builderFields, ast)); + } + // 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)); @@ -532,6 +534,93 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { return out; } + /** + * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks + * like this: + * <pre> + * protected B $fillValuesFrom(final C instance) { + * super.$fillValuesFrom(instance); + * this.field(instance.field); + * return self(); + * } + * </pre> + */ + private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) { + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + out.selector = FILL_VALUES_METHOD_NAME; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + out.modifiers = ClassFileConstants.AccProtected; + if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; + out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); + + final String instanceVariableName = "instance"; + TypeReference builderType = createTypeReferenceWithTypeParameters(classGenericName, typeParams); + out.arguments = new Argument[] {new Argument(instanceVariableName.toCharArray(), 0, builderType, Modifier.FINAL)}; + + List<Statement> body = new ArrayList<Statement>(); + + if (inherited) { + // Call super. + MessageSend callToSuper = new MessageSend(); + callToSuper.receiver = new SuperReference(0, 0); + callToSuper.selector = FILL_VALUES_METHOD_NAME; + callToSuper.arguments = new Expression[] {new SingleNameReference(instanceVariableName.toCharArray(), 0)}; + body.add(callToSuper); + } + + // Call the builder's setter methods to fill the values from the instance. + for (BuilderFieldData bfd : builderFields) { + MessageSend exec = createSetterCallWithInstanceValue(bfd, instanceVariableName, tdParent, source); + body.add(exec); + } + + MessageSend returnCall = new MessageSend(); + returnCall.receiver = ThisReference.implicitThis(); + returnCall.selector = SELF_METHOD_NAME; + body.add(new ReturnStatement(returnCall, 0, 0)); + + out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]); + + return out; + } + + private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, final String instanceVariableName, EclipseNode type, ASTNode source) { + char[] setterName = bfd.name; + MessageSend ms = new MessageSend(); + Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; + + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { + char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray(); + for (int i = 0; i < tgt.length; i++) { + FieldReference fr = new FieldReference(fieldName, 0); + fr.receiver = new SingleNameReference(instanceVariableName.toCharArray(), 0); + tgt[i] = fr; + } + } else { + String obtainName = bfd.obtainVia.method(); + boolean obtainIsStatic = bfd.obtainVia.isStatic(); + for (int i = 0; i < tgt.length; i++) { + MessageSend obtainExpr = new MessageSend(); + obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0); + obtainExpr.selector = obtainName.toCharArray(); + if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)}; + tgt[i] = obtainExpr; + } + } + if (bfd.singularData == null) { + ms.arguments = tgt; + } else { + Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + MessageSend emptyList = new MessageSend(); + emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray()); + emptyList.selector = EMPTY_LIST; + ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])}; + } + ms.receiver = ThisReference.implicitThis(); + ms.selector = setterName; + return ms; + } + private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 01ac2794..c8727f93 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -236,8 +236,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { injectFieldAndMarkGenerated(builderType, uncleanField); } - // Generate $fillValuesFrom() method in the abstract builder. - injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields)); + if (toBuilder) { + // Generate $fillValuesFrom() method in the abstract builder. + injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderFields)); + } // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName)); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java index 5e5f9760..6dd06fb7 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java @@ -1,7 +1,7 @@ import java.util.List;
-public class SuperBuilderBasic {
- public static @lombok.experimental.SuperBuilder class Parent {
+public class SuperBuilderBasicToBuilder {
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
private @java.lang.SuppressWarnings("all") int field1;
private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
@@ -9,8 +9,8 @@ public class SuperBuilderBasic { super();
}
protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
- this.field1(instance.field1);
- this.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
+ field1(instance.field1);
+ items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
return self();
}
protected abstract @java.lang.SuppressWarnings("all") B self();
@@ -37,7 +37,7 @@ public class SuperBuilderBasic { return self();
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
- return (((("SuperBuilderBasic.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
+ return (((("SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
}
}
private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
@@ -76,7 +76,7 @@ public class SuperBuilderBasic { return new ParentBuilderImpl().$fillValuesFrom(this);
}
}
- public static @lombok.experimental.SuperBuilder class Child extends Parent {
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
private @java.lang.SuppressWarnings("all") double field3;
public ChildBuilder() {
@@ -84,7 +84,7 @@ public class SuperBuilderBasic { }
protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(C instance) {
super.$fillValuesFrom(instance);
- this.field3(instance.field3);
+ field3(instance.field3);
return self();
}
protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
@@ -94,7 +94,7 @@ public class SuperBuilderBasic { return self();
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
- return (((("SuperBuilderBasic.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
+ return (((("SuperBuilderBasicToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
}
}
private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
|