From f46aa53e8c592f562ef5dc21e65e9c74c98a8ecc Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 10 Feb 2017 17:02:29 +0100 Subject: builder-based constructors for type @Builders [Eclipse] --- src/core/lombok/Builder.java | 17 +++++ .../lombok/eclipse/handlers/HandleBuilder.java | 56 ++++++++++++++--- .../lombok/eclipse/handlers/HandleConstructor.java | 72 ++++++++++++++++++---- 3 files changed, 125 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index 7a965486..855e96e8 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -122,6 +122,23 @@ public @interface Builder { */ String builderClassName() default ""; + /** + * If true, the generated builder class will extend the builder of the + * superclass. In this way, the builder will also contain methods for fields + * from the superclass. Note that both this builder and the superclass' + * builder must be a type {@code @Builder}; this feature does neither work + * for constructor nor method {@code @Builder}s. + */ + boolean inherit() default false; + + /** + * Name of the builder class in the superclass. Only relevant if + * {@code inherit = true} (see {@link #inherit()}). + * + * Default {@code (SuperclassTypeName)Builder}. + */ + String superclassBuilderClassName() default ""; + /** * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. * Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index afa03538..4f152fe3 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -139,6 +139,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); + + boolean inherit = builderInstance.inherit(); + String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + String toBuilderMethodName = "toBuilder"; boolean toBuilder = builderInstance.toBuilder(); List typeArgsForToBuilder = null; @@ -146,6 +150,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) builderMethodName = "build"; if (builderClassName == null) builderClassName = ""; + if (superclassBuilderClassName == null) { + superclassBuilderClassName = ""; + } if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; @@ -189,15 +196,26 @@ public class HandleBuilder extends EclipseAnnotationHandler { allFields.add(fieldNode); } - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null, - Collections.emptyList(), annotationNode); + if (builderClassName.isEmpty()) { + builderClassName = new String(td.name) + "Builder"; + } + if (superclassBuilderClassName.isEmpty() && td.superclass != null) { + superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; + } + + boolean callSuperConstructor = inherit && td.superclass != null; + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, true, + Collections.emptyList(), annotationNode, builderClassName, callSuperConstructor); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; thrownExceptions = null; nameOfStaticBuilderMethod = null; - if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder"; } else if (parent.get() instanceof ConstructorDeclaration) { + if (inherit) { + annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); + return; + } ConstructorDeclaration cd = (ConstructorDeclaration) parent.get(); if (cd.typeParameters != null && cd.typeParameters.length > 0) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); @@ -212,6 +230,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = new String(cd.selector) + "Builder"; } else if (parent.get() instanceof MethodDeclaration) { + if (inherit) { + annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); + return; + } MethodDeclaration md = (MethodDeclaration) parent.get(); tdParent = parent.up(); isStatic = md.isStatic(); @@ -341,7 +363,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast); + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, inherit ? superclassBuilderClassName : null); } else { TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { @@ -396,7 +418,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, null, - annotationNode, Collections.emptyList()); + annotationNode, Collections.emptyList(), null, false); if (cd != null) injectMethod(builderType, cd); } @@ -405,7 +427,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); + boolean useBuilderBasedConstructor = parent.get() instanceof TypeDeclaration; + MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, useBuilderBasedConstructor); if (md != null) injectMethod(builderType, md); } @@ -514,7 +537,13 @@ public class HandleBuilder extends EclipseAnnotationHandler { return decl; } - public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { + /** + * @param useBuilderBasedConstructor + * if true, the {@code build()} method will use a constructor + * that takes the builder instance as parameter (instead of a + * constructor with all relevant fields as parameters) + */ + public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, boolean useBuilderBasedConstructor) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); @@ -554,7 +583,13 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (staticName == null) { AllocationExpression allocationStatement = new AllocationExpression(); allocationStatement.type = copyType(out.returnType); - allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]); + if (useBuilderBasedConstructor) { + // Use a constructor that only has this builder as parameter. + allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; + } else { + // Use a constructor with all the fields. + allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]); + } statements.add(new ReturnStatement(allocationStatement, 0, 0)); } else { MessageSend invoke = new MessageSend(); @@ -672,7 +707,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { return null; } - public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) { + public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source, String parentBuilderClassName) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -680,6 +715,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic; builder.typeParameters = copyTypeParams(typeParams, source); builder.name = builderClassName.toCharArray(); + if (parentBuilderClassName != null) { + builder.superclass = new SingleTypeReference(parentBuilderClassName.toCharArray(), 0); + } builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index a3b0585d..eb11e6e4 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -64,10 +64,12 @@ import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.LongLiteral; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +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.SingleMemberAnnotation; 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.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -95,7 +97,7 @@ public class HandleConstructor { List fields = force ? findFinalFields(typeNode) : Collections.emptyList(); List onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode); - new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode); + new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode, null, false); } } @@ -121,7 +123,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - suppressConstructorProperties, onConstructor, annotationNode); + suppressConstructorProperties, onConstructor, annotationNode, null, false); } } @@ -183,7 +185,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - suppressConstructorProperties, onConstructor, annotationNode); + suppressConstructorProperties, onConstructor, annotationNode, null, false); } } @@ -205,23 +207,34 @@ public class HandleConstructor { EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode); + generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode, null, false); } public void generateAllArgsConstructor( EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode); + generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode, null, false); } public enum SkipIfConstructorExists { YES, NO, I_AM_BUILDER; } + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ public void generateConstructor( EclipseNode typeNode, AccessLevel level, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, - Boolean suppressConstructorProperties, List onConstructor, EclipseNode sourceNode) { + Boolean suppressConstructorProperties, List onConstructor, EclipseNode sourceNode, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { ASTNode source = sourceNode.get(); boolean staticConstrRequired = staticName != null && !staticName.equals(""); @@ -256,7 +269,7 @@ public class HandleConstructor { ConstructorDeclaration constr = createConstructor( staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, - suppressConstructorProperties, sourceNode, onConstructor); + suppressConstructorProperties, sourceNode, onConstructor, builderClassnameAsParameter, callBuilderBasedSuperConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.emptyList() : fields, source); @@ -296,9 +309,28 @@ public class HandleConstructor { return new Annotation[] { ann }; } + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassnameAsParameter != null}. + */ public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection fields, boolean allToDefault, - Boolean suppressConstructorProperties, EclipseNode sourceNode, List onConstructor) { + Boolean suppressConstructorProperties, EclipseNode sourceNode, List onConstructor, + String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + + if (builderClassnameAsParameter != null && builderClassnameAsParameter.isEmpty()) { + builderClassnameAsParameter = null; + } + if (callBuilderBasedSuperConstructor && builderClassnameAsParameter == null) { + type.addError("Calling a builder-based superclass constructor ('callBuilderBasedSuperConstructor') requires a non-empty 'builderClassnameAsParameter' value."); + } ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); @@ -320,7 +352,12 @@ public class HandleConstructor { constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + if (callBuilderBasedSuperConstructor) { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; + } else { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + } constructor.constructorCall.sourceStart = source.sourceStart; constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; @@ -343,7 +380,16 @@ public class HandleConstructor { int e = (int) p; thisX.receiver = new ThisReference(s, e); - Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p); + Expression assignmentExpr; + if (allToDefault) { + assignmentExpr = getDefaultExpr(field.type, s, e); + } else if (builderClassnameAsParameter != null) { + char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; + long[] positions = new long[] {p, p}; + assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); + } else { + assignmentExpr = new SingleNameReference(fieldName, p); + } Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); @@ -364,7 +410,11 @@ public class HandleConstructor { nullChecks.addAll(assigns); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); + if (builderClassnameAsParameter != null) { + constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; + } else { + constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); + } /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; -- cgit From 4ec2e5135c373242dd00151eedf40997b0d75fe7 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 15 Feb 2017 15:25:41 +0100 Subject: builder-based constructors for type @Builders [javac] --- src/core/lombok/javac/handlers/HandleBuilder.java | 66 +++++++++++++++--- .../lombok/javac/handlers/HandleConstructor.java | 79 +++++++++++++++++++--- 2 files changed, 126 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 8588fc16..32bf7949 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -22,7 +22,10 @@ package lombok.javac.handlers; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.Consumer; import javax.lang.model.element.Modifier; @@ -50,6 +53,7 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; +import jdk.nashorn.internal.runtime.Context; import lombok.AccessLevel; import lombok.Builder; import lombok.Builder.ObtainVia; @@ -64,6 +68,7 @@ import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.javac.JavacTreeMaker.TypeTag; import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; @@ -103,12 +108,19 @@ public class HandleBuilder extends JavacAnnotationHandler { String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); String toBuilderMethodName = "toBuilder"; + + boolean inherit = builderInstance.inherit(); + String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + boolean toBuilder = builderInstance.toBuilder(); java.util.List typeArgsForToBuilder = null; if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; + if (superclassBuilderClassName == null) { + superclassBuilderClassName = ""; + } if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; @@ -155,15 +167,27 @@ public class HandleBuilder extends JavacAnnotationHandler { allFields.append(fieldNode); } - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode); + if (builderClassName.isEmpty()) { + builderClassName = td.name.toString() + "Builder"; + } + + JCTree extendsClause = Javac.getExtendsClause(td); + if (superclassBuilderClassName.isEmpty() && extendsClause != null) { + superclassBuilderClassName = extendsClause + "Builder"; + } + + boolean callSuperConstructor = inherit && extendsClause != null; + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode, builderClassName, callSuperConstructor); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); nameOfBuilderMethod = null; - if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("")) { - JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); + if (inherit) { + annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); + return; + } JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); if (!jmd.typarams.isEmpty()) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); return; @@ -177,6 +201,10 @@ public class HandleBuilder extends JavacAnnotationHandler { nameOfBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null) { + if (inherit) { + annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); + return; + } tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); @@ -298,7 +326,7 @@ public class HandleBuilder extends JavacAnnotationHandler { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast); + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, inherit ? superclassBuilderClassName : null); } else { JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { @@ -350,7 +378,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), false, null, annotationNode); + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), false, null, annotationNode, null, false); if (cd != null) injectMethod(builderType, cd); } @@ -359,7 +387,8 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); + boolean useBuilderBasedConstructor = parent.get() instanceof JCClassDecl; + JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, useBuilderBasedConstructor); if (md != null) injectMethod(builderType, md); } @@ -496,7 +525,13 @@ public class HandleBuilder extends JavacAnnotationHandler { */ } - private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning) { + /** + * @param useBuilderBasedConstructor + * if true, the {@code build()} method will use a constructor + * that takes the builder instance as parameter (instead of a + * constructor with all relevant fields as parameters) + */ + private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -525,7 +560,14 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (builderName == null) { - call = maker.NewClass(null, List.nil(), returnType, args.toList(), null); + if (useBuilderBasedConstructor) { + // 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); + } else { + // Use a constructor with all the fields. + call = maker.NewClass(null, List.nil(), returnType, args.toList(), null); + } statements.append(maker.Return(call)); } else { @@ -629,12 +671,16 @@ public class HandleBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast, String parentBuilderClassName) { JavacTreeMaker maker = tdParent.getTreeMaker(); int modifiers = Flags.PUBLIC; if (isStatic) modifiers |= Flags.STATIC; JCModifiers mods = maker.Modifiers(modifiers); - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.nil(), List.nil()); + JCExpression extending = null; + if (parentBuilderClassName != null) { + extending = maker.Ident(tdParent.toName(parentBuilderClassName)); + } + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), extending, List.nil(), List.nil()); return injectType(tdParent, builder); } diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index ef4ba088..3481b2c3 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -23,6 +23,9 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; + +import java.util.Arrays; + import static lombok.javac.Javac.*; import lombok.AccessLevel; @@ -51,6 +54,7 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 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.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; @@ -78,7 +82,7 @@ public class HandleConstructor { String staticName = ann.staticName(); boolean force = ann.force(); List fields = force ? findFinalFields(typeNode) : List.nil(); - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, null, annotationNode); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, null, annotationNode, null, false); } } @@ -103,7 +107,7 @@ public class HandleConstructor { suppressConstructorProperties = suppress; } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode, null, false); } } @@ -152,7 +156,7 @@ public class HandleConstructor { boolean suppress = ann.suppressConstructorProperties(); suppressConstructorProperties = suppress; } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode, null, false); } } @@ -188,7 +192,7 @@ public class HandleConstructor { } public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) { - generateConstructor(typeNode, level, List.nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source); + generateConstructor(typeNode, level, List.nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source, null, false); } public enum SkipIfConstructorExists { @@ -196,10 +200,21 @@ public class HandleConstructor { } public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) { - generateConstructor(typeNode, level, List.nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source); + generateConstructor(typeNode, level, List.nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source, null, false); } - public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) { + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { boolean staticConstrRequired = staticName != null && !staticName.equals(""); if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return; @@ -228,7 +243,7 @@ public class HandleConstructor { } } - JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, suppressConstructorProperties, source); + JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, suppressConstructorProperties, source, builderClassnameAsParameter, callBuilderBasedSuperConstructor); ListBuffer argTypes = new ListBuffer(); for (JavacNode fieldNode : fields) { Type mirror = getMirrorForFieldType(fieldNode); @@ -262,7 +277,25 @@ public class HandleConstructor { mods.annotations = mods.annotations.append(annotation); } - public static JCMethodDecl createConstructor(AccessLevel level, List onConstructor, JavacNode typeNode, List fields, boolean allToDefault, Boolean suppressConstructorProperties, JavacNode source) { + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassnameAsParameter != null}. + */ + public static JCMethodDecl createConstructor(AccessLevel level, List onConstructor, JavacNode typeNode, List fields, boolean allToDefault, Boolean suppressConstructorProperties, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + if (builderClassnameAsParameter != null && builderClassnameAsParameter.isEmpty()) { + builderClassnameAsParameter = null; + } + if (callBuilderBasedSuperConstructor && builderClassnameAsParameter == null) { + source.addError("Calling a builder-based superclass constructor ('callBuilderBasedSuperConstructor') requires a non-empty 'builderClassnameAsParameter' value."); + } + JavacTreeMaker maker = typeNode.getTreeMaker(); boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; @@ -280,6 +313,7 @@ public class HandleConstructor { ListBuffer assigns = new ListBuffer(); ListBuffer params = new ListBuffer(); + Name builderVariableName = typeNode.toName("b"); for (JavacNode fieldNode : fields) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); Name fieldName = removePrefixFromField(fieldNode); @@ -296,7 +330,17 @@ public class HandleConstructor { } } JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName); - JCExpression assign = maker.Assign(thisX, allToDefault ? getDefaultExpr(maker, field.vartype) : maker.Ident(fieldName)); + + JCExpression rhs; + if (allToDefault) { + rhs = getDefaultExpr(maker, field.vartype); + } else if (builderClassnameAsParameter != null) { + rhs = maker.Select(maker.Ident(builderVariableName), rawName); + } else { + rhs = maker.Ident(fieldName); + } + JCExpression assign = maker.Assign(thisX, rhs); + assigns.append(maker.Exec(assign)); } @@ -306,6 +350,23 @@ public class HandleConstructor { } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); + if (builderClassnameAsParameter != null) { + // Create a constructor that has just the builder as parameter. + params.clear(); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + Name builderClassname = typeNode.toName(builderClassnameAsParameter); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); + params.append(param); + } + + if (callBuilderBasedSuperConstructor) { + // The first statement must be the call to the super constructor. + JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), + maker.Ident(typeNode.toName("super")), + List.of(maker.Ident(builderVariableName))); + assigns.prepend(maker.Exec(callToSuperConstructor)); + } + return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""), null, List.nil(), params.toList(), List.nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); -- cgit From bd51fa8484fde587eef10fc58e4527f26ec16430 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 13 Mar 2017 14:32:57 +0100 Subject: activate extendable builders explicitly (ensure backwards compatibility) --- src/core/lombok/Builder.java | 23 ++++++++++++++++++---- .../lombok/eclipse/handlers/HandleBuilder.java | 15 +++++++++++--- src/core/lombok/javac/handlers/HandleBuilder.java | 18 +++++++++++++---- 3 files changed, 45 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index 855e96e8..9cf82191 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -123,14 +123,29 @@ public @interface Builder { String builderClassName() default ""; /** - * If true, the generated builder class will extend the builder of the + * If true, the generated builder class will extend the {@code @Builder} of the * superclass. In this way, the builder will also contain methods for fields - * from the superclass. Note that both this builder and the superclass' - * builder must be a type {@code @Builder}; this feature does neither work - * for constructor nor method {@code @Builder}s. + * from the superclass.
+ * Note that both this builder and the superclass' builder must be a type + * {@code @Builder}; this feature does neither work for constructor nor + * method {@code @Builder}s. The parent {@code @Builder} must be + * {@link #extendable()}.
+ * Implies {@link #extendable()}. */ boolean inherit() default false; + /** + * If true, the generated builder pattern will be extendable by + * {@code @Builder}s of subclasses, using the {@link #inherit()} feature. + * This is achieved by generating a special constructor that takes a builder + * instance as parameter (instead of an {@link AllArgsConstructor}).
+ * Note that this builder must be a type {@code @Builder}; this feature does + * neither work for constructor nor method {@code @Builder}s.
+ * If {@link #inherit()} {@code == true}, {@link #extendable()} will + * automatically be {@code true}. + */ + boolean extendable() default false; + /** * Name of the builder class in the superclass. Only relevant if * {@code inherit = true} (see {@link #inherit()}). diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 4f152fe3..4bfc89f4 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -141,6 +141,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { String builderClassName = builderInstance.builderClassName(); boolean inherit = builderInstance.inherit(); + boolean extendable = inherit || builderInstance.extendable(); // inherit implies extendable String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); String toBuilderMethodName = "toBuilder"; @@ -203,9 +204,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; } - boolean callSuperConstructor = inherit && td.superclass != null; + boolean callBuilderBasedSuperConstructor = inherit && td.superclass != null; new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, true, - Collections.emptyList(), annotationNode, builderClassName, callSuperConstructor); + Collections.emptyList(), annotationNode, extendable ? builderClassName : null, callBuilderBasedSuperConstructor); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; @@ -216,6 +217,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } + if (extendable) { + annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); + return; + } ConstructorDeclaration cd = (ConstructorDeclaration) parent.get(); if (cd.typeParameters != null && cd.typeParameters.length > 0) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); @@ -234,6 +239,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } + if (extendable) { + annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); + return; + } MethodDeclaration md = (MethodDeclaration) parent.get(); tdParent = parent.up(); isStatic = md.isStatic(); @@ -427,7 +436,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - boolean useBuilderBasedConstructor = parent.get() instanceof TypeDeclaration; + boolean useBuilderBasedConstructor = parent.get() instanceof TypeDeclaration && extendable; MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, useBuilderBasedConstructor); if (md != null) injectMethod(builderType, md); } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 32bf7949..7701d85c 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -110,6 +110,7 @@ public class HandleBuilder extends JavacAnnotationHandler { String toBuilderMethodName = "toBuilder"; boolean inherit = builderInstance.inherit(); + boolean extendable = inherit || builderInstance.extendable(); // inherit implies extendable String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); boolean toBuilder = builderInstance.toBuilder(); @@ -176,8 +177,8 @@ public class HandleBuilder extends JavacAnnotationHandler { superclassBuilderClassName = extendsClause + "Builder"; } - boolean callSuperConstructor = inherit && extendsClause != null; - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode, builderClassName, callSuperConstructor); + boolean callBuilderBasedSuperConstructor = inherit && extendsClause != null; + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode, extendable ? builderClassName : null, callBuilderBasedSuperConstructor); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; @@ -187,7 +188,12 @@ public class HandleBuilder extends JavacAnnotationHandler { if (inherit) { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; - } JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); + } + if (extendable) { + annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); + return; + } + JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); if (!jmd.typarams.isEmpty()) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); return; @@ -205,6 +211,10 @@ public class HandleBuilder extends JavacAnnotationHandler { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } + if (extendable) { + annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); + return; + } tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); @@ -387,7 +397,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - boolean useBuilderBasedConstructor = parent.get() instanceof JCClassDecl; + boolean useBuilderBasedConstructor = parent.get() instanceof JCClassDecl && extendable; JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, useBuilderBasedConstructor); if (md != null) injectMethod(builderType, md); } -- cgit From f51bbce3e396a0151bc0242d00e250f2bc720316 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 13 Mar 2017 15:59:46 +0100 Subject: do not assign values in build() method for extendable builders (WIP) --- .../lombok/eclipse/handlers/HandleBuilder.java | 43 ++++++++++++---------- src/core/lombok/javac/handlers/HandleBuilder.java | 41 +++++++++++---------- 2 files changed, 45 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 4bfc89f4..74ef57a7 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -556,31 +556,34 @@ public class HandleBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); + List args = new ArrayList(); - if (addCleaning) { - FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); - thisUnclean.receiver = new ThisReference(0, 0); - Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT); - MessageSend invokeClean = new MessageSend(); - invokeClean.selector = CLEAN_METHOD_NAME; - statements.add(new IfStatement(notClean, invokeClean, 0, 0)); - } + // Extendable builders assign their values in the constructor, not in this build() method. + if (!useBuilderBasedConstructor) { + if (addCleaning) { + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); + thisUnclean.receiver = new ThisReference(0, 0); + Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT); + MessageSend invokeClean = new MessageSend(); + invokeClean.selector = CLEAN_METHOD_NAME; + statements.add(new IfStatement(notClean, invokeClean, 0, 0)); + } - for (BuilderFieldData bfd : builderFields) { - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name); + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name); + } } - } - List args = new ArrayList(); - for (BuilderFieldData bfd : builderFields) { - args.add(new SingleNameReference(bfd.name, 0L)); - } + for (BuilderFieldData bfd : builderFields) { + args.add(new SingleNameReference(bfd.name, 0L)); + } - if (addCleaning) { - FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); - thisUnclean.receiver = new ThisReference(0, 0); - statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); + if (addCleaning) { + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); + thisUnclean.receiver = new ThisReference(0, 0); + statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); + } } out.modifiers = ClassFileConstants.AccPublic; diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 7701d85c..41ff64e0 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -546,27 +546,30 @@ public class HandleBuilder extends JavacAnnotationHandler { JCExpression call; ListBuffer statements = new ListBuffer(); - - if (addCleaning) { - JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean"))); - JCStatement invokeClean = maker.Exec(maker.Apply(List.nil(), maker.Ident(type.toName("$lombokClean")), List.nil())); - JCIf ifUnclean = maker.If(notClean, invokeClean, null); - statements.append(ifUnclean); - } - - for (BuilderFieldData bfd : builderFields) { - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name); - } - } - ListBuffer args = new ListBuffer(); - for (BuilderFieldData bfd : builderFields) { - args.append(maker.Ident(bfd.name)); - } + + // Extendable builders assign their values in the constructor, not in this build() method. + if (!useBuilderBasedConstructor) { + if (addCleaning) { + JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean"))); + JCStatement invokeClean = maker.Exec(maker.Apply(List.nil(), maker.Ident(type.toName("$lombokClean")), List.nil())); + JCIf ifUnclean = maker.If(notClean, invokeClean, null); + statements.append(ifUnclean); + } + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name); + } + } - if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); + for (BuilderFieldData bfd : builderFields) { + args.append(maker.Ident(bfd.name)); + } + + if (addCleaning) { + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); + } } if (builderName == null) { -- cgit From 2e0d4183c236a7b42f2f6ff0e4a68812bba3e559 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 14 Mar 2017 11:56:41 +0100 Subject: moved generation of builder-based constructor to HandleBuilder --- src/core/lombok/javac/handlers/HandleBuilder.java | 93 +++++++++++++++++++++- .../lombok/javac/handlers/HandleConstructor.java | 27 ++----- 2 files changed, 99 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 43520656..be8c9765 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -43,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCLiteral; 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.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCStatement; @@ -63,6 +64,7 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.handlers.HandlerUtil; +import lombok.delombok.LombokOptionsFactory; import lombok.experimental.NonFinal; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; @@ -87,6 +89,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } private static class BuilderFieldData { + JavacNode fieldNode; JCExpression type; Name rawName; Name name; @@ -159,6 +162,7 @@ public class HandleBuilder extends JavacAnnotationHandler { // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; BuilderFieldData bfd = new BuilderFieldData(); + bfd.fieldNode = fieldNode; bfd.rawName = fd.name; bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.vartype; @@ -178,7 +182,11 @@ public class HandleBuilder extends JavacAnnotationHandler { } boolean callBuilderBasedSuperConstructor = inherit && extendsClause != null; - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode, extendable ? builderClassName : null, callBuilderBasedSuperConstructor); + if (extendable) { + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); + } else { + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); + } returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; @@ -505,6 +513,89 @@ public class HandleBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, List.of(statement)); return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); } + + /** + * Generates a constructor that has a builder as the only parameter. + * + * @param builderClassnameAsParameter + * the only parameter of the constructor will be a builder with + * this classname; the constructor will use the values within + * this builder to assign the fields of new instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List builderFields, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { + source.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); + } + + JavacTreeMaker maker = typeNode.getTreeMaker(); + + AccessLevel level = AccessLevel.PROTECTED; + boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; + if (isEnum) { + level = AccessLevel.PRIVATE; + } + + ListBuffer nullChecks = new ListBuffer(); + ListBuffer assigns = new ListBuffer(); + + Name builderVariableName = typeNode.toName("b"); + for (BuilderFieldData field : builderFields) { + List nonNulls = findAnnotations(field.fieldNode, NON_NULL_PATTERN); + if (!nonNulls.isEmpty()) { + JCStatement nullCheck = generateNullCheck(maker, field.fieldNode, source); + if (nullCheck != null) { + nullChecks.append(nullCheck); + } + } + + if (field.singularData != null && field.singularData.getSingularizer() != null) { + field.singularData.getSingularizer().appendBuildCode(field.singularData, field.fieldNode, field.type, assigns, field.name); + } else { + JCFieldAccess thisX = maker.Select(maker.Ident(field.fieldNode.toName("this")), field.rawName); + + JCExpression rhs; + rhs = maker.Select(maker.Ident(builderVariableName), field.rawName); + JCExpression assign = maker.Assign(thisX, rhs); + + assigns.append(maker.Exec(assign)); + } + } + + JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); + + // Add ConstructorProperties + JCExpression constructorPropertiesType = chainDots(typeNode, "java", "beans", "ConstructorProperties"); + ListBuffer fieldNames = new ListBuffer(); + fieldNames.append(maker.Literal(builderVariableName.toString())); + JCExpression fieldNamesArray = maker.NewArray(null, List.nil(), fieldNames.toList()); + JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); + mods.annotations = mods.annotations.append(annotation); + + // Create a constructor that has just the builder as parameter. + ListBuffer params = new ListBuffer(); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + Name builderClassname = typeNode.toName(builderClassnameAsParameter); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); + params.append(param); + + if (callBuilderBasedSuperConstructor) { + // The first statement must be the call to the super constructor. + JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), + maker.Ident(typeNode.toName("super")), + List.of(maker.Ident(builderVariableName))); + assigns.prepend(maker.Exec(callToSuperConstructor)); + } + + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""), + null, List.nil(), params.toList(), List.nil(), + maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); + + injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { JavacTreeMaker maker = type.getTreeMaker(); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 06ac74bd..eed3ebd3 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -24,8 +24,6 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import java.util.Arrays; - import static lombok.javac.Javac.*; import lombok.AccessLevel; @@ -82,7 +80,7 @@ public class HandleConstructor { String staticName = ann.staticName(); boolean force = ann.force(); List fields = force ? findFinalFields(typeNode) : List.nil(); - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode, null, false); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode); } } @@ -104,7 +102,7 @@ public class HandleConstructor { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode, null, false); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } @@ -150,7 +148,7 @@ public class HandleConstructor { if (annotation.isExplicit("suppressConstructorProperties")) { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode, null, false); + new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } @@ -186,7 +184,7 @@ public class HandleConstructor { } public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) { - generateConstructor(typeNode, level, List.nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, source, null, false); + generateConstructor(typeNode, level, List.nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, source); } public enum SkipIfConstructorExists { @@ -194,21 +192,10 @@ public class HandleConstructor { } public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) { - generateConstructor(typeNode, level, List.nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, source, null, false); + generateConstructor(typeNode, level, List.nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, source); } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassAsParameter != null}. - */ - public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) { boolean staticConstrRequired = staticName != null && !staticName.equals(""); if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return; @@ -237,7 +224,7 @@ public class HandleConstructor { } } - JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source, builderClassnameAsParameter, callBuilderBasedSuperConstructor); + JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source, null, false); ListBuffer argTypes = new ListBuffer(); for (JavacNode fieldNode : fields) { Type mirror = getMirrorForFieldType(fieldNode); -- cgit From 549e1b962210b8d4b0610d39789122ec537dcd89 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 14 Mar 2017 12:13:50 +0100 Subject: reverted HandleConstructor to original state --- src/core/lombok/javac/handlers/HandleBuilder.java | 2 +- .../lombok/javac/handlers/HandleConstructor.java | 54 ++-------------------- 2 files changed, 4 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index be8c9765..fea1110e 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -394,7 +394,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), false, annotationNode, null, false); + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), false, annotationNode); if (cd != null) injectMethod(builderType, cd); } diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index eed3ebd3..4e90b639 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -23,7 +23,6 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; - import static lombok.javac.Javac.*; import lombok.AccessLevel; @@ -52,7 +51,6 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 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.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; @@ -224,7 +222,7 @@ public class HandleConstructor { } } - JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source, null, false); + JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source); ListBuffer argTypes = new ListBuffer(); for (JavacNode fieldNode : fields) { Type mirror = getMirrorForFieldType(fieldNode); @@ -258,25 +256,7 @@ public class HandleConstructor { mods.annotations = mods.annotations.append(annotation); } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassnameAsParameter != null}. - */ - public static JCMethodDecl createConstructor(AccessLevel level, List onConstructor, JavacNode typeNode, List fields, boolean allToDefault, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - if (builderClassnameAsParameter != null && builderClassnameAsParameter.isEmpty()) { - builderClassnameAsParameter = null; - } - if (callBuilderBasedSuperConstructor && builderClassnameAsParameter == null) { - source.addError("Calling a builder-based superclass constructor ('callBuilderBasedSuperConstructor') requires a non-empty 'builderClassnameAsParameter' value."); - } - + public static JCMethodDecl createConstructor(AccessLevel level, List onConstructor, JavacNode typeNode, List fields, boolean allToDefault, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; @@ -294,7 +274,6 @@ public class HandleConstructor { ListBuffer assigns = new ListBuffer(); ListBuffer params = new ListBuffer(); - Name builderVariableName = typeNode.toName("b"); for (JavacNode fieldNode : fields) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); Name fieldName = removePrefixFromField(fieldNode); @@ -311,17 +290,7 @@ public class HandleConstructor { } } JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName); - - JCExpression rhs; - if (allToDefault) { - rhs = getDefaultExpr(maker, field.vartype); - } else if (builderClassnameAsParameter != null) { - rhs = maker.Select(maker.Ident(builderVariableName), rawName); - } else { - rhs = maker.Ident(fieldName); - } - JCExpression assign = maker.Assign(thisX, rhs); - + JCExpression assign = maker.Assign(thisX, allToDefault ? getDefaultExpr(maker, field.vartype) : maker.Ident(fieldName)); assigns.append(maker.Exec(assign)); } @@ -331,23 +300,6 @@ public class HandleConstructor { } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); - if (builderClassnameAsParameter != null) { - // Create a constructor that has just the builder as parameter. - params.clear(); - long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); - Name builderClassname = typeNode.toName(builderClassnameAsParameter); - JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); - params.append(param); - } - - if (callBuilderBasedSuperConstructor) { - // The first statement must be the call to the super constructor. - JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), - maker.Ident(typeNode.toName("super")), - List.of(maker.Ident(builderVariableName))); - assigns.prepend(maker.Exec(callToSuperConstructor)); - } - return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""), null, List.nil(), params.toList(), List.nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); -- cgit From cd2e2f25690c46f01b876074682e87af369d15d3 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 14 Mar 2017 16:22:26 +0100 Subject: moved code only relevant to builders to HandleBuilder (Eclipse) reverted HandleConstructor to original state moved generation of builder-based constructor to HandleBuilder --- .../lombok/eclipse/handlers/HandleBuilder.java | 116 ++++++++++++++++++++- .../lombok/eclipse/handlers/HandleConstructor.java | 72 ++----------- 2 files changed, 124 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 58e08b0d..ebeb82ea 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -25,6 +25,7 @@ import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -39,6 +40,7 @@ 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.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -49,6 +51,7 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; @@ -80,6 +83,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; @@ -99,6 +103,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } private static class BuilderFieldData { + EclipseNode fieldNode; TypeReference type; char[] rawName; char[] name; @@ -188,6 +193,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; BuilderFieldData bfd = new BuilderFieldData(); + bfd.fieldNode = fieldNode; bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; @@ -205,8 +211,13 @@ public class HandleBuilder extends EclipseAnnotationHandler { } boolean callBuilderBasedSuperConstructor = inherit && td.superclass != null; - new HandleConstructor().generateConstructor(tdParent, extendable ? AccessLevel.PROTECTED : AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, - Collections.emptyList(), annotationNode, extendable ? builderClassName : null, callBuilderBasedSuperConstructor); + if (extendable) { + generateConstructor(tdParent, builderFields, annotationNode, + builderClassName, callBuilderBasedSuperConstructor); + } else { + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, + Collections.emptyList(), annotationNode); + } returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; @@ -427,7 +438,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, - annotationNode, Collections.emptyList(), null, false); + annotationNode, Collections.emptyList()); if (cd != null) injectMethod(builderType, cd); } @@ -524,6 +535,105 @@ public class HandleBuilder extends EclipseAnnotationHandler { } + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + private void generateConstructor(EclipseNode typeNode, List builderFields, EclipseNode sourceNode, + String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + + if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { + typeNode.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); + } + + AccessLevel level = AccessLevel.PROTECTED; + ASTNode source = sourceNode.get(); + + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); + long p = (long) source.sourceStart << 32 | source.sourceEnd; + + boolean isEnum = (((TypeDeclaration) typeNode.get()).modifiers & ClassFileConstants.AccEnum) != 0; + if (isEnum) { + level = AccessLevel.PRIVATE; + } + +// boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + + constructor.modifiers = toEclipseModifier(level); + constructor.selector = typeDeclaration.name; + if (callBuilderBasedSuperConstructor) { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; + } else { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + } + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; + constructor.thrownExceptions = null; + constructor.typeParameters = null; + constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.arguments = null; + + List assigns = new ArrayList(); + List nullChecks = new ArrayList(); + + for (BuilderFieldData fieldNode : builderFields) { + char[] fieldName = removePrefixFromField(fieldNode.fieldNode); + FieldReference thisX = new FieldReference(fieldNode.rawName, p); + int s = (int) (p >> 32); + int e = (int) p; + thisX.receiver = new ThisReference(s, e); + + if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { + fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, assigns, fieldNode.name); + } + + Expression assignmentExpr; + if (builderClassnameAsParameter != null) { + char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; + long[] positions = new long[] {p, p}; + assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); + } else { + assignmentExpr = new SingleNameReference(fieldName, p); + } + + Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); + assigns.add(assignment); + Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.fieldNode.get(), NON_NULL_PATTERN); + if (nonNulls.length != 0) { + Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.fieldNode.get(), sourceNode); + if (nullCheck != null) { + nullChecks.add(nullCheck); + } + } + } + + nullChecks.addAll(assigns); + constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); + constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; + + // Generate annotations that must be put on the generated method, and attach them. +// Annotation[] constructorProperties = null; +// if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !HandleConstructor.isLocalType(typeNode)) { +// constructorProperties = HandleConstructor.createConstructorProperties(source, builderFields); +// } + + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + + injectMethod(typeNode, constructor); + } + private MethodDeclaration generateCleanMethod(List builderFields, EclipseNode builderType, ASTNode source) { List statements = new ArrayList(); diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index a8b823b2..49b09231 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -64,12 +64,10 @@ import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.LongLiteral; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; -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.SingleMemberAnnotation; 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.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -97,7 +95,7 @@ public class HandleConstructor { List fields = force ? findFinalFields(typeNode) : Collections.emptyList(); List onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); - new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode, null, false); + new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } } @@ -120,7 +118,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - onConstructor, annotationNode, null, false); + onConstructor, annotationNode); } } @@ -179,7 +177,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - onConstructor, annotationNode, null, false); + onConstructor, annotationNode); } } @@ -201,34 +199,23 @@ public class HandleConstructor { EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode, null, false); + generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode); } public void generateAllArgsConstructor( EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode, null, false); + generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode); } public enum SkipIfConstructorExists { YES, NO, I_AM_BUILDER; } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassAsParameter != null}. - */ public void generateConstructor( EclipseNode typeNode, AccessLevel level, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, - List onConstructor, EclipseNode sourceNode, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + List onConstructor, EclipseNode sourceNode) { ASTNode source = sourceNode.get(); boolean staticConstrRequired = staticName != null && !staticName.equals(""); @@ -263,7 +250,7 @@ public class HandleConstructor { ConstructorDeclaration constr = createConstructor( staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, - sourceNode, onConstructor, builderClassnameAsParameter, callBuilderBasedSuperConstructor); + sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.emptyList() : fields, source); @@ -303,28 +290,9 @@ public class HandleConstructor { return new Annotation[] { ann }; } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassnameAsParameter != null}. - */ public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection fields, boolean allToDefault, - EclipseNode sourceNode, List onConstructor, - String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - - if (builderClassnameAsParameter != null && builderClassnameAsParameter.isEmpty()) { - builderClassnameAsParameter = null; - } - if (callBuilderBasedSuperConstructor && builderClassnameAsParameter == null) { - type.addError("Calling a builder-based superclass constructor ('callBuilderBasedSuperConstructor') requires a non-empty 'builderClassnameAsParameter' value."); - } + EclipseNode sourceNode, List onConstructor) { ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); @@ -345,12 +313,7 @@ public class HandleConstructor { constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; - if (callBuilderBasedSuperConstructor) { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); - constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; - } else { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - } + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); constructor.constructorCall.sourceStart = source.sourceStart; constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; @@ -373,16 +336,7 @@ public class HandleConstructor { int e = (int) p; thisX.receiver = new ThisReference(s, e); - Expression assignmentExpr; - if (allToDefault) { - assignmentExpr = getDefaultExpr(field.type, s, e); - } else if (builderClassnameAsParameter != null) { - char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; - long[] positions = new long[] {p, p}; - assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); - } else { - assignmentExpr = new SingleNameReference(fieldName, p); - } + Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p); Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); @@ -403,11 +357,7 @@ public class HandleConstructor { nullChecks.addAll(assigns); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - if (builderClassnameAsParameter != null) { - constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; - } else { - constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); - } + constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; -- cgit From e79022226b18fae8cb5e0864a405c8c7c5d4a559 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 14 Mar 2017 16:56:33 +0100 Subject: singularizer's build code for any variable name (not only "this") --- src/core/lombok/javac/handlers/HandleBuilder.java | 39 ++++++++++------------ .../javac/handlers/JavacSingularsRecipes.java | 8 ++--- .../handlers/singulars/JavacGuavaSingularizer.java | 6 ++-- .../singulars/JavacJavaUtilListSingularizer.java | 14 ++++---- .../singulars/JavacJavaUtilMapSingularizer.java | 8 ++--- .../singulars/JavacJavaUtilSetSingularizer.java | 8 ++--- .../singulars/JavacJavaUtilSingularizer.java | 26 +++++++-------- 7 files changed, 52 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index fea1110e..f46a95fd 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -22,10 +22,7 @@ package lombok.javac.handlers; import java.lang.annotation.Annotation; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; -import java.util.function.Consumer; import javax.lang.model.element.Modifier; @@ -54,7 +51,6 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; -import jdk.nashorn.internal.runtime.Context; import lombok.AccessLevel; import lombok.Builder; import lombok.Builder.ObtainVia; @@ -64,13 +60,11 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.handlers.HandlerUtil; -import lombok.delombok.LombokOptionsFactory; import lombok.experimental.NonFinal; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; -import lombok.javac.JavacTreeMaker.TypeTag; import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; @@ -540,29 +534,30 @@ public class HandleBuilder extends JavacAnnotationHandler { } ListBuffer nullChecks = new ListBuffer(); - ListBuffer assigns = new ListBuffer(); + ListBuffer statements = new ListBuffer(); Name builderVariableName = typeNode.toName("b"); - for (BuilderFieldData field : builderFields) { - List nonNulls = findAnnotations(field.fieldNode, NON_NULL_PATTERN); + for (BuilderFieldData bfd : builderFields) { + List nonNulls = findAnnotations(bfd.fieldNode, NON_NULL_PATTERN); if (!nonNulls.isEmpty()) { - JCStatement nullCheck = generateNullCheck(maker, field.fieldNode, source); + JCStatement nullCheck = generateNullCheck(maker, bfd.fieldNode, source); if (nullCheck != null) { nullChecks.append(nullCheck); } } - if (field.singularData != null && field.singularData.getSingularizer() != null) { - field.singularData.getSingularizer().appendBuildCode(field.singularData, field.fieldNode, field.type, assigns, field.name); + JCExpression rhs; + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.fieldNode, bfd.type, statements, bfd.name, "b"); + rhs = maker.Ident(bfd.singularData.getPluralName()); } else { - JCFieldAccess thisX = maker.Select(maker.Ident(field.fieldNode.toName("this")), field.rawName); - - JCExpression rhs; - rhs = maker.Select(maker.Ident(builderVariableName), field.rawName); - JCExpression assign = maker.Assign(thisX, rhs); - - assigns.append(maker.Exec(assign)); + rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); } + JCFieldAccess thisX = maker.Select(maker.Ident(bfd.fieldNode.toName("this")), bfd.rawName); + + JCExpression assign = maker.Assign(thisX, rhs); + + statements.append(maker.Exec(assign)); } JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); @@ -587,12 +582,12 @@ public class HandleBuilder extends JavacAnnotationHandler { JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), maker.Ident(typeNode.toName("super")), List.of(maker.Ident(builderVariableName))); - assigns.prepend(maker.Exec(callToSuperConstructor)); + 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(assigns).toList()), null), source.get(), typeNode.getContext()); + maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext()); injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } @@ -648,7 +643,7 @@ public class HandleBuilder extends JavacAnnotationHandler { for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); } } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index c6d601bd..33501349 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -187,7 +187,7 @@ public class JavacSingularsRecipes { public abstract java.util.List generateFields(SingularData data, JavacNode builderType, JCTree source); public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain); - public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName); + public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable); public boolean requiresCleaning() { try { @@ -266,9 +266,9 @@ public class JavacSingularsRecipes { return arguments.toList(); } - /** Generates 'this.name.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ - protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens) { - Name thisName = builderType.toName("this"); + /** Generates 'builderVariable.name.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ + protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens, String builderVariable) { + Name thisName = builderType.toName(builderVariable); JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size")); JCExpression sizeInvoke = maker.Apply(List.nil(), fn, List.nil()); if (nullGuard) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index fba4c80e..fb3d2c55 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -169,7 +169,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); List jceBlank = List.nil(); @@ -186,12 +186,12 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { JCExpression invokeBuild; { //this.pluralName.build(); - invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank); + invokeBuild = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName().toString(), "build"), jceBlank); } JCExpression isNull; { //this.pluralName == null - isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), maker.Literal(CTC_BOT, null)); + isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName(builderVariable)), data.getPluralName()), maker.Literal(CTC_BOT, null)); } JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build() diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java index 3002a98f..b1375151 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java @@ -46,9 +46,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable"); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } @@ -71,7 +71,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari JCStatement assignStat; { // pluralName = java.util.Collections.singletonList(this.pluralName.get(0)); JCExpression zeroLiteral = maker.Literal(CTC_INT, 0); - JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "get"), List.of(zeroLiteral)); + JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName().toString(), "get"), List.of(zeroLiteral)); List args = List.of(arg); JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "singletonList"), args); assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke)); @@ -82,12 +82,12 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari } /* default: Create with right size, then addAll */ { - List defStats = createListCopy(maker, data, builderType, source); + List defStats = createListCopy(maker, data, builderType, source, builderVariable); JCCase defaultCase = maker.Case(null, defStats); cases.append(defaultCase); } - JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList()); + JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source); JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); @@ -95,9 +95,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari statements.append(switchStat); } - private List createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + private List createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, String builderVariable) { List jceBlank = List.nil(); - Name thisName = builderType.toName("this"); + Name thisName = builderType.toName(builderVariable); JCExpression argToUnmodifiable; { // new java.util.ArrayList(this.pluralName); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 93b120e1..774b1200 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -210,18 +210,18 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Map")) { - statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source)); + statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source, builderVariable)); } else { - statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source)); + statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source, builderVariable)); } } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java index 317233cb..71a36c0e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java @@ -40,18 +40,18 @@ public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingulariz return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet"); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Set")) { - statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source)); + statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source, builderVariable)); } else { - statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source)); + statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source, builderVariable)); } } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 25669721..12922d3d 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -46,7 +46,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } - protected List createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) { + protected List createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source, String builderVariable) { List jceBlank = List.nil(); ListBuffer cases = new ListBuffer(); @@ -66,11 +66,11 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { // !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0)); // mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); JCExpression zeroLiteral = maker.Literal(CTC_INT, 0); - JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral)); + JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral)); List args; if (mapMode) { JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0); - JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone)); + JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone)); args = List.of(arg, arg2); } else { args = List.of(arg); @@ -84,12 +84,12 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { } { // default: - List statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source); + List statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source, builderVariable); JCCase defaultCase = maker.Case(null, statements); cases.append(defaultCase); } - JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false), cases.toList()); + JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source); JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); @@ -125,9 +125,9 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { return maker.If(cond, thenPart, null); } - protected List createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source) { + protected List createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source, String builderVariable) { List jceBlank = List.nil(); - Name thisName = builderType.toName("this"); + Name thisName = builderType.toName(builderVariable); JCStatement createStat; { // pluralName = new java.util.TargetType(initialCap); @@ -136,10 +136,10 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(); // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE; // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2 - JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 0x40000000)); + JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true, builderVariable), maker.Literal(CTC_INT, 0x40000000)); JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE"); - JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true)); - JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 3))); + JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true, builderVariable)); + JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true, builderVariable), maker.Literal(CTC_INT, 3))); JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3)); JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight); constructorArgs = List.of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue)); @@ -163,11 +163,11 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { Name ivar = builderType.toName("$i"); Name keyVarName = builderType.toName(data.getPluralName() + "$key"); JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put")); - JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$key", "get"), List.of(maker.Ident(ivar))); - JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.of(maker.Ident(ivar))); + JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$key", "get"), List.of(maker.Ident(ivar))); + JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$value", "get"), List.of(maker.Ident(ivar))); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); - JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true)); + JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable)); JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar)); fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement); } else { -- cgit From e61561b5a587f9cc770902c3ad9532f0daa5b85f Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 14 Mar 2017 18:47:29 +0100 Subject: singularizer's build code for any variable name (not only "this") --- .../eclipse/handlers/EclipseSingularsRecipes.java | 24 +++++++-- .../lombok/eclipse/handlers/HandleBuilder.java | 58 ++++++++++++++-------- .../lombok/eclipse/handlers/HandleConstructor.java | 2 +- .../singulars/EclipseGuavaSingularizer.java | 6 +-- .../singulars/EclipseJavaUtilListSingularizer.java | 11 ++-- .../singulars/EclipseJavaUtilMapSingularizer.java | 8 +-- .../singulars/EclipseJavaUtilSetSingularizer.java | 8 +-- .../singulars/EclipseJavaUtilSingularizer.java | 28 +++++------ src/core/lombok/javac/handlers/HandleBuilder.java | 17 ++++--- 9 files changed, 98 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 4cb41d4f..10ed7fbb 100644 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -45,6 +45,8 @@ 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.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Reference; +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.ThisReference; @@ -212,7 +214,7 @@ public class EclipseSingularsRecipes { public abstract List generateFields(SingularData data, EclipseNode builderType); public abstract void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain); - public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName); + public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable); public boolean requiresCleaning() { try { @@ -302,17 +304,18 @@ public class EclipseSingularsRecipes { private static final char[] SIZE_TEXT = new char[] {'s', 'i', 'z', 'e'}; - /** Generates 'this.name.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ - protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard) { + /** Generates 'this.name.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). + * @param builderVariable */ + protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard, String builderVariable) { MessageSend invoke = new MessageSend(); - ThisReference thisRef = new ThisReference(0, 0); + Reference thisRef = getBuilderReference(builderVariable); FieldReference thisDotName = new FieldReference(name, 0L); thisDotName.receiver = thisRef; invoke.receiver = thisDotName; invoke.selector = SIZE_TEXT; if (!nullGuard) return invoke; - ThisReference cdnThisRef = new ThisReference(0, 0); + Reference cdnThisRef = getBuilderReference(builderVariable); FieldReference cdnThisDotName = new FieldReference(name, 0L); cdnThisDotName.receiver = cdnThisRef; NullLiteral nullLiteral = new NullLiteral(0, 0); @@ -341,5 +344,16 @@ public class EclipseSingularsRecipes { return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS); } + + /** + * @return a {@link SingleNameReference} to the builder in the variable builderVariable. If builderVariable == "this", a {@link ThisReference} is returned. + */ + protected static Reference getBuilderReference(String builderVariable) { + if ("this".equals(builderVariable)) { + return new ThisReference(0, 0); + } else { + return new SingleNameReference(builderVariable.toCharArray(), 0); + } + } } } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index ebeb82ea..3d4a7a66 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -37,6 +37,7 @@ import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; @@ -55,9 +56,11 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; 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.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -212,7 +215,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { boolean callBuilderBasedSuperConstructor = inherit && td.superclass != null; if (extendable) { - generateConstructor(tdParent, builderFields, annotationNode, + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, @@ -546,11 +549,12 @@ public class HandleBuilder extends EclipseAnnotationHandler { * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateConstructor(EclipseNode typeNode, List builderFields, EclipseNode sourceNode, + private void generateBuilderBasedConstructor(EclipseNode typeNode, List builderFields, EclipseNode sourceNode, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { typeNode.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); + return; } AccessLevel level = AccessLevel.PROTECTED; @@ -564,8 +568,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { level = AccessLevel.PRIVATE; } -// boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); - ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(level); @@ -585,7 +587,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; constructor.arguments = null; - List assigns = new ArrayList(); + List statements = new ArrayList(); List nullChecks = new ArrayList(); for (BuilderFieldData fieldNode : builderFields) { @@ -595,21 +597,18 @@ public class HandleBuilder extends EclipseAnnotationHandler { int e = (int) p; thisX.receiver = new ThisReference(s, e); - if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { - fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, assigns, fieldNode.name); - } - Expression assignmentExpr; - if (builderClassnameAsParameter != null) { + if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { + fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b"); + assignmentExpr = new SingleNameReference(fieldNode.name, p); + } else { char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; long[] positions = new long[] {p, p}; assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); - } else { - assignmentExpr = new SingleNameReference(fieldName, p); } Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); - assigns.add(assignment); + statements.add(assignment); Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.fieldNode.get(), NON_NULL_PATTERN); if (nonNulls.length != 0) { Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.fieldNode.get(), sourceNode); @@ -619,15 +618,34 @@ public class HandleBuilder extends EclipseAnnotationHandler { } } - nullChecks.addAll(assigns); + nullChecks.addAll(statements); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; - // Generate annotations that must be put on the generated method, and attach them. -// Annotation[] constructorProperties = null; -// if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !HandleConstructor.isLocalType(typeNode)) { -// constructorProperties = HandleConstructor.createConstructorProperties(source, builderFields); -// } + boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + if (!suppressConstructorProperties) { + // Add ConstructorProperties + long[] poss = new long[3]; + Arrays.fill(poss, p); + QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(HandleConstructor.JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); + setGeneratedBy(constructorPropertiesType, source); + SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, source.sourceStart); + ann.declarationSourceEnd = source.sourceEnd; + + ArrayInitializer fieldNames = new ArrayInitializer(); + fieldNames.sourceStart = source.sourceStart; + fieldNames.sourceEnd = source.sourceEnd; + fieldNames.expressions = new Expression[1]; + + fieldNames.expressions[0] = new StringLiteral("b".toCharArray(), source.sourceStart, source.sourceEnd, 0); + setGeneratedBy(fieldNames.expressions[0], source); + + ann.memberValue = fieldNames; + setGeneratedBy(ann, source); + setGeneratedBy(ann.memberValue, source); + Annotation[] constructorProperties = new Annotation[] { ann }; + constructor.annotations = copyAnnotations(source, constructorProperties); + } constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); @@ -681,7 +699,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this"); } } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index 49b09231..b504495d 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -258,7 +258,7 @@ public class HandleConstructor { } } - private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; + public static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; public static Annotation[] createConstructorProperties(ASTNode source, Collection fields) { if (fields.isEmpty()) return null; diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 242bde1f..d9f4cb73 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -195,7 +195,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { injectMethod(builderType, md); } - @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName) { + @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable) { TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS); String simpleTypeName = getSimpleTargetTypeName(data); int agrumentsCount = getTypeArgumentsCount(); @@ -214,14 +214,14 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { invokeBuild = new MessageSend(); invokeBuild.selector = new char[] {'b', 'u', 'i', 'l', 'd'}; FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); - thisDotField.receiver = new ThisReference(0, 0); + thisDotField.receiver = getBuilderReference(builderVariable); invokeBuild.receiver = thisDotField; } Expression isNull; { //this.pluralName == null FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); - thisDotField.receiver = new ThisReference(0, 0); + thisDotField.receiver = getBuilderReference(builderVariable); isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java index 576115b0..f512bacf 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java @@ -45,7 +45,6 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; -import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; @@ -56,9 +55,9 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable"); } - @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName) { + @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable); return; } @@ -76,7 +75,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu /* case 1: (singleton) break; */ { switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0)); FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); - thisDotField.receiver = new ThisReference(0, 0); + thisDotField.receiver = getBuilderReference(builderVariable); MessageSend thisDotFieldGet0 = new MessageSend(); thisDotFieldGet0.receiver = thisDotField; thisDotFieldGet0.selector = new char[] {'g', 'e', 't'}; @@ -97,7 +96,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu Expression argToUnmodifiable; /* new j.u.ArrayList(this.pluralName); */ { FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L); - thisDotPluralName.receiver = new ThisReference(0, 0); + thisDotPluralName.receiver = getBuilderReference(builderVariable); TypeReference targetTypeExpr = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS); targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs()); AllocationExpression constructorCall = new AllocationExpression(); @@ -117,7 +116,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu SwitchStatement switchStat = new SwitchStatement(); switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]); - switchStat.expression = getSize(builderType, data.getPluralName(), true); + switchStat.expression = getSize(builderType, data.getPluralName(), true, builderVariable); TypeReference localShadowerType = new QualifiedTypeReference(Eclipse.fromQualifiedName(data.getTargetFqn()), NULL_POSS); localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs()); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index ef9e2a76..3d7b05ed 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -293,16 +293,16 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer injectMethod(builderType, md); } - @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName) { + @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName); + guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable); return; } if (data.getTargetFqn().equals("java.util.Map")) { - statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap")); + statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", builderVariable)); } else { - statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap")); + statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap", builderVariable)); } } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java index 2d16eae0..200e615e 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java @@ -37,16 +37,16 @@ public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingul return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet"); } - @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName) { + @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable); return; } if (data.getTargetFqn().equals("java.util.Set")) { - statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet")); + statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet", builderVariable)); } else { - statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet")); + statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet", builderVariable)); } } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java index 6661f4af..8bcfa65d 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java @@ -90,7 +90,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } - protected List createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) { + protected List createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, String builderVariable) { List switchContents = new ArrayList(); char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName(); @@ -112,7 +112,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { /* !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0)); mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); */ { FieldReference thisDotKey = new FieldReference(keyName, 0L); - thisDotKey.receiver = new ThisReference(0, 0); + thisDotKey.receiver = getBuilderReference(builderVariable); MessageSend thisDotKeyGet0 = new MessageSend(); thisDotKeyGet0.receiver = thisDotKey; thisDotKeyGet0.selector = new char[] {'g', 'e', 't'}; @@ -122,7 +122,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { if (mapMode) { char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray(); FieldReference thisDotValue = new FieldReference(valueName, 0L); - thisDotValue.receiver = new ThisReference(0, 0); + thisDotValue.receiver = getBuilderReference(builderVariable); MessageSend thisDotValueGet0 = new MessageSend(); thisDotValueGet0.receiver = thisDotValue; thisDotValueGet0.selector = new char[] {'g', 'e', 't'}; @@ -143,12 +143,12 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { { // default: switchContents.add(new CaseStatement(null, 0, 0)); - switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType)); + switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, builderVariable)); } SwitchStatement switchStat = new SwitchStatement(); switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]); - switchStat.expression = getSize(builderType, keyName, true); + switchStat.expression = getSize(builderType, keyName, true, builderVariable); TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs()); @@ -157,7 +157,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { return Arrays.asList(varDefStat, switchStat); } - protected List createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) { + protected List createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, String builderVariable) { char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName(); Statement createStat; { @@ -166,11 +166,11 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { if (addInitialCapacityArg) { // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE; // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2 - Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS); + Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS); FieldReference integerMaxValue = new FieldReference("MAX_VALUE".toCharArray(), 0L); integerMaxValue.receiver = new QualifiedNameReference(TypeConstants.JAVA_LANG_INTEGER, NULL_POSS, 0, 0); - Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard), OperatorIds.PLUS); - Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS); + Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard, builderVariable), OperatorIds.PLUS); + Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS); Expression sizeFormulaRight = new BinaryExpression(sizeFormulaRightLeft, makeIntLiteral(new char[] {'3'}, null), OperatorIds.DIVIDE); Expression sizeFormula = new BinaryExpression(sizeFormulaLeft, sizeFormulaRight, OperatorIds.PLUS); Expression cond = new ConditionalExpression(lessThanCutoff, sizeFormula, integerMaxValue); @@ -203,9 +203,9 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { pluralnameDotPut.selector = new char[] {'p', 'u', 't'}; pluralnameDotPut.receiver = new SingleNameReference(data.getPluralName(), 0L); FieldReference thisDotKey = new FieldReference(varName, 0L); - thisDotKey.receiver = new ThisReference(0, 0); + thisDotKey.receiver = getBuilderReference(builderVariable); FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L); - thisDotValue.receiver = new ThisReference(0, 0); + thisDotValue.receiver = getBuilderReference(builderVariable); MessageSend keyArg = new MessageSend(); keyArg.receiver = thisDotKey; keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)}; @@ -219,7 +219,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { LocalDeclaration forInit = new LocalDeclaration(iVar, 0, 0); forInit.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); forInit.initialization = makeIntLiteral(new char[] {'0'}, null); - Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard), OperatorIds.LESS); + Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard, builderVariable), OperatorIds.LESS); Expression incrementExpr = new PostfixExpression(new SingleNameReference(iVar, 0L), IntLiteral.One, OperatorIds.PLUS, 0); fillStat = new ForStatement(new Statement[] {forInit}, checkExpr, new Statement[] {incrementExpr}, pluralnameDotPut, true, 0, 0); } else { @@ -228,14 +228,14 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { pluralnameDotAddAll.selector = new char[] {'a', 'd', 'd', 'A', 'l', 'l'}; pluralnameDotAddAll.receiver = new SingleNameReference(data.getPluralName(), 0L); FieldReference thisDotPluralname = new FieldReference(varName, 0L); - thisDotPluralname.receiver = new ThisReference(0, 0); + thisDotPluralname.receiver = getBuilderReference(builderVariable); pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname}; fillStat = pluralnameDotAddAll; } if (nullGuard) { FieldReference thisDotField = new FieldReference(varName, 0L); - thisDotField.receiver = new ThisReference(0, 0); + thisDotField.receiver = getBuilderReference(builderVariable); Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL); fillStat = new IfStatement(cond, fillStat, 0, 0); } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index f46a95fd..42e76cf1 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -562,13 +562,16 @@ public class HandleBuilder extends JavacAnnotationHandler { JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); - // Add ConstructorProperties - JCExpression constructorPropertiesType = chainDots(typeNode, "java", "beans", "ConstructorProperties"); - ListBuffer fieldNames = new ListBuffer(); - fieldNames.append(maker.Literal(builderVariableName.toString())); - JCExpression fieldNamesArray = maker.NewArray(null, List.nil(), fieldNames.toList()); - JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); - mods.annotations = mods.annotations.append(annotation); + boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + if (!suppressConstructorProperties) { + // Add ConstructorProperties + JCExpression constructorPropertiesType = chainDots(typeNode, "java", "beans", "ConstructorProperties"); + ListBuffer fieldNames = new ListBuffer(); + fieldNames.append(maker.Literal(builderVariableName.toString())); + JCExpression fieldNamesArray = maker.NewArray(null, List.nil(), fieldNames.toList()); + JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); + mods.annotations = mods.annotations.append(annotation); + } // Create a constructor that has just the builder as parameter. ListBuffer params = new ListBuffer(); -- cgit From 14550d408415b9f7f7090db9770ef0b58ccd4212 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 15 Mar 2017 10:32:06 +0100 Subject: ConstructorProperties not necessary; javadoc --- .../lombok/eclipse/handlers/HandleBuilder.java | 42 ++++++---------------- src/core/lombok/javac/handlers/HandleBuilder.java | 30 +++++++--------- 2 files changed, 24 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 3d4a7a66..3795c3de 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -37,7 +37,6 @@ import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; -import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; @@ -56,11 +55,9 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; -import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; 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.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -213,8 +210,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; } - boolean callBuilderBasedSuperConstructor = inherit && td.superclass != null; if (extendable) { + boolean callBuilderBasedSuperConstructor = td.superclass != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { @@ -539,13 +536,21 @@ public class HandleBuilder extends EclipseAnnotationHandler { } /** + * 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. + * + * @param typeNode + * the type (with the {@code @Builder} annotation) for which a + * constructor should be generated. + * @param builderFields a list of fields in the builder which should be assigned to new instances. + * @param sourceNode the annotation (used for setting source code locations for the generated code). * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will + * If {@code != null}, the only parameter of the constructor will * be a builder with this classname; the constructor will then * use the values within this builder to assign the fields of new * instances. * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super + * If {@code true}, the constructor will explicitly call a super * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ @@ -622,31 +627,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; - boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); - if (!suppressConstructorProperties) { - // Add ConstructorProperties - long[] poss = new long[3]; - Arrays.fill(poss, p); - QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(HandleConstructor.JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); - setGeneratedBy(constructorPropertiesType, source); - SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, source.sourceStart); - ann.declarationSourceEnd = source.sourceEnd; - - ArrayInitializer fieldNames = new ArrayInitializer(); - fieldNames.sourceStart = source.sourceStart; - fieldNames.sourceEnd = source.sourceEnd; - fieldNames.expressions = new Expression[1]; - - fieldNames.expressions[0] = new StringLiteral("b".toCharArray(), source.sourceStart, source.sourceEnd, 0); - setGeneratedBy(fieldNames.expressions[0], source); - - ann.memberValue = fieldNames; - setGeneratedBy(ann, source); - setGeneratedBy(ann.memberValue, source); - Annotation[] constructorProperties = new Annotation[] { ann }; - constructor.annotations = copyAnnotations(source, constructorProperties); - } - constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); injectMethod(typeNode, constructor); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 42e76cf1..9860fb07 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -175,8 +175,8 @@ public class HandleBuilder extends JavacAnnotationHandler { superclassBuilderClassName = extendsClause + "Builder"; } - boolean callBuilderBasedSuperConstructor = inherit && extendsClause != null; if (extendable) { + boolean callBuilderBasedSuperConstructor = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); @@ -510,13 +510,20 @@ public class HandleBuilder extends JavacAnnotationHandler { /** * 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. + * + * @param typeNode + * the type (with the {@code @Builder} annotation) for which a + * constructor should be generated. + * @param builderFields a list of fields in the builder which should be assigned to new instances. + * @param source the annotation (used for setting source code locations for the generated code). * @param builderClassnameAsParameter - * the only parameter of the constructor will be a builder with - * this classname; the constructor will use the values within - * this builder to assign the fields of new instances. + * If {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super + * If {@code true}, the constructor will explicitly call a super * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ @@ -562,17 +569,6 @@ public class HandleBuilder extends JavacAnnotationHandler { JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); - boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); - if (!suppressConstructorProperties) { - // Add ConstructorProperties - JCExpression constructorPropertiesType = chainDots(typeNode, "java", "beans", "ConstructorProperties"); - ListBuffer fieldNames = new ListBuffer(); - fieldNames.append(maker.Literal(builderVariableName.toString())); - JCExpression fieldNamesArray = maker.NewArray(null, List.nil(), fieldNames.toList()); - JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); - mods.annotations = mods.annotations.append(annotation); - } - // Create a constructor that has just the builder as parameter. ListBuffer params = new ListBuffer(); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); -- cgit From 2a6c0c16b303b6af0b96e296858c2c54140f6dfe Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 28 Mar 2018 00:38:07 +0200 Subject: [builderParentClass] Code to handle builder on enums removed; that doesn’t make a heck of a lot of sense and enums aren’t extensible in the first place. Also fixed up a wrong access level; now all existing builder tests pass. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/lombok/eclipse/handlers/HandleBuilder.java | 5 +---- src/core/lombok/javac/handlers/HandleBuilder.java | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index b5c6e793..08ef91b5 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -604,12 +604,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); long p = (long) source.sourceStart << 32 | source.sourceEnd; - boolean isEnum = (((TypeDeclaration) typeNode.get()).modifiers & ClassFileConstants.AccEnum) != 0; - AccessLevel level = isEnum ? AccessLevel.PRIVATE : AccessLevel.PROTECTED; - ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); - constructor.modifiers = toEclipseModifier(level); + constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED); constructor.selector = typeDeclaration.name; if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 4c575d91..a74ebfda 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -195,7 +195,7 @@ public class HandleBuilder extends JavacAnnotationHandler { boolean callBuilderBasedSuperConstructor = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { - handleConstructor.generateConstructor(tdParent, AccessLevel.PROTECTED, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); + handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); } returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); @@ -569,9 +569,6 @@ public class HandleBuilder extends JavacAnnotationHandler { private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List builderFields, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { JavacTreeMaker maker = typeNode.getTreeMaker(); - boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; - AccessLevel level = isEnum ? AccessLevel.PRIVATE : AccessLevel.PROTECTED; - ListBuffer nullChecks = new ListBuffer(); ListBuffer statements = new ListBuffer(); @@ -599,7 +596,7 @@ public class HandleBuilder extends JavacAnnotationHandler { statements.append(maker.Exec(assign)); } - JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); + JCModifiers mods = maker.Modifiers(toJavacModifier(AccessLevel.PROTECTED), List.nil()); // Create a constructor that has just the builder as parameter. ListBuffer params = new ListBuffer(); -- cgit From 3eea3b958946dd813197f00a1292b7a72380878a Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 4 Apr 2018 23:26:01 +0200 Subject: introduction of `@SuperBuilder` and a testcase. --- src/core/lombok/Builder.java | 50 +-------- src/core/lombok/experimental/SuperBuilder.java | 66 ++++++++++++ .../resource/after-delombok/SuperBuilderBasic.java | 112 +++++++++++++++++++++ .../resource/before/BuilderWithInherit.java | 20 ---- .../resource/before/SuperBuilderBasic.java | 20 ++++ 5 files changed, 203 insertions(+), 65 deletions(-) create mode 100644 src/core/lombok/experimental/SuperBuilder.java create mode 100644 test/transform/resource/after-delombok/SuperBuilderBasic.java delete mode 100644 test/transform/resource/before/BuilderWithInherit.java create mode 100644 test/transform/resource/before/SuperBuilderBasic.java (limited to 'src') diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index 7ae43bfa..d7a2a109 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 The Project Lombok Authors. + * Copyright (C) 2013-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -55,8 +55,8 @@ import java.lang.annotation.Target; * *
  * @Builder
- * class Example {
- * 	private int foo;
+ * class Example<T> {
+ * 	private T foo;
  * 	private final String bar;
  * }
  * 
@@ -103,6 +103,8 @@ import java.lang.annotation.Target; * } * } * + * + * @see Singular */ @Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(SOURCE) @@ -131,48 +133,6 @@ public @interface Builder { */ String builderClassName() default ""; - /** - * Should the generated builder extend the parent class's builder? - * - * If {@code true}, the generated builder class will extend the builder of the - * superclass. This means the builder also contains the methods for fields from - * the superclass. - *

- * Note that both this builder and the superclass' builder must be a builder - * for a type (so, not for a static method or a constructor). You must mark - * the parent extendable: {@code @Builder(extensible = true)}. - *

- * This builder will also be {@code extensible = true} if you set {@code inherit = true}. - * - * @see #extensible() - */ - boolean inherit() default false; - - /** - * Should the generated builder be extensible by subclasses? - * - * If {@code true} the generated builder class will be extensible by - * {@code @Builder} classes of this class's subclasses, by marking them - * with {@code @Builder(inherit = true)}. - *

- * The {@code @Builder} extensible system only works for {@code @Builder} - * annotations on types (so, not on a static method or a constructor). - * - * @see #inherit() - */ - boolean extensible() default false; - - /** - * Name of the builder class in the superclass. - * - * Only relevant if {@code inherit = true} - * - * Default: {@code (SuperclassTypeName)Builder}. - * - * @see #inherit() - */ - String superclassBuilderClassName() default ""; - /** * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. * Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java new file mode 100644 index 00000000..26127a5f --- /dev/null +++ b/src/core/lombok/experimental/SuperBuilder.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.experimental; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import lombok.Singular; + +/** + * The SuperBuilder annotation creates a so-called 'builder' aspect to the class that is annotated with {@code @SuperBuilder}, but which works well when extending. + * It is similar to {@code @Builder}, except it is only legal on types, is less configurable, but allows you to {@code extends} other builder-able classes. + *

+ * All classes in the hierarchy must be annotated with {@code @SuperBuilder}. + *

+ * Lombok generates 2 inner 'builder' classes, which extend the parent class' builder class (unless your class doesn't have an extends clause). + * Lombok also generates a static method named {@code builder()}, and a protected constructor that takes 1 argument of the builderclass type. + *

+ * The TBuilder class contains 1 method for each parameter of the annotated + * constructor / method (each field, when annotating a class), which returns the builder itself. + * The builder also has a build() method which returns a completed instance of the original type. + *

+ * Complete documentation is found at the project lombok features page for @SuperBuilder. + * + * @see Singular + */ +@Target(TYPE) +@Retention(SOURCE) +public @interface SuperBuilder { + /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */ + String builderMethodName() default "builder"; + + /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ + String buildMethodName() default "build"; + + // toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method) + // to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2. + /* + * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. + * + * @return Whether to generate a {@code toBuilder()} method. + */ +// boolean toBuilder() default false; +} diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java new file mode 100644 index 00000000..ffb56e0e --- /dev/null +++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java @@ -0,0 +1,112 @@ +import java.util.List; + +public class SuperBuilderBasic { + public static class Parent { + int field1; + List items; + protected Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + private static class ParentBuilderImpl extends ParentBuilder { + @Override + public Parent build() { + return new Parent(this); + } + + @Override + public ParentBuilderImpl self() { + return this; + } + } + public abstract static class ParentBuilder> { + @java.lang.SuppressWarnings("all") + private int field1; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList items; + @java.lang.SuppressWarnings("all") + ParentBuilder() { + } + @java.lang.SuppressWarnings("all") + public B field1(final int field1) { + this.field1 = field1; + return self(); + } + @java.lang.SuppressWarnings("all") + public B item(final String item) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.add(item); + return self(); + } + @java.lang.SuppressWarnings("all") + public B items(final java.util.Collection items) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.addAll(items); + return self(); + } + @java.lang.SuppressWarnings("all") + public B clearItems() { + if (this.items != null) this.items.clear(); + return self(); + } + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @java.lang.SuppressWarnings("all") + public abstract C build(); + } + public static ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static class Child extends Parent { + double field3; + protected Child(ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + private static class ChildBuilderImpl extends ChildBuilder { + @Override + public Child build() { + return new Child(this); + } + + @Override + public ChildBuilderImpl self() { + return this; + } + } + public abstract static class ChildBuilder> extends Parent.ParentBuilder { + @java.lang.SuppressWarnings("all") + private double field3; + @java.lang.SuppressWarnings("all") + ChildBuilder() { + } + @java.lang.SuppressWarnings("all") + public B field3(final double field3) { + this.field3 = field3; + return self(); + } + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @java.lang.SuppressWarnings("all") + public abstract C build(); + } + public static ChildBuilder builder() { + return new ChildBuilderImpl(); + } + } + public static void test() { + Child x = Child.builder().field3(0.0).field1(5).item("").build(); + } +} diff --git a/test/transform/resource/before/BuilderWithInherit.java b/test/transform/resource/before/BuilderWithInherit.java deleted file mode 100644 index dcc7b5ad..00000000 --- a/test/transform/resource/before/BuilderWithInherit.java +++ /dev/null @@ -1,20 +0,0 @@ -import lombok.Builder; -import lombok.Singular; -import java.util.List; - -public class BuilderWithInherit { - @Builder(extensible = true) - public static class Parent { - int field1; - @Singular List items; - } - - @Builder(inherit = true) - public static class Child extends Parent { - double field3; - } - - public static void test() { - Child x = Child.builder().field3(0.0).field1(5).item("").build(); - } -} diff --git a/test/transform/resource/before/SuperBuilderBasic.java b/test/transform/resource/before/SuperBuilderBasic.java new file mode 100644 index 00000000..bf3eea64 --- /dev/null +++ b/test/transform/resource/before/SuperBuilderBasic.java @@ -0,0 +1,20 @@ +import lombok.experimental.SuperBuilder; +import lombok.Singular; +import java.util.List; + +public class SuperBuilderBasic { + @SuperBuilder + public static class Parent { + int field1; + @Singular List items; + } + + @SuperBuilder + public static class Child extends Parent { + double field3; + } + + public static void test() { + Child x = Child.builder().field3(0.0).field1(5).item("").build(); + } +} -- cgit From aef9cdc3924bc7999dc966a536ff2b9060a4dd14 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 2 Apr 2018 15:17:22 +0200 Subject: add javac handler Conflicts: src/core/lombok/experimental/SuperBuilder.java --- .../lombok/javac/handlers/HandleSuperBuilder.java | 649 +++++++++++++++++++++ 1 file changed, 649 insertions(+) create mode 100644 src/core/lombok/javac/handlers/HandleSuperBuilder.java (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java new file mode 100644 index 00000000..74b24bdb --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2013-2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.Javac.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import java.util.ArrayList; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +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.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Builder.ObtainVia; +import lombok.ConfigurationKeys; +import lombok.Singular; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.core.handlers.HandlerUtil; +import lombok.experimental.NonFinal; +import lombok.experimental.SuperBuilder; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +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.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 HandleConstructor handleConstructor = new HandleConstructor(); + +// private static final boolean toBoolean(Object expr, boolean defaultValue) { +// if (expr == null) return defaultValue; +// if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0; +// return ((Boolean) expr).booleanValue(); +// } + + private static class BuilderFieldData { + JCExpression type; + Name rawName; + Name name; + Name nameOfDefaultProvider; + Name nameOfSetFlag; + SingularData singularData; + ObtainVia obtainVia; + JavacNode obtainViaNode; + JavacNode originalFieldNode; + + java.util.List createdFields = new ArrayList(); + } + + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + SuperBuilder builderInstance = annotation.getInstance(); + + String builderMethodName = builderInstance.builderMethodName(); + String buildMethodName = builderInstance.buildMethodName(); + String builderClassName = builderInstance.builderClassName(); + String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + boolean inherit = builderInstance.inherit(); + String toBuilderMethodName = "toBuilder"; + boolean toBuilder = builderInstance.toBuilder(); + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + if (builderClassName == null) builderClassName = ""; + if (superclassBuilderClassName == null) superclassBuilderClassName = ""; + + if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; + if (!builderClassName.isEmpty()) { + if (!checkName("builderClassName", builderClassName, annotationNode)) return; + } + + JavacNode tdParent = annotationNode.up(); + + java.util.List builderFields = new ArrayList(); + JCExpression returnType; + List typeParams = List.nil(); + List thrownExceptions = 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(); + boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); + for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); + JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); + boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); + BuilderFieldData bfd = new BuilderFieldData(); + bfd.rawName = fd.name; + bfd.name = removePrefixFromField(fieldNode); + 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; + 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.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); + } + addObtainVia(bfd, fieldNode); + builderFields.add(bfd); + allFields.append(fieldNode); + } + + // Set the default names of the builder classes if not customized via annotation. + if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; + String builderImplClassName = builderClassName + "Impl"; + JCTree extendsClause = Javac.getExtendsClause(td); + if (superclassBuilderClassName.isEmpty() && extendsClause != null) { + superclassBuilderClassName = extendsClause + "Builder"; + } + + boolean useInheritanceOnBuilder = extendsClause != null && inherit; + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, useInheritanceOnBuilder); + + returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); + typeParams = td.typarams; + thrownExceptions = List.nil(); + + // Create the abstract builder class. + JavacNode builderType = findInnerClass(tdParent, builderClassName); + if (builderType == null) { + builderType = makeBuilderClass(true, true, annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); + } else { + annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + } + + // Create the builder implementation class. + JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); + if (builderImplType == null) { + builderImplType = makeBuilderClass(false, false, annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + } else { + annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + } + + // Check that all builder fields for @Singular and @ObtainVia. + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + if (bfd.singularData.getSingularizer().requiresCleaning()) { + addCleaning = true; + break; + } + } + if (bfd.obtainVia != null) { + if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) { + bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\")."); + return; + } + if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) { + bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set."); + return; + } + } + } + + // Generate the fields in the builder class that hold the values for the instance. + generateBuilderFields(builderType, builderFields, ast); + if (addCleaning) { + JavacTreeMaker maker = builderType.getTreeMaker(); + JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, uncleanField); + } + + // Create a simple constructor for the builder class. + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode); + if (cd != null) injectMethod(builderImplType, cd); + + // Create the setter methods in the abstract builder. + for (BuilderFieldData bfd : builderFields) { + makeSetterMethodsForBuilder(builderType, bfd, annotationNode); + } + + // Create the build() method in the BuilderImpl. + if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { + JCMethodDecl md = generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast); + if (md != null) injectMethod(builderImplType, md); + } + + // Create the toString() method for the abstract builder. + if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { + java.util.List fieldNodes = new ArrayList(); + for (BuilderFieldData bfd : builderFields) { + fieldNodes.addAll(bfd.createdFields); + } + JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); + if (md != null) injectMethod(builderType, md); + } + + if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + + // Add the builder() method to the annotated class. + if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + if (md != null) injectMethod(tdParent, md); + } + + if (toBuilder) { + switch (methodExists(toBuilderMethodName, tdParent, 0)) { + case EXISTS_BY_USER: + annotationNode.addWarning("Not generating toBuilder() as it already exists."); + return; + case NOT_EXISTS: + List tps = typeParams; + JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, ast); + if (md != null) injectMethod(tdParent, md); + } + } + + recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); + recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); + } + +// private static String unpack(JCExpression expr) { +// StringBuilder sb = new StringBuilder(); +// unpack(sb, expr); +// return sb.toString(); +// } + +// private static void unpack(StringBuilder sb, JCExpression expr) { +// if (expr instanceof JCIdent) { +// sb.append(((JCIdent) expr).name.toString()); +// return; +// } +// +// if (expr instanceof JCFieldAccess) { +// JCFieldAccess jcfa = (JCFieldAccess) expr; +// unpack(sb, jcfa.selected); +// sb.append(".").append(jcfa.name.toString()); +// return; +// } +// +// if (expr instanceof JCTypeApply) { +// sb.setLength(0); +// sb.append("ERR:"); +// sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate."); +// sb.append("__ERR__"); +// return; +// } +// +// sb.setLength(0); +// sb.append("ERR:"); +// sb.append("Expected a type of some sort, not a " + expr.getClass().getName()); +// sb.append("__ERR__"); +// } + + + /** + * 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. + * + * @param typeNode + * the type (with the {@code @Builder} annotation) for which a + * constructor should be generated. + * @param builderFields a list of fields in the builder which should be assigned to new instances. + * @param source the annotation (used for setting source code locations for the generated code). + * @param callBuilderBasedSuperConstructor + * If {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { + JavacTreeMaker maker = typeNode.getTreeMaker(); + + AccessLevel level = AccessLevel.PROTECTED; + boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; + if (isEnum) { + level = AccessLevel.PRIVATE; + } + + 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); + } + } + + JCExpression rhs; + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name); + rhs = maker.Ident(bfd.singularData.getPluralName()); + } else { + rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); + } + JCFieldAccess thisX = maker.Select(maker.Ident(bfd.originalFieldNode.toName("this")), bfd.rawName); + + JCExpression assign = maker.Assign(thisX, rhs); + + statements.append(maker.Exec(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()); + Name builderClassname = typeNode.toName(builderClassName); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); + params.append(param); + + if (callBuilderBasedSuperConstructor) { + // The first statement must be the call to the super constructor. + JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), + maker.Ident(typeNode.toName("super")), + 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 generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, JCAnnotation ast) { + // return new ThingieBuilder().setA(this.a).setB(this.b); + JavacTreeMaker maker = type.getTreeMaker(); + + ListBuffer typeArgs = new ListBuffer(); + for (JCTypeParameter typeParam : typeParams) { + typeArgs.append(maker.Ident(typeParam.name)); + } + + JCExpression call = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), null); + JCExpression invoke = call; + for (BuilderFieldData bfd : builderFields) { + Name setterName = bfd.name; + JCExpression arg; + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { + arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + } else { + if (bfd.obtainVia.isStatic()) { + JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); + arg = maker.Apply(List.nil(), c, List.of(maker.Ident(type.toName("this")))); + } else { + JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); + arg = maker.Apply(List.nil(), c, List.nil()); + } + } + invoke = maker.Apply(List.nil(), maker.Select(invoke, setterName), List.of(arg)); + } + JCStatement statement = maker.Return(invoke); + + JCBlock body = maker.Block(0, List.of(statement)); + return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); + } + + private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { + JavacTreeMaker maker = type.getTreeMaker(); + ListBuffer statements = new ListBuffer(); + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements); + } + } + + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); + JCBlock body = maker.Block(0, statements.toList()); + 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); + /* + * if (shouldReturnThis) { + methodType = cloneSelfType(field); + } + + if (methodType == null) { + //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6. + methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID)); + shouldReturnThis = false; + } + + */ + } + + private JCMethodDecl generateBuildMethod(JavacNode tdParent, String buildName, JCExpression returnType, JavacNode type, List thrownExceptions, JCTree source) { + 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()); + + return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); + } + + public 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); + } + + public 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)); + } + + 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; + return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); + } + + public void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { + int len = builderFields.size(); + java.util.List existing = new ArrayList(); + for (JavacNode child : builderType.down()) { + if (child.getKind() == Kind.FIELD) existing.add(child); + } + + for (int i = len - 1; i >= 0; i--) { + BuilderFieldData bfd = builderFields.get(i); + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); + } else { + JavacNode field = null, setFlag = null; + for (JavacNode exists : existing) { + Name n = ((JCVariableDecl) exists.get()).name; + if (n.equals(bfd.name)) field = exists; + if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; + } + JavacTreeMaker maker = builderType.getTreeMaker(); + if (field == null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); + field = injectFieldAndMarkGenerated(builderType, newField); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, newField); + } + bfd.createdFields.add(field); + } + } + } + + public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source) { + boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); + if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { + makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true); + } else { + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, true); + } + } + + private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) { + Name fieldName = ((JCVariableDecl) fieldNode.get()).name; + + for (JavacNode child : builderType.down()) { + if (child.getKind() != Kind.METHOD) continue; + JCMethodDecl methodDecl = (JCMethodDecl) child.get(); + Name existingName = methodDecl.name; + if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return; + } + + String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); + + JavacTreeMaker maker = fieldNode.getTreeMaker(); + + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.nil(), List.nil()); + + injectMethod(builderType, newMethod); + } + + public JavacNode findInnerClass(JavacNode parent, String name) { + for (JavacNode child : parent.down()) { + if (child.getKind() != Kind.TYPE) continue; + JCClassDecl td = (JCClassDecl) child.get(); + if (td.name.contentEquals(name)) return child; + } + return null; + } + + public JavacNode makeBuilderClass(boolean isAbstract, boolean isPublic, JavacNode source, JavacNode tdParent, String builderClassName, String parentBuilderClassName, List typeParams, JCAnnotation ast) { + JavacTreeMaker maker = tdParent.getTreeMaker(); + int modifiers = Flags.STATIC; + if (isAbstract) modifiers |= Flags.ABSTRACT; + if (isPublic) { + modifiers |= Flags.PUBLIC; + } else { + modifiers |= Flags.PRIVATE; + } + JCModifiers mods = maker.Modifiers(modifiers); + + JCExpression extending = null; + if (parentBuilderClassName != null) { + extending = maker.Ident(tdParent.toName(parentBuilderClassName)); + } + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); + return injectType(tdParent, builder); + } + + private void addObtainVia(BuilderFieldData bfd, JavacNode node) { + for (JavacNode child : node.down()) { + if (!annotationTypeMatches(ObtainVia.class, child)) continue; + AnnotationValues ann = createAnnotation(ObtainVia.class, child); + bfd.obtainVia = ann.getInstance(); + bfd.obtainViaNode = child; + deleteAnnotationIfNeccessary(child, ObtainVia.class); + return; + } + } + + /** + * Returns the explicitly requested singular annotation on this node (field + * or parameter), or null if there's no {@code @Singular} annotation on it. + * + * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. + */ + private SingularData getSingularData(JavacNode node) { + for (JavacNode child : node.down()) { + if (!annotationTypeMatches(Singular.class, child)) continue; + Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; + AnnotationValues ann = createAnnotation(Singular.class, child); + deleteAnnotationIfNeccessary(child, Singular.class); + String explicitSingular = ann.getInstance().value(); + if (explicitSingular.isEmpty()) { + if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { + node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); + explicitSingular = pluralName.toString(); + } else { + explicitSingular = autoSingularize(pluralName.toString()); + if (explicitSingular == null) { + node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))"); + explicitSingular = pluralName.toString(); + } + } + } + Name singularName = node.toName(explicitSingular); + + JCExpression type = null; + if (node.get() instanceof JCVariableDecl) { + type = ((JCVariableDecl) node.get()).vartype; + } + + String name = null; + List typeArgs = List.nil(); + if (type instanceof JCTypeApply) { + typeArgs = ((JCTypeApply) type).arguments; + type = ((JCTypeApply) type).clazz; + } + + name = type.toString(); + + String targetFqn = JavacSingularsRecipes.get().toQualified(name); + JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn); + if (singularizer == null) { + node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated."); + return null; + } + + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer); + } + + return null; + } +} -- cgit From 0ffbd3eec7079b598e3364b90dc949357ad09654 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 2 Apr 2018 19:39:45 +0200 Subject: generics for the Builder class; generate abstract methods --- src/core/lombok/javac/handlers/HandleSetter.java | 43 ++++++------ .../lombok/javac/handlers/HandleSuperBuilder.java | 79 +++++++++++++++++----- 2 files changed, 86 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 1453aa27..77919b8e 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -21,23 +21,14 @@ */ package lombok.javac.handlers; -import static lombok.javac.Javac.*; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; +import static lombok.javac.handlers.JavacHandlerUtil.toAllSetterNames; +import static lombok.javac.handlers.JavacHandlerUtil.toSetterName; import java.util.Collection; -import lombok.AccessLevel; -import lombok.ConfigurationKeys; -import lombok.Setter; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.javac.Javac; -import lombok.javac.JavacAnnotationHandler; -import lombok.javac.JavacNode; -import lombok.javac.JavacTreeMaker; -import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; - import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; @@ -57,6 +48,18 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.Setter; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; +import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; + /** * Handles the {@code lombok.Setter} annotation for javac. */ @@ -210,6 +213,12 @@ 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"))); + 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) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -240,19 +249,13 @@ public class HandleSetter extends JavacAnnotationHandler { statements.append(treeMaker.Exec(setBool)); } - JCExpression methodType = null; - if (shouldReturnThis) { - methodType = cloneSelfType(field); - } - if (methodType == null) { //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6. methodType = treeMaker.Type(Javac.createVoidType(field.getSymbolTable(), CTC_VOID)); - shouldReturnThis = false; + returnStatement = null; } - if (shouldReturnThis) { - JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + if (returnStatement != null) { statements.append(returnStatement); } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 74b24bdb..94926e80 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -36,9 +36,11 @@ import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; 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; @@ -188,15 +190,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(true, true, annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); + builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); } - + // Create the builder implementation class. JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { - builderImplType = makeBuilderClass(false, false, annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); } @@ -232,7 +234,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create a simple constructor for the builder class. JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode); if (cd != null) injectMethod(builderImplType, cd); - + + // Generate abstract getInstance() and build() methods. + injectMethod(builderType, generateAbstractGetInstanceMethod(tdParent)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, builderMethodName)); + // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { makeSetterMethodsForBuilder(builderType, bfd, annotationNode); @@ -420,6 +426,24 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); } + private JCMethodDecl generateAbstractGetInstanceMethod(JavacNode type) { + JavacTreeMaker maker = type.getTreeMaker(); + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + Name name = type.toName("getInstance"); + JCExpression returnType = maker.Ident(type.toName("B")); + + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + } + + private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName) { + JavacTreeMaker maker = type.getTreeMaker(); + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + Name name = type.toName(methodName); + JCExpression returnType = maker.Ident(type.toName("C")); + + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + } + private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer statements = new ListBuffer(); @@ -549,7 +573,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.nil(), List.nil()); + JCExpression returnType = maker.Ident(builderType.toName("B")); + JCReturn returnStatement = maker.Return(maker.Ident(builderType.toName("this"))); + + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); injectMethod(builderType, newMethod); } @@ -563,22 +590,42 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(boolean isAbstract, boolean isPublic, JavacNode source, JavacNode tdParent, String builderClassName, String parentBuilderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, String parentBuilderClass, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); - int modifiers = Flags.STATIC; - if (isAbstract) modifiers |= Flags.ABSTRACT; - if (isPublic) { - modifiers |= Flags.PUBLIC; - } else { - modifiers |= Flags.PRIVATE; + JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); + + JCExpression extending = null; + if (parentBuilderClass != null) { + extending = maker.Ident(tdParent.toName(parentBuilderClass)); } - JCModifiers mods = maker.Modifiers(modifiers); + + // Keep any type params of the annotated class. + // TODO: Prevent name clashes with type params from annotated class. + ListBuffer allTypeParams = new ListBuffer(); + allTypeParams.addAll(copyTypeParams(source, typeParams)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method, named "C", which extends the annotated class. + JCIdent annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + allTypeParams.add(maker.TypeParameter(tdParent.toName("C"), List.of(annotatedClass))); + // 2. The return type for all setter methods, named "B", which extends this builder class. + Name builderClassName = tdParent.toName(builderClass); + JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), + List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); + allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), List.of(typeApply))); + + JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.nil()); + return injectType(tdParent, builder); + } + + public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClassName, String builderClassName, List typeParams, JCAnnotation ast) { + JavacTreeMaker maker = tdParent.getTreeMaker(); + JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE); JCExpression extending = null; - if (parentBuilderClassName != null) { - extending = maker.Ident(tdParent.toName(parentBuilderClassName)); + if (builderClassName != null) { + extending = maker.Ident(tdParent.toName(builderClassName)); } - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); return injectType(tdParent, builder); } -- cgit From 1b38901064d7e53d4f4e7ab793031f56fe0a0000 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 2 Apr 2018 23:46:02 +0200 Subject: more generics; return getInstance() instead of this --- .../lombok/javac/handlers/HandleSuperBuilder.java | 46 ++++++++++++++++------ 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 94926e80..73271cba 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -193,6 +193,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + return; } // Create the builder implementation class. @@ -201,6 +202,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + return; } // Check that all builder fields for @Singular and @ObtainVia. @@ -237,18 +239,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Generate abstract getInstance() and build() methods. injectMethod(builderType, generateAbstractGetInstanceMethod(tdParent)); - injectMethod(builderType, generateAbstractBuildMethod(tdParent, builderMethodName)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName)); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { makeSetterMethodsForBuilder(builderType, bfd, annotationNode); } - // Create the build() method in the BuilderImpl. - if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast); - if (md != null) injectMethod(builderImplType, md); - } + // Create the getInstance() and build() methods in the BuilderImpl. + injectMethod(builderImplType, generateGetInstanceMethod(builderImplType)); + injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast)); // Create the toString() method for the abstract builder. if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { @@ -428,13 +428,25 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { private JCMethodDecl generateAbstractGetInstanceMethod(JavacNode type) { JavacTreeMaker maker = type.getTreeMaker(); - JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT); Name name = type.toName("getInstance"); JCExpression returnType = maker.Ident(type.toName("B")); return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } + private JCMethodDecl generateGetInstanceMethod(JavacNode builderImplType) { + JavacTreeMaker maker = builderImplType.getTreeMaker(); + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED); + Name name = builderImplType.toName("getInstance"); + 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) { JavacTreeMaker maker = type.getTreeMaker(); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); @@ -555,6 +567,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true); } else { + // TODO: Fix singular methods to return getInstance(). fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, true); } } @@ -574,7 +587,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacTreeMaker maker = fieldNode.getTreeMaker(); JCExpression returnType = maker.Ident(builderType.toName("B")); - JCReturn returnStatement = maker.Return(maker.Ident(builderType.toName("this"))); + JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName("getInstance")), List.nil())); JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); @@ -617,15 +630,26 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return injectType(tdParent, builder); } - public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClassName, String builderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderClassName, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); - JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE); + JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL); JCExpression extending = null; if (builderClassName != null) { extending = maker.Ident(tdParent.toName(builderClassName)); + + // Add any type params of the annotated class. + ListBuffer allTypeParams = new ListBuffer(); + allTypeParams.addAll(copyTypeParams(source, typeParams)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. + JCIdent annotatedClassIdent = maker.Ident(tdParent.toName(tdParent.getName())); + // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. + JCIdent builderImplClassIdent = maker.Ident(tdParent.toName(builderImplClass)); + extending = maker.TypeApply(extending, List.of(annotatedClassIdent, builderImplClassIdent)); } - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); + + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); return injectType(tdParent, builder); } -- cgit From d586cc6b4fc1afb0a9e59e73ac3db1f5c914e191 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 3 Apr 2018 11:58:44 +0200 Subject: wildcards for the builder() method --- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 73271cba..2d844432 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import org.mangosdk.spi.ProviderFor; +import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -45,6 +46,7 @@ import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -525,7 +527,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, List.of(statement)); int modifiers = Flags.PUBLIC; modifiers |= Flags.STATIC; - return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); + + // Add any type params of the annotated class to the return type. + ListBuffer typeParameterNames = new ListBuffer(); + typeParameterNames.addAll(typeParameterNames(maker, typeParams)); + // Now add the . + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + 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); } public void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { -- cgit From c37c33be46961fd6ebc8bec24cee24f62d066f03 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 3 Apr 2018 12:12:48 +0200 Subject: wildcards for the constructor parameter --- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 2d844432..2c292822 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -377,7 +377,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { ListBuffer params = new ListBuffer(); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); Name builderClassname = typeNode.toName(builderClassName); - JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); + // Now add the . + ListBuffer typeParams = new ListBuffer(); + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParams.add(wildcard); + typeParams.add(wildcard); + JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParams.toList()); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null); params.append(param); if (callBuilderBasedSuperConstructor) { -- cgit From 9534af2d86e9d821fd1550ba4ca1e9890f077ccb Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 5 Apr 2018 18:51:19 +0200 Subject: build class names not configurable any more; removed toBuilder --- .../lombok/javac/handlers/HandleSuperBuilder.java | 144 +++------------------ 1 file changed, 18 insertions(+), 126 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 2c292822..c271a262 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -74,14 +74,9 @@ 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 HandleConstructor handleConstructor = new HandleConstructor(); - -// private static final boolean toBoolean(Object expr, boolean defaultValue) { -// if (expr == null) return defaultValue; -// if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0; -// return ((Boolean) expr).booleanValue(); -// } - + + private static final String SELF_METHOD = "self"; + private static class BuilderFieldData { JCExpression type; Name rawName; @@ -101,22 +96,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); - String builderClassName = builderInstance.builderClassName(); - String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - boolean inherit = builderInstance.inherit(); - String toBuilderMethodName = "toBuilder"; - boolean toBuilder = builderInstance.toBuilder(); if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; - if (builderClassName == null) builderClassName = ""; - if (superclassBuilderClassName == null) superclassBuilderClassName = ""; if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; - if (!builderClassName.isEmpty()) { - if (!checkName("builderClassName", builderClassName, annotationNode)) return; - } JavacNode tdParent = annotationNode.up(); @@ -175,14 +160,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Set the default names of the builder classes if not customized via annotation. - if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; + String builderClassName = td.name.toString() + "Builder"; String builderImplClassName = builderClassName + "Impl"; JCTree extendsClause = Javac.getExtendsClause(td); - if (superclassBuilderClassName.isEmpty() && extendsClause != null) { - superclassBuilderClassName = extendsClause + "Builder"; - } - - boolean useInheritanceOnBuilder = extendsClause != null && inherit; + String superclassBuilderClassName= extendsClause + "Builder"; + + boolean useInheritanceOnBuilder = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, useInheritanceOnBuilder); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); @@ -239,8 +222,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode); if (cd != null) injectMethod(builderImplType, cd); - // Generate abstract getInstance() and build() methods. - injectMethod(builderType, generateAbstractGetInstanceMethod(tdParent)); + // Generate abstract self() and build() methods. + injectMethod(builderType, generateAbstractSelfMethod(tdParent)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName)); // Create the setter methods in the abstract builder. @@ -248,8 +231,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { makeSetterMethodsForBuilder(builderType, bfd, annotationNode); } - // Create the getInstance() and build() methods in the BuilderImpl. - injectMethod(builderImplType, generateGetInstanceMethod(builderImplType)); + // Create the self() and build() methods in the BuilderImpl. + injectMethod(builderImplType, generateSelfMethod(builderImplType)); injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast)); // Create the toString() method for the abstract builder. @@ -271,55 +254,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (md != null) injectMethod(tdParent, md); } - if (toBuilder) { - switch (methodExists(toBuilderMethodName, tdParent, 0)) { - case EXISTS_BY_USER: - annotationNode.addWarning("Not generating toBuilder() as it already exists."); - return; - case NOT_EXISTS: - List tps = typeParams; - JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, ast); - if (md != null) injectMethod(tdParent, md); - } - } - recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } - -// private static String unpack(JCExpression expr) { -// StringBuilder sb = new StringBuilder(); -// unpack(sb, expr); -// return sb.toString(); -// } - -// private static void unpack(StringBuilder sb, JCExpression expr) { -// if (expr instanceof JCIdent) { -// sb.append(((JCIdent) expr).name.toString()); -// return; -// } -// -// if (expr instanceof JCFieldAccess) { -// JCFieldAccess jcfa = (JCFieldAccess) expr; -// unpack(sb, jcfa.selected); -// sb.append(".").append(jcfa.name.toString()); -// return; -// } -// -// if (expr instanceof JCTypeApply) { -// sb.setLength(0); -// sb.append("ERR:"); -// sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate."); -// sb.append("__ERR__"); -// return; -// } -// -// sb.setLength(0); -// sb.append("ERR:"); -// sb.append("Expected a type of some sort, not a " + expr.getClass().getName()); -// sb.append("__ERR__"); -// } - /** * Generates a constructor that has a builder as the only parameter. @@ -359,7 +296,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCExpression rhs; if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name, "b"); rhs = maker.Ident(bfd.singularData.getPluralName()); } else { rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); @@ -401,52 +338,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } - private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, JCAnnotation ast) { - // return new ThingieBuilder().setA(this.a).setB(this.b); - JavacTreeMaker maker = type.getTreeMaker(); - - ListBuffer typeArgs = new ListBuffer(); - for (JCTypeParameter typeParam : typeParams) { - typeArgs.append(maker.Ident(typeParam.name)); - } - - JCExpression call = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), null); - JCExpression invoke = call; - for (BuilderFieldData bfd : builderFields) { - Name setterName = bfd.name; - JCExpression arg; - if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { - arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); - } else { - if (bfd.obtainVia.isStatic()) { - JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.nil(), c, List.of(maker.Ident(type.toName("this")))); - } else { - JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.nil(), c, List.nil()); - } - } - invoke = maker.Apply(List.nil(), maker.Select(invoke, setterName), List.of(arg)); - } - JCStatement statement = maker.Return(invoke); - - JCBlock body = maker.Block(0, List.of(statement)); - return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); - } - - private JCMethodDecl generateAbstractGetInstanceMethod(JavacNode type) { + private JCMethodDecl generateAbstractSelfMethod(JavacNode type) { JavacTreeMaker maker = type.getTreeMaker(); JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT); - Name name = type.toName("getInstance"); + Name name = type.toName(SELF_METHOD); JCExpression returnType = maker.Ident(type.toName("B")); return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } - private JCMethodDecl generateGetInstanceMethod(JavacNode builderImplType) { + private JCMethodDecl generateSelfMethod(JavacNode builderImplType) { JavacTreeMaker maker = builderImplType.getTreeMaker(); JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED); - Name name = builderImplType.toName("getInstance"); + Name name = builderImplType.toName(SELF_METHOD); JCExpression returnType = maker.Ident(builderImplType.toName(builderImplType.getName())); JCStatement statement = maker.Return(maker.Ident(builderImplType.toName("this"))); @@ -477,18 +381,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); JCBlock body = maker.Block(0, statements.toList()); 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); - /* - * if (shouldReturnThis) { - methodType = cloneSelfType(field); - } - - if (methodType == null) { - //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6. - methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID)); - shouldReturnThis = false; - } - - */ } private JCMethodDecl generateBuildMethod(JavacNode tdParent, String buildName, JCExpression returnType, JavacNode type, List thrownExceptions, JCTree source) { @@ -585,7 +477,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true); } else { - // TODO: Fix singular methods to return getInstance(). + // TODO: Fix singular methods to return self(). fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, true); } } @@ -605,7 +497,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacTreeMaker maker = fieldNode.getTreeMaker(); JCExpression returnType = maker.Ident(builderType.toName("B")); - JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName("getInstance")), List.nil())); + JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); -- cgit From 8acc37011f5c13d6547ca2e76fa15d89e1472dae Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 10 Apr 2018 20:25:19 +0200 Subject: apply generics on builder extends clause; fixed builder name generation --- .../lombok/javac/handlers/HandleSuperBuilder.java | 62 +++++++++++++--------- 1 file changed, 36 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index c271a262..8bf5b7a2 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -163,7 +163,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { String builderClassName = td.name.toString() + "Builder"; String builderImplClassName = builderClassName + "Impl"; JCTree extendsClause = Javac.getExtendsClause(td); - String superclassBuilderClassName= extendsClause + "Builder"; + String superclassBuilderClassName = null; + if (extendsClause instanceof JCFieldAccess) { + // The extends clause consists of a fully-qualified name. + superclassBuilderClassName = ((JCFieldAccess)extendsClause).getIdentifier() + "Builder"; + } else { + // A simple class name is used in the extends clause. + superclassBuilderClassName = extendsClause + "Builder"; + } boolean useInheritanceOnBuilder = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, useInheritanceOnBuilder); @@ -181,15 +188,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return; } - // Create the builder implementation class. - JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); - if (builderImplType == null) { - builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); - } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; - } - // Check that all builder fields for @Singular and @ObtainVia. for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { @@ -210,7 +208,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } - // Generate the fields in the builder class that hold the values for the instance. + // Generate the fields in the abstract builder class that hold the values for the instance. generateBuilderFields(builderType, builderFields, ast); if (addCleaning) { JavacTreeMaker maker = builderType.getTreeMaker(); @@ -218,11 +216,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectFieldAndMarkGenerated(builderType, uncleanField); } - // Create a simple constructor for the builder class. - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode); - if (cd != null) injectMethod(builderImplType, cd); - - // Generate abstract self() and build() methods. + // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName)); @@ -231,10 +225,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { makeSetterMethodsForBuilder(builderType, bfd, annotationNode); } - // Create the self() and build() methods in the BuilderImpl. - injectMethod(builderImplType, generateSelfMethod(builderImplType)); - injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast)); - // Create the toString() method for the abstract builder. if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { java.util.List fieldNodes = new ArrayList(); @@ -247,6 +237,23 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + // Create the builder implementation class. + JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); + if (builderImplType == null) { + builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + } else { + 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); + + // Create the self() and build() methods in the BuilderImpl. + injectMethod(builderImplType, generateSelfMethod(builderImplType)); + injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast)); + // Add the builder() method to the annotated class. if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); @@ -516,12 +523,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, String parentBuilderClass, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); - - JCExpression extending = null; - if (parentBuilderClass != null) { - extending = maker.Ident(tdParent.toName(parentBuilderClass)); - } - + // Keep any type params of the annotated class. // TODO: Prevent name clashes with type params from annotated class. ListBuffer allTypeParams = new ListBuffer(); @@ -535,6 +537,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), List.of(typeApply))); + + JCExpression extending = null; + if (parentBuilderClass != null) { + // If the annotated class extends another class, we want this builder to extend the builder of the superclass. + extending = maker.TypeApply(maker.Ident(tdParent.toName(parentBuilderClass)), + List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); + // TODO: type params from annotated class + } JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.nil()); return injectType(tdParent, builder); -- cgit From fdc23437e290b1b5d16d02d1d8927dc7dade3dcb Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 10 Apr 2018 21:28:45 +0200 Subject: @Override for overridden methods --- .../lombok/javac/handlers/HandleSuperBuilder.java | 44 +++++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 8bf5b7a2..0bd9ee2e 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -167,13 +167,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (extendsClause instanceof JCFieldAccess) { // The extends clause consists of a fully-qualified name. superclassBuilderClassName = ((JCFieldAccess)extendsClause).getIdentifier() + "Builder"; - } else { + } else if (extendsClause != null) { // A simple class name is used in the extends clause. superclassBuilderClassName = extendsClause + "Builder"; } + // If there is no superclass, superclassBuilderClassName is still == null at this point. + // You can use it to check whether to inherit or not. - boolean useInheritanceOnBuilder = extendsClause != null; - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, useInheritanceOnBuilder); + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, superclassBuilderClassName != null); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; @@ -182,7 +183,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); + builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, superclassBuilderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -217,8 +218,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Generate abstract self() and build() methods in the abstract builder. - injectMethod(builderType, generateAbstractSelfMethod(tdParent)); - injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName)); + injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassName != null)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassName != null)); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { @@ -252,7 +253,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType)); - injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, builderImplType, thrownExceptions, ast)); + injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions)); // Add the builder() method to the annotated class. if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { @@ -345,9 +346,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } - private JCMethodDecl generateAbstractSelfMethod(JavacNode type) { + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override) { JavacTreeMaker maker = type.getTreeMaker(); - JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT); + List annotations = List.nil(); + if (override) { + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); + annotations = List.of(overrideAnnotation); + } + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); Name name = type.toName(SELF_METHOD); JCExpression returnType = maker.Ident(type.toName("B")); @@ -356,7 +362,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { private JCMethodDecl generateSelfMethod(JavacNode builderImplType) { JavacTreeMaker maker = builderImplType.getTreeMaker(); - JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED); + + 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())); @@ -366,9 +374,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), body, null); } - private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName) { + private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override) { JavacTreeMaker maker = type.getTreeMaker(); - JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + List annotations = List.nil(); + if (override) { + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); + annotations = List.of(overrideAnnotation); + } + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations); Name name = type.toName(methodName); JCExpression returnType = maker.Ident(type.toName("C")); @@ -390,7 +403,7 @@ 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 generateBuildMethod(JavacNode tdParent, String buildName, JCExpression returnType, JavacNode type, List thrownExceptions, JCTree source) { + private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List thrownExceptions) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -402,8 +415,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); + return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); } public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List params) { -- cgit From 9a3866ef52186bde47a698230ba9498df378768d Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 10 Apr 2018 22:28:08 +0200 Subject: correct return type and statement for @Singular --- .../lombok/javac/handlers/HandleSuperBuilder.java | 14 ++++++------ .../javac/handlers/JavacSingularsRecipes.java | 25 +++++++++++++++++++++- .../handlers/singulars/JavacGuavaSingularizer.java | 12 +---------- .../JavacJavaUtilListSetSingularizer.java | 16 ++------------ .../singulars/JavacJavaUtilMapSingularizer.java | 15 ++----------- 5 files changed, 36 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 0bd9ee2e..1228dfd3 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -497,15 +497,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); + JavacTreeMaker maker = builderType.getTreeMaker(); + JCExpression returnType = maker.Ident(builderType.toName("B")); + JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true); + makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType, returnStatement); } else { - // TODO: Fix singular methods to return self(). - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, true); + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); } } - private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) { + private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -519,9 +521,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCExpression returnType = maker.Ident(builderType.toName("B")); - JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); injectMethod(builderType, newMethod); @@ -557,6 +556,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCExpression extending = null; if (parentBuilderClass != null) { // If the annotated class extends another class, we want this builder to extend the builder of the superclass. + // FIXME: The extends clause should look like "Parent.ParentBuilder", not just "ParentBuilder" (risk of name clashes). extending = maker.TypeApply(maker.Ident(tdParent.toName(parentBuilderClass)), List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); // TODO: type params from annotated class diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 4279ec63..ea5d1a54 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -194,7 +194,30 @@ public class JavacSingularsRecipes { } public abstract java.util.List generateFields(SingularData data, JavacNode builderType, JCTree source); - public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain); + + /** + * 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, JavacNode, JCTree, boolean, JCExpression, JCStatement)}. + */ + public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + JavacTreeMaker maker = builderType.getTreeMaker(); + JCExpression returnType = chain ? + cloneSelfType(builderType) : + maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); + JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); + } + /** + * Generates the singular, plural, and clear methods for the given + * {@link SingularData}.
+ * Uses the given returnType and returnStatement for the generated methods. + */ + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement); + public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable); public boolean requiresCleaning() { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 1798d52c..9a6a46f2 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -36,7 +36,6 @@ import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -70,19 +69,10 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 196ce45d..dfffb06b 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -33,7 +33,6 @@ import lombok.javac.handlers.JavacHandlerUtil; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -76,26 +75,15 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - Name thisName = builderType.toName("this"); - - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index fbcf776e..930ec3a4 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -37,7 +37,6 @@ import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -101,25 +100,15 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } -- cgit From 9697e6cc36cec5f304a43933d3fa5a8cf6eb58aa Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sat, 14 Apr 2018 20:01:01 +0200 Subject: include superclass when referring to parent builder example: "ChildBuilder extends Parent.ParentBuilder" --- .../lombok/javac/handlers/HandleSuperBuilder.java | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 1228dfd3..7f0858d2 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -163,18 +163,22 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { String builderClassName = td.name.toString() + "Builder"; String builderImplClassName = builderClassName + "Impl"; JCTree extendsClause = Javac.getExtendsClause(td); - String superclassBuilderClassName = null; + JCExpression superclassBuilderClassExpression = null; if (extendsClause instanceof JCFieldAccess) { // The extends clause consists of a fully-qualified name. - superclassBuilderClassName = ((JCFieldAccess)extendsClause).getIdentifier() + "Builder"; + Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier(); + String superclassBuilderClassName = superclassClassName + "Builder"; + superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess)extendsClause, + tdParent.toName(superclassBuilderClassName)); } else if (extendsClause != null) { // A simple class name is used in the extends clause. - superclassBuilderClassName = extendsClause + "Builder"; + String superclassBuilderClassName = extendsClause + "Builder"; + superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName); } // If there is no superclass, superclassBuilderClassName is still == null at this point. // You can use it to check whether to inherit or not. - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, superclassBuilderClassName != null); + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; @@ -183,7 +187,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, superclassBuilderClassName, typeParams, ast); + builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -218,8 +222,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Generate abstract self() and build() methods in the abstract builder. - injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassName != null)); - injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassName != null)); + injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null)); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { @@ -535,7 +539,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, String parentBuilderClass, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, JCExpression superclassBuilderClassExpression, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); @@ -554,10 +558,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), List.of(typeApply))); JCExpression extending = null; - if (parentBuilderClass != null) { + if (superclassBuilderClassExpression != null) { // If the annotated class extends another class, we want this builder to extend the builder of the superclass. - // FIXME: The extends clause should look like "Parent.ParentBuilder", not just "ParentBuilder" (risk of name clashes). - extending = maker.TypeApply(maker.Ident(tdParent.toName(parentBuilderClass)), + extending = maker.TypeApply(superclassBuilderClassExpression, List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); // TODO: type params from annotated class } -- cgit From 6dd21ad7e04cec9405fd798e236808ed4acac954 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sat, 14 Apr 2018 20:13:59 +0200 Subject: adjusted expected result of test case --- .../lombok/javac/handlers/HandleSuperBuilder.java | 1 + .../resource/after-delombok/SuperBuilderBasic.java | 278 ++++++++++++--------- 2 files changed, 167 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 7f0858d2..d30740c0 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -236,6 +236,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { for (BuilderFieldData bfd : builderFields) { fieldNodes.addAll(bfd.createdFields); } + // TODO: toString() should also call super.toString(), so that it also shows fields from the superclass. JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); if (md != null) injectMethod(builderType, md); } diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java index ffb56e0e..ed1f9bdc 100644 --- a/test/transform/resource/after-delombok/SuperBuilderBasic.java +++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java @@ -1,112 +1,166 @@ -import java.util.List; - -public class SuperBuilderBasic { - public static class Parent { - int field1; - List items; - protected Parent(final ParentBuilder b) { - this.field1 = b.field1; - java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); - } - this.items = items; - } - private static class ParentBuilderImpl extends ParentBuilder { - @Override - public Parent build() { - return new Parent(this); - } - - @Override - public ParentBuilderImpl self() { - return this; - } - } - public abstract static class ParentBuilder> { - @java.lang.SuppressWarnings("all") - private int field1; - @java.lang.SuppressWarnings("all") - private java.util.ArrayList items; - @java.lang.SuppressWarnings("all") - ParentBuilder() { - } - @java.lang.SuppressWarnings("all") - public B field1(final int field1) { - this.field1 = field1; - return self(); - } - @java.lang.SuppressWarnings("all") - public B item(final String item) { - if (this.items == null) this.items = new java.util.ArrayList(); - this.items.add(item); - return self(); - } - @java.lang.SuppressWarnings("all") - public B items(final java.util.Collection items) { - if (this.items == null) this.items = new java.util.ArrayList(); - this.items.addAll(items); - return self(); - } - @java.lang.SuppressWarnings("all") - public B clearItems() { - if (this.items != null) this.items.clear(); - return self(); - } - @java.lang.SuppressWarnings("all") - protected abstract B self(); - @java.lang.SuppressWarnings("all") - public abstract C build(); - } - public static ParentBuilder builder() { - return new ParentBuilderImpl(); - } - } - public static class Child extends Parent { - double field3; - protected Child(ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - private static class ChildBuilderImpl extends ChildBuilder { - @Override - public Child build() { - return new Child(this); - } - - @Override - public ChildBuilderImpl self() { - return this; - } - } - public abstract static class ChildBuilder> extends Parent.ParentBuilder { - @java.lang.SuppressWarnings("all") - private double field3; - @java.lang.SuppressWarnings("all") - ChildBuilder() { - } - @java.lang.SuppressWarnings("all") - public B field3(final double field3) { - this.field3 = field3; - return self(); - } - @java.lang.SuppressWarnings("all") - protected abstract B self(); - @java.lang.SuppressWarnings("all") - public abstract C build(); - } - public static ChildBuilder builder() { - return new ChildBuilderImpl(); - } - } - public static void test() { - Child x = Child.builder().field3(0.0).field1(5).item("").build(); - } -} +import java.util.List; + +public class SuperBuilderBasic { + public static class Parent { + int field1; + List items; + + @java.lang.SuppressWarnings("all") + protected Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + + + @java.lang.SuppressWarnings("all") + public static abstract class ParentBuilder> { + @java.lang.SuppressWarnings("all") + private int field1; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList items; + + @java.lang.SuppressWarnings("all") + protected abstract B self(); + + @java.lang.SuppressWarnings("all") + public abstract C build(); + + @java.lang.SuppressWarnings("all") + public B field1(final int field1) { + this.field1 = field1; + return self(); + } + + @java.lang.SuppressWarnings("all") + public B item(final String item) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.add(item); + return self(); + } + + @java.lang.SuppressWarnings("all") + public B items(final java.util.Collection items) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.addAll(items); + return self(); + } + + @java.lang.SuppressWarnings("all") + public B clearItems() { + if (this.items != null) this.items.clear(); + return self(); + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderBasic.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")"; + } + } + + + @java.lang.SuppressWarnings("all") + private static final class ParentBuilderImpl extends ParentBuilder { + @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") + public static ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + + + public static class Child extends Parent { + double field3; + + @java.lang.SuppressWarnings("all") + protected Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + + + @java.lang.SuppressWarnings("all") + public static abstract class ChildBuilder> extends Parent.ParentBuilder { + @java.lang.SuppressWarnings("all") + private double field3; + + @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 field3(final double field3) { + this.field3 = field3; + return self(); + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderBasic.Child.ChildBuilder(field3=" + this.field3 + ")"; + } + } + + + @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") + public static ChildBuilder builder() { + return new ChildBuilderImpl(); + } + } + + public static void test() { + Child x = Child.builder().field3(0.0).field1(5).item("").build(); + } +} -- cgit From cec9b39c693a0fa06ea1451d698bd4617b5b2457 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sat, 14 Apr 2018 20:17:46 +0200 Subject: revert changes to HandleBuilder (everything is in HandleSuperBuilder) --- src/core/lombok/javac/handlers/HandleBuilder.java | 200 ++++------------------ 1 file changed, 33 insertions(+), 167 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index a74ebfda..fec999ba 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -39,7 +39,6 @@ import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCLiteral; 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.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCStatement; @@ -84,7 +83,6 @@ public class HandleBuilder extends JavacAnnotationHandler { } private static class BuilderFieldData { - JavacNode fieldNode; JCExpression type; Name rawName; Name name; @@ -109,10 +107,6 @@ public class HandleBuilder extends JavacAnnotationHandler { String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); String toBuilderMethodName = "toBuilder"; - - boolean inherit = builderInstance.inherit(); - boolean extensible = inherit || builderInstance.extensible(); // inherit implies extendable - boolean toBuilder = builderInstance.toBuilder(); java.util.List typeArgsForToBuilder = null; @@ -141,11 +135,9 @@ public class HandleBuilder extends JavacAnnotationHandler { boolean addCleaning = false; boolean isStatic = true; - JCClassDecl td = null; - if (parent.get() instanceof JCClassDecl) { tdParent = parent; - td = (JCClassDecl) tdParent.get(); + JCClassDecl td = (JCClassDecl) tdParent.get(); ListBuffer allFields = new ListBuffer(); boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { @@ -153,7 +145,6 @@ public class HandleBuilder extends JavacAnnotationHandler { JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); BuilderFieldData bfd = new BuilderFieldData(); - bfd.fieldNode = fieldNode; bfd.rawName = fd.name; bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.vartype; @@ -187,30 +178,14 @@ public class HandleBuilder extends JavacAnnotationHandler { allFields.append(fieldNode); } - if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; - - JCTree extendsClause = Javac.getExtendsClause(td); - - if (extensible) { - boolean callBuilderBasedSuperConstructor = extendsClause != null; - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); - } else { - handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); - } + handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); nameOfBuilderMethod = null; + if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("")) { - if (inherit) { - annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); - return; - } - if (extensible) { - annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); - return; - } JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); if (!jmd.typarams.isEmpty()) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); @@ -218,23 +193,15 @@ public class HandleBuilder extends JavacAnnotationHandler { } tdParent = parent.up(); - td = (JCClassDecl) tdParent.get(); + JCClassDecl td = (JCClassDecl) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = jmd.thrown; nameOfBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null) { - if (inherit) { - annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); - return; - } - if (extensible) { - annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); - return; - } tdParent = parent.up(); - td = (JCClassDecl) tdParent.get(); + JCClassDecl td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); isStatic = (jmd.mods.flags & Flags.STATIC) != 0; JCExpression fullReturnType = jmd.restype; @@ -366,18 +333,7 @@ public class HandleBuilder extends JavacAnnotationHandler { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - String superclassBuilderClassName = null; - if (inherit) { - JCTree extendsClause = Javac.getExtendsClause(td); - if (extendsClause == null) { - annotationNode.addError("@Builder(inherit = true) requires that your class has an 'extends' clause."); - return; - } - - superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - if (superclassBuilderClassName == null || superclassBuilderClassName.isEmpty()) superclassBuilderClassName = extendsClause + "Builder"; - } - builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, superclassBuilderClassName); + builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast); } else { JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { @@ -437,7 +393,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, extensible); + JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); if (md != null) injectMethod(builderType, md); } @@ -546,79 +502,6 @@ public class HandleBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, List.of(statement)); return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); } - - /** - * 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. - * - * @param typeNode - * the type (with the {@code @Builder} annotation) for which a - * constructor should be generated. - * @param builderFields a list of fields in the builder which should be assigned to new instances. - * @param source the annotation (used for setting source code locations for the generated code). - * @param builderClassnameAsParameter - * If {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * If {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassAsParameter != null}. - */ - private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List builderFields, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - JavacTreeMaker maker = typeNode.getTreeMaker(); - - ListBuffer nullChecks = new ListBuffer(); - ListBuffer statements = new ListBuffer(); - - Name builderVariableName = typeNode.toName("b"); - for (BuilderFieldData bfd : builderFields) { - List nonNulls = findAnnotations(bfd.fieldNode, NON_NULL_PATTERN); - if (!nonNulls.isEmpty()) { - JCStatement nullCheck = generateNullCheck(maker, bfd.fieldNode, source); - if (nullCheck != null) { - nullChecks.append(nullCheck); - } - } - - JCExpression rhs; - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.fieldNode, bfd.type, statements, bfd.name, "b"); - rhs = maker.Ident(bfd.singularData.getPluralName()); - } else { - rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); - } - JCFieldAccess thisX = maker.Select(maker.Ident(bfd.fieldNode.toName("this")), bfd.rawName); - - JCExpression assign = maker.Assign(thisX, rhs); - - statements.append(maker.Exec(assign)); - } - - JCModifiers mods = maker.Modifiers(toJavacModifier(AccessLevel.PROTECTED), List.nil()); - - // Create a constructor that has just the builder as parameter. - ListBuffer params = new ListBuffer(); - long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); - Name builderClassname = typeNode.toName(builderClassnameAsParameter); - JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, maker.Ident(builderClassname), null); - params.append(param); - - if (callBuilderBasedSuperConstructor) { - // The first statement must be the call to the super constructor. - JCMethodInvocation callToSuperConstructor = maker.Apply(List.nil(), - maker.Ident(typeNode.toName("super")), - 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 generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { JavacTreeMaker maker = type.getTreeMaker(); @@ -647,56 +530,40 @@ public class HandleBuilder extends JavacAnnotationHandler { */ } - /** - * @param useBuilderBasedConstructor - * if true, the {@code build()} method will use a constructor - * that takes the builder instance as parameter (instead of a - * constructor with all relevant fields as parameters) - */ - private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { + private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; ListBuffer statements = new ListBuffer(); - ListBuffer args = new ListBuffer(); - // Extendable builders assign their values in the constructor, not in this build() method. - if (!useBuilderBasedConstructor) { - if (addCleaning) { - JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean"))); - JCStatement invokeClean = maker.Exec(maker.Apply(List.nil(), maker.Ident(type.toName("$lombokClean")), List.nil())); - JCIf ifUnclean = maker.If(notClean, invokeClean, null); - statements.append(ifUnclean); - } - - for (BuilderFieldData bfd : builderFields) { - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); - } - } - - for (BuilderFieldData bfd : builderFields) { - if (bfd.nameOfSetFlag != null) { - statements.append(maker.VarDef(maker.Modifiers(0L), bfd.name, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(type.toName("this")), bfd.name))); - statements.append(maker.If(maker.Unary(CTC_NOT, maker.Ident(bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.name),maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.nil()))), null)); - } - args.append(maker.Ident(bfd.name)); + if (addCleaning) { + JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean"))); + JCStatement invokeClean = maker.Exec(maker.Apply(List.nil(), maker.Ident(type.toName("$lombokClean")), List.nil())); + JCIf ifUnclean = maker.If(notClean, invokeClean, null); + statements.append(ifUnclean); + } + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); } - - if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); + } + + ListBuffer args = new ListBuffer(); + for (BuilderFieldData bfd : builderFields) { + if (bfd.nameOfSetFlag != null) { + statements.append(maker.VarDef(maker.Modifiers(0L), bfd.name, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(type.toName("this")), bfd.name))); + statements.append(maker.If(maker.Unary(CTC_NOT, maker.Ident(bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.name),maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.nil()))), null)); } + args.append(maker.Ident(bfd.name)); + } + + if (addCleaning) { + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); } if (builderName == null) { - if (useBuilderBasedConstructor) { - // 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); - } else { - // Use a constructor with all the fields. - call = maker.NewClass(null, List.nil(), returnType, args.toList(), null); - } + call = maker.NewClass(null, List.nil(), returnType, args.toList(), null); statements.append(maker.Return(call)); } else { ListBuffer typeParams = new ListBuffer(); @@ -819,13 +686,12 @@ public class HandleBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast, String parentBuilderClassName) { + public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); int modifiers = Flags.PUBLIC; if (isStatic) modifiers |= Flags.STATIC; JCModifiers mods = maker.Modifiers(modifiers); - JCExpression extending = parentBuilderClassName == null ? null : maker.Ident(tdParent.toName(parentBuilderClassName)); - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), null, List.nil(), List.nil()); return injectType(tdParent, builder); } -- cgit From 24a6c3354104e73874e8641f93517924a2f6d760 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sun, 15 Apr 2018 19:05:21 +0200 Subject: consider generics on the annotated class and potential superclass --- .../lombok/javac/handlers/HandleSuperBuilder.java | 103 ++++++++++++++------- 1 file changed, 67 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index d30740c0..893f38d4 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -37,7 +37,6 @@ import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; -import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; @@ -91,7 +90,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { java.util.List createdFields = new ArrayList(); } - @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + @Override + public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { SuperBuilder builderInstance = annotation.getInstance(); String builderMethodName = builderInstance.builderMethodName(); @@ -109,6 +109,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JCExpression returnType; List typeParams = List.nil(); List thrownExceptions = List.nil(); + List superclassTypeParams = List.nil(); boolean addCleaning = false; @@ -159,35 +160,39 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { allFields.append(fieldNode); } - // Set the default names of the builder classes if not customized via annotation. + // Set the names of the builder classes. String builderClassName = td.name.toString() + "Builder"; String builderImplClassName = builderClassName + "Impl"; JCTree extendsClause = Javac.getExtendsClause(td); 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(); + // A class name with a generics type, e.g., "Superclass". + extendsClause = ((JCTypeApply)extendsClause).getType(); + } if (extendsClause instanceof JCFieldAccess) { - // The extends clause consists of a fully-qualified name. Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier(); String superclassBuilderClassName = superclassClassName + "Builder"; superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess)extendsClause, tdParent.toName(superclassBuilderClassName)); } else if (extendsClause != null) { - // A simple class name is used in the extends clause. - String superclassBuilderClassName = extendsClause + "Builder"; + String superclassBuilderClassName = extendsClause.toString() + "Builder"; superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName); } // If there is no superclass, superclassBuilderClassName is still == null at this point. // You can use it to check whether to inherit or not. - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); - returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); + // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, typeParams, ast); + builderType = makeBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, typeParams, superclassTypeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -270,7 +275,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } - + /** * 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. @@ -278,6 +283,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { * @param typeNode * the type (with the {@code @Builder} annotation) for which a * constructor should be generated. + * @param typeParams * @param builderFields a list of fields in the builder which should be assigned to new instances. * @param source the annotation (used for setting source code locations for the generated code). * @param callBuilderBasedSuperConstructor @@ -285,7 +291,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { + 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; @@ -327,12 +333,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { ListBuffer params = new ListBuffer(); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); Name builderClassname = typeNode.toName(builderClassName); + // First add all generics that are present on the parent type. + ListBuffer typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker); // Now add the . - ListBuffer typeParams = new ListBuffer(); JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParams.add(wildcard); - typeParams.add(wildcard); - JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParams.toList()); + typeParamsForBuilderParameter.add(wildcard); + typeParamsForBuilderParameter.add(wildcard); + JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParamsForBuilderParameter.toList()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null); params.append(param); @@ -351,6 +358,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } + private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { + ListBuffer typeParamsForBuilderParameter = new ListBuffer(); + for (JCTypeParameter typeParam : typeParams) { + typeParamsForBuilderParameter.add(maker.Ident(typeParam.getName())); + } + return typeParamsForBuilderParameter; + } + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); @@ -540,7 +555,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, JCExpression superclassBuilderClassExpression, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass, JCExpression superclassBuilderClassExpression, List typeParams, List superclassTypeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); @@ -550,44 +565,60 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { allTypeParams.addAll(copyTypeParams(source, typeParams)); // Add builder-specific type params required for inheritable builders. // 1. The return type for the build() method, named "C", which extends the annotated class. - JCIdent annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + if (typeParams.nonEmpty()) { + // Add type params of the annotated class. + annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); + } allTypeParams.add(maker.TypeParameter(tdParent.toName("C"), List.of(annotatedClass))); // 2. The return type for all setter methods, named "B", which extends this builder class. Name builderClassName = tdParent.toName(builderClass); - JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), - List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); + ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + typeParamsForBuilder.add(maker.Ident(tdParent.toName("C"))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName("B"))); + JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList()); allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), 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. - extending = maker.TypeApply(superclassBuilderClassExpression, - List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); - // TODO: type params from annotated class + // 1. Add the type parameters of the superclass. + typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + // 2. Add the builder type params . + typeParamsForBuilder.add(maker.Ident(tdParent.toName("C"))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName("B"))); + extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList()); } JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.nil()); return injectType(tdParent, builder); } - public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderImplClass(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); - JCExpression extending = null; - if (builderClassName != null) { - extending = maker.Ident(tdParent.toName(builderClassName)); - - // Add any type params of the annotated class. - ListBuffer allTypeParams = new ListBuffer(); - allTypeParams.addAll(copyTypeParams(source, typeParams)); - // Add builder-specific type params required for inheritable builders. - // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. - JCIdent annotatedClassIdent = maker.Ident(tdParent.toName(tdParent.getName())); - // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - JCIdent builderImplClassIdent = maker.Ident(tdParent.toName(builderImplClass)); - extending = maker.TypeApply(extending, List.of(annotatedClassIdent, builderImplClassIdent)); + // Extend the abstract builder. + JCExpression extending = maker.Ident(tdParent.toName(builderAbstractClass)); + // Add any type params of the annotated class. + ListBuffer allTypeParams = new ListBuffer(); + allTypeParams.addAll(copyTypeParams(source, typeParams)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. + JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + if (typeParams.nonEmpty()) { + // Add type params of the annotated class. + annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); + } + // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. + JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass)); + if (typeParams.nonEmpty()) { + builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList()); } + ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + 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); -- cgit From f13f5c697385fde8c3833c47786909deba2833ce Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sun, 15 Apr 2018 19:31:02 +0200 Subject: use superclass generics for abstract builder class' extends clause --- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 893f38d4..740b8e89 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -37,6 +37,7 @@ import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; @@ -358,10 +359,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } - private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { + private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { ListBuffer typeParamsForBuilderParameter = new ListBuffer(); - for (JCTypeParameter typeParam : typeParams) { - typeParamsForBuilderParameter.add(maker.Ident(typeParam.getName())); + for (JCTree typeParam : typeParams) { + if (typeParam instanceof JCTypeParameter) { + typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); + } else if (typeParam instanceof JCIdent) { + typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); + } } return typeParamsForBuilderParameter; } @@ -583,7 +588,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (superclassBuilderClassExpression != null) { // If the annotated class extends another class, we want this builder to extend the builder of the superclass. // 1. Add the type parameters of the superclass. - typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); // 2. Add the builder type params . typeParamsForBuilder.add(maker.Ident(tdParent.toName("C"))); typeParamsForBuilder.add(maker.Ident(tdParent.toName("B"))); -- cgit From 054a966bad3037a758af50d5801461c8995fffa9 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 16 Apr 2018 20:15:44 +0200 Subject: avoid name clashes with generics on the annotated class --- .../lombok/javac/handlers/HandleSuperBuilder.java | 65 +++++++++++++++------- 1 file changed, 46 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 740b8e89..31976447 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -186,14 +186,29 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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()); + } + classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); + builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); + thrownExceptions = List.nil(); - generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClassExpression != null); // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, typeParams, superclassTypeParams, ast); + builderType = makeBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, + typeParams, superclassTypeParams, ast, classGenericName, builderGenericName); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -228,12 +243,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Generate abstract self() and build() methods in the abstract builder. - injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null)); - injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null)); + 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) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode); + makeSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); } // Create the toString() method for the abstract builder. @@ -277,6 +292,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } + private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { + if (!typeParamStrings.contains(classGenericName)) + return classGenericName; + int counter = 2; + while (typeParamStrings.contains(classGenericName + counter)) { + counter++; + } + return classGenericName + counter; + } + + /** * 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. @@ -371,7 +397,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return typeParamsForBuilderParameter; } - private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override) { + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); if (override) { @@ -380,7 +406,7 @@ 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("B")); + JCExpression returnType = maker.Ident(type.toName(builderGenericName)); return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } @@ -399,7 +425,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), body, null); } - private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override) { + private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); if (override) { @@ -408,7 +434,7 @@ 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("C")); + JCExpression returnType = maker.Ident(type.toName(classGenericName)); return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } @@ -520,10 +546,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } - public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source) { + public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, String builderGenericName) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); JavacTreeMaker maker = builderType.getTreeMaker(); - JCExpression returnType = maker.Ident(builderType.toName("B")); + JCExpression returnType = maker.Ident(builderType.toName(builderGenericName)); JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType, returnStatement); @@ -560,12 +586,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass, JCExpression superclassBuilderClassExpression, List typeParams, List superclassTypeParams, JCAnnotation ast) { + public JavacNode makeBuilderAbstractClass(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. - // TODO: Prevent name clashes with type params from annotated class. ListBuffer allTypeParams = new ListBuffer(); allTypeParams.addAll(copyTypeParams(source, typeParams)); // Add builder-specific type params required for inheritable builders. @@ -575,14 +602,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Add type params of the annotated class. annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); } - allTypeParams.add(maker.TypeParameter(tdParent.toName("C"), List.of(annotatedClass))); + allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.of(annotatedClass))); // 2. The return type for all setter methods, named "B", which extends this builder class. Name builderClassName = tdParent.toName(builderClass); ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); - typeParamsForBuilder.add(maker.Ident(tdParent.toName("C"))); - typeParamsForBuilder.add(maker.Ident(tdParent.toName("B"))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList()); - allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), List.of(typeApply))); + allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.of(typeApply))); JCExpression extending = null; if (superclassBuilderClassExpression != null) { @@ -590,8 +617,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // 1. Add the type parameters of the superclass. typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); // 2. Add the builder type params . - typeParamsForBuilder.add(maker.Ident(tdParent.toName("C"))); - typeParamsForBuilder.add(maker.Ident(tdParent.toName("B"))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); + typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList()); } -- cgit From 460c70890cea12e0224d8b978b36bfc89b3b0e1e Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 16 Apr 2018 20:21:02 +0200 Subject: let @SuperBuilder's toString() call super.toString() --- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 31976447..bd0f3b70 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -181,7 +181,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { String superclassBuilderClassName = extendsClause.toString() + "Builder"; superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName); } - // If there is no superclass, superclassBuilderClassName is still == null at this point. + // 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); @@ -257,8 +257,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { for (BuilderFieldData bfd : builderFields) { fieldNodes.addAll(bfd.createdFields); } - // TODO: toString() should also call super.toString(), so that it also shows fields from the superclass. - JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); + // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. + JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast); if (md != null) injectMethod(builderType, md); } -- cgit From 4434782cccf0559464c84b64356dd28b3dd38ba9 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sat, 21 Apr 2018 20:32:27 +0200 Subject: first try for Eclipse handler [WIP] --- .../eclipse/handlers/HandleSuperBuilder.java | 948 +++++++++++++++++++++ 1 file changed, 948 insertions(+) create mode 100644 src/core/lombok/eclipse/handlers/HandleSuperBuilder.java (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java new file mode 100644 index 00000000..cf2e0fbb --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -0,0 +1,948 @@ +/* + * Copyright (C) 2013-2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.handlers; + +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.Eclipse.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +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.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +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.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.mangosdk.spi.ProviderFor; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Builder.ObtainVia; +import lombok.ConfigurationKeys; +import lombok.Singular; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +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.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 HandleConstructor handleConstructor = new HandleConstructor(); + + 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 class BuilderFieldData { + TypeReference type; + char[] rawName; + char[] name; + char[] nameOfDefaultProvider; + char[] nameOfSetFlag; + SingularData singularData; + ObtainVia obtainVia; + EclipseNode obtainViaNode; + EclipseNode originalFieldNode; + + List createdFields = new ArrayList(); + } + + @Override + public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + 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; + } + + EclipseNode tdParent = annotationNode.up(); + + java.util.List builderFields = new ArrayList(); + TypeReference returnType; + TypeParameter[] typeParams; + TypeParameter[] superclassTypeParams = new TypeParameter[] {}; //TODO + + 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)); + for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + 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.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); + } + } + addObtainVia(bfd, fieldNode); + builderFields.add(bfd); + allFields.add(fieldNode); + } + + // Set the names of the builder classes. + String builderClassName = td.name.toString() + "Builder"; + String builderImplClassName = builderClassName + "Impl"; + + TypeReference extendsClause = td.superclass; + TypeReference superclassBuilderClass = null; +// if (extendsClause.getTypeArguments() != null) { +// // Remember the type arguments, because we need them for the extends clause of our abstract builder class. +// superclassTypeParams = ((JCTypeApply)extendsClause).getTypeArguments(); +// // A class name with a generics type, e.g., "Superclass". +// extendsClause = ((JCTypeApply)extendsClause).getType(); +// } + if (extendsClause instanceof QualifiedTypeReference) { + QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause; + String superclassClassName = qualifiedTypeReference.getLastToken().toString(); + 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); + superclassBuilderClass = new QualifiedTypeReference(tokens, poss); + } else if (extendsClause != null) { + String superclassBuilderClassName = extendsClause.getTypeName().toString() + "Builder"; + char[][] tokens = new char[][] {extendsClause.getTypeName().toString().toCharArray(), superclassBuilderClassName.toCharArray()}; + long[] poss = new long[tokens.length]; + Arrays.fill(poss, p); + superclassBuilderClass = new QualifiedTypeReference(tokens, poss); + } + // 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(td.name, td.typeParameters, p); + typeParams = td.typeParameters; + + // 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()); + } + classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); + builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); + + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClass != null); + + // Create the abstract builder class. + EclipseNode builderType = findInnerClass(tdParent, builderClassName); + if (builderType == null) { + builderType = makeBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, + typeParams, superclassTypeParams, 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) { + if (bfd.singularData.getSingularizer().requiresCleaning()) { + addCleaning = true; + break; + } + } + if (bfd.obtainVia != null) { + if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) { + bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\")."); + return; + } + if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) { + bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set."); + return; + } + } + } + + // Generate the fields in the abstract builder class that hold the values for the instance. + generateBuilderFields(builderType, builderFields, ast); + if (addCleaning) { + FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1); + cleanDecl.declarationSourceEnd = -1; + cleanDecl.modifiers = ClassFileConstants.AccPrivate; + 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) { + makeSetterMethodsForBuilder(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(); + 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. + MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD); + if (md != null) { + injectMethod(builderType, md); + } + } + + if (addCleaning) { + injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + } + + // Create the builder implementation class. + EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); + if (builderImplType == null) { + builderImplType = makeBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); + } else { + annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + return; + } + + // Create a simple constructor for the BuilderImpl class. + ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, annotationNode, Collections.emptyList()); + if (cd != null) { + injectMethod(builderImplType, cd); + } + + // 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); +// recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + if (md != null) { + injectMethod(tdParent, md); + } + } + +// recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); +// recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); + } + + private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { + if (!typeParamStrings.contains(classGenericName)) { + return classGenericName; + } + int counter = 2; + while (typeParamStrings.contains(classGenericName + counter)) { + counter++; + } + return classGenericName + counter; + } + + + /** + * 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. + * + * @param typeNode + * the type (with the {@code @Builder} annotation) for which a + * constructor should be generated. + * @param typeParams + * @param builderFields a list of fields in the builder which should be assigned to new instances. + * @param source the annotation (used for setting source code locations for the generated code). + * @param callBuilderBasedSuperConstructor + * If {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + 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) { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; + } else { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + } + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; + constructor.thrownExceptions = null; + constructor.typeParameters = null; + constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.arguments = null; + + 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"); + assignmentExpr = new SingleNameReference(fieldNode.name, p); + } else { + char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; + long[] positions = new long[] {p, p}; + assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); + } + + Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); + 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); + if (nullCheck != null) { + nullChecks.add(nullCheck); + } + } + } + + nullChecks.addAll(statements); + constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); + constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassName.toCharArray(), p), Modifier.FINAL)}; + + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + + injectMethod(typeNode, constructor); + } + +// private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { +// ListBuffer typeParamsForBuilderParameter = new ListBuffer(); +// for (JCTree typeParam : typeParams) { +// if (typeParam instanceof JCTypeParameter) { +// typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); +// } else if (typeParam instanceof JCIdent) { +// typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); +// } +// } +// return typeParamsForBuilderParameter; +// } + + private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { +// JavacTreeMaker maker = tdParent.getTreeMaker(); +// List annotations = List.nil(); +// if (override) { +// JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(tdParent, "Override"), List.nil()); +// annotations = List.of(overrideAnnotation); +// } +// JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); +// Name name = tdParent.toName(SELF_METHOD); +// JCExpression returnType = maker.Ident(tdParent.toName(builderGenericName)); +// +// return maker.MthodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + out.selector = SELF_METHOD.toCharArray(); + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + out.modifiers = ClassFileConstants.AccAbstract; + out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); + return out; + } + + private MethodDeclaration generateSelfMethod(EclipseNode 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); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); + out.selector = SELF_METHOD.toCharArray(); + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + out.modifiers = ClassFileConstants.AccAbstract; + out.returnType = new SingleTypeReference(builderImplType.getName().toCharArray(), 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; + 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)}; + } + + AllocationExpression allocationStatement = new AllocationExpression(); + allocationStatement.type = copyType(out.returnType); + // Use a constructor that only has this builder as parameter. + allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; + 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)); + MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + decl.selector = CLEAN_METHOD_NAME; + decl.modifiers = ClassFileConstants.AccPrivate; + decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); + decl.statements = statements.toArray(new Statement[0]); + decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return decl; + } + + 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; + + AllocationExpression allocationStatement = new AllocationExpression(); + allocationStatement.type = copyType(out.returnType); + // Use a constructor that only has this builder as parameter. + allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; + statements.add(new ReturnStatement(allocationStatement, 0, 0)); + out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]); + out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return out; + } + +// private MethodDeclaration generateBuildMethod(String buildName, JCExpression returnType, EclipseNode 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); +// } + + public MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { + int pS = source.sourceStart, pE = source.sourceEnd; + + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); + out.typeParameters = copyTypeParams(typeParameters, source); + out.selector = methodName; + out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); + out.returnType = copyType(fd.type, source); + out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; + fd.initialization = null; + + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); + return out; + } +// public MethodDeclaration generateDefaultProvider(Name methodName, EclipseNode 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); +// } + + public 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; + out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); + out.typeParameters = copyTypeParams(typeParams, source); + AllocationExpression invoke = new AllocationExpression(); + invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); + out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; + + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + return out; + } +// public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode source, EclipseNode type, List typeParams) { +// JavacTreeMaker maker = type.getTreeMaker(); +// +// ListBuffer typeArgs = new ListBuffer(); +// 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)); +// // Now add the . +// JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); +// 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); +// } + + public 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); + } + } + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); + } else { + 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 (field == null) { + FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = copyType(bfd.type); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + field = injectFieldAndMarkGenerated(builderType, fd); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + injectFieldAndMarkGenerated(builderType, fd); + } + bfd.createdFields.add(field); + } + } + } + + public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, String builderGenericName) { + boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); + if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { + makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode); + } else { + bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, true); + } + } +// public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData fieldNode, EclipseNode source, String builderGenericName) { +// boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); +// JavacTreeMaker maker = builderType.getTreeMaker(); +// JCExpression returnType = maker.Ident(builderType.toName(builderGenericName)); +// JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); +// if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { +// makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType, returnStatement); +// } else { +// fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); +// } +// } + + private static final AbstractMethodDeclaration[] EMPTY = {}; + private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode) { + TypeDeclaration td = (TypeDeclaration) builderType.get(); + AbstractMethodDeclaration[] existing = td.methods; + if (existing == null) { + existing = EMPTY; + } + 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; + } + char[] existingName = existing[i].selector; + if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) { + return; + } + } + + String setterName = fieldNode.getName(); + + MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, true, ClassFileConstants.AccPublic, + sourceNode, Collections.emptyList(), Collections.emptyList()); + injectMethod(builderType, setter); + } +// private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, Name nameOfSetFlag, EclipseNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { +// Name fieldName = ((JCVariableDecl) fieldNode.get()).name; +// +// for (EclipseNode child : builderType.down()) { +// if (child.getKind() != Kind.METHOD) { +// continue; +// } +// MethodDeclaration methodDecl = (MethodDeclaration) child.get(); +// Name existingName = methodDecl.name; +// if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) { +// return; +// } +// } +// +// String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); +// +// JavacTreeMaker maker = fieldNode.getTreeMaker(); +// +// MethodDeclaration newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); +// +// injectMethod(builderType, newMethod); +// } + + public EclipseNode findInnerClass(EclipseNode parent, String name) { + char[] c = name.toCharArray(); + for (EclipseNode child : parent.down()) { + if (child.getKind() != Kind.TYPE) { + continue; + } + TypeDeclaration td = (TypeDeclaration) child.get(); + if (Arrays.equals(td.name, c)) { + return child; + } + } + return null; + } + + public EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, + TypeReference superclassBuilderClass, TypeParameter[] typeParams, + TypeParameter[] superclassTypeParams, 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.typeParameters = copyTypeParams(typeParams, source); + builder.name = builderClass.toCharArray(); + builder.superclass = superclassBuilderClass; + +// // Keep any type params of the annotated class. +// ListBuffer allTypeParams = new ListBuffer(); +// allTypeParams.addAll(copyTypeParams(source, typeParams)); +// // Add builder-specific type params required for inheritable builders. +// // 1. The return type for the build() method, named "C", which extends the annotated class. +// JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); +// if (typeParams.nonEmpty()) { +// // Add type params of the annotated class. +// annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); +// } +// allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.of(annotatedClass))); +// // 2. The return type for all setter methods, named "B", which extends this builder class. +// Name builderClassName = tdParent.toName(builderClass); +// ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); +// typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); +// 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. +// // 1. Add the type parameters of the superclass. +// typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); +// // 2. Add the builder type params . +// typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); +// 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()); + + builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return injectType(tdParent, builder); + } + + public EclipseNode makeBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { + TypeDeclaration parent = (TypeDeclaration) tdParent.get(); + TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); + builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + builder.modifiers |= ClassFileConstants.AccPublic; + builder.modifiers |= ClassFileConstants.AccStatic; + builder.typeParameters = copyTypeParams(typeParams, source); + builder.name = builderImplClass.toCharArray(); + if (builderAbstractClass != null) { + builder.superclass = new SingleTypeReference(builderAbstractClass.toCharArray(), 0); + } + builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return injectType(tdParent, builder); + } +// public EclipseNode makeBuilderImplClass(EclipseNode source, EclipseNode 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. +// ListBuffer allTypeParams = new ListBuffer(); +// allTypeParams.addAll(copyTypeParams(source, typeParams)); +// // Add builder-specific type params required for inheritable builders. +// // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. +// JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); +// if (typeParams.nonEmpty()) { +// // Add type params of the annotated class. +// annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); +// } +// // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. +// JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass)); +// if (typeParams.nonEmpty()) { +// builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList()); +// } +// ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); +// 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); +// } + + private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { + for (EclipseNode child : node.down()) { + 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. + * + * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. + */ + private SingularData getSingularData(EclipseNode node, ASTNode source) { + for (EclipseNode child : node.down()) { + 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(); + if (explicitSingular.isEmpty()) { + if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { + node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); + explicitSingular = new String(pluralName); + } else { + explicitSingular = autoSingularize(new String(pluralName)); + if (explicitSingular == null) { + node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))"); + explicitSingular = new String(pluralName); + } + } + } + char[] singularName = explicitSingular.toCharArray(); + + TypeReference type = ((AbstractVariableDeclaration) node.get()).type; + TypeReference[] typeArgs = null; + String typeName; + if (type instanceof ParameterizedSingleTypeReference) { + typeArgs = ((ParameterizedSingleTypeReference) type).typeArguments; + typeName = new String(((ParameterizedSingleTypeReference) type).token); + } else if (type instanceof ParameterizedQualifiedTypeReference) { + TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments; + 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("."); + } + 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 static final char[] prefixWith(char[] prefix, char[] name) { + char[] out = new char[prefix.length + name.length]; + System.arraycopy(prefix, 0, out, 0, prefix.length); + System.arraycopy(name, 0, out, prefix.length, name.length); + return out; + } +} -- cgit From 0b789291e42706f8772eafcd2510233f22f6ad76 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 27 Apr 2018 16:59:42 +0200 Subject: fixed generation problem caused by reusing expressions --- .../lombok/eclipse/handlers/HandleSuperBuilder.java | 2 +- .../lombok/javac/handlers/HandleSuperBuilder.java | 19 +++++++++++++------ .../lombok/javac/handlers/JavacSingularsRecipes.java | 18 ++++++++++++------ .../handlers/singulars/JavacGuavaSingularizer.java | 9 +++++---- .../singulars/JavacJavaUtilListSetSingularizer.java | 9 +++++---- .../singulars/JavacJavaUtilMapSingularizer.java | 9 +++++---- 6 files changed, 41 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index cf2e0fbb..7e6daa17 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -225,7 +225,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // You can use it to check whether to inherit or not. returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); - typeParams = td.typeParameters; + typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; // are the generics for our builder. String classGenericName = "C"; diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index bd0f3b70..0004ddcb 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -26,6 +26,7 @@ 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; @@ -214,7 +215,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return; } - // Check that all builder fields for @Singular and @ObtainVia. + // 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) { if (bfd.singularData.getSingularizer().requiresCleaning()) { @@ -365,6 +366,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Now add the . JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); typeParamsForBuilderParameter.add(wildcard); + wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); typeParamsForBuilderParameter.add(wildcard); JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParamsForBuilderParameter.toList()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null); @@ -546,13 +548,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } - public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, String builderGenericName) { + public void makeSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); - JavacTreeMaker maker = builderType.getTreeMaker(); - JCExpression returnType = maker.Ident(builderType.toName(builderGenericName)); - JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); + final JavacTreeMaker maker = builderType.getTreeMaker(); + // TODO: Make these lambdas when switching to a source level >= 1.8. + Supplier returnType = new Supplier() { @Override public JCExpression get() { + return maker.Ident(builderType.toName(builderGenericName)); + }}; + Supplier returnStatement = new Supplier() { @Override public JCReturn get() { + return maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); + }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType, returnStatement); + makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get()); } else { fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index ea5d1a54..d6b1df44 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; @@ -203,12 +204,17 @@ public class JavacSingularsRecipes { * control over the return type and value, use * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, JCExpression, JCStatement)}. */ - public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { - JavacTreeMaker maker = builderType.getTreeMaker(); - JCExpression returnType = chain ? - cloneSelfType(builderType) : + public void generateMethods(SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain) { + final JavacTreeMaker maker = builderType.getTreeMaker(); + // TODO: Make these lambdas when switching to a source level >= 1.8. + Supplier returnType = new Supplier() { @Override public JCExpression get() { + return chain ? + cloneSelfType(builderType) : maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + }}; + Supplier returnStatement = new Supplier() { @Override public JCStatement get() { + return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + }}; generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); } /** @@ -216,7 +222,7 @@ public class JavacSingularsRecipes { * {@link SingularData}.
* Uses the given returnType and returnStatement for the generated methods. */ - public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement); + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement); public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable); diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 9a6a46f2..ecaf5c87 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -25,6 +25,7 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; +import java.util.function.Supplier; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; @@ -69,11 +70,11 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index dfffb06b..bd0f740e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -25,6 +25,7 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; +import java.util.function.Supplier; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; @@ -75,16 +76,16 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { if (useGuavaInstead(builderType)) { guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 930ec3a4..1b4dd960 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -25,6 +25,7 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Arrays; +import java.util.function.Supplier; import lombok.core.LombokImmutableList; import lombok.core.handlers.HandlerUtil; @@ -100,16 +101,16 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, JCExpression returnType, JCStatement returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { if (useGuavaInstead(builderType)) { guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { -- cgit From e12a956c2579ae6d38ef9850fb29f3f720ddac47 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Sun, 6 May 2018 22:07:28 +0200 Subject: ecj: builder generics [WIP] --- .../eclipse/handlers/HandleSuperBuilder.java | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 7e6daa17..486e8f91 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -194,7 +194,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } // Set the names of the builder classes. - String builderClassName = td.name.toString() + "Builder"; + String builderClassName = String.valueOf(td.name) + "Builder"; String builderImplClassName = builderClassName + "Impl"; TypeReference extendsClause = td.superclass; @@ -224,8 +224,8 @@ 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. - returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; + returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p); // are the generics for our builder. String classGenericName = "C"; @@ -607,7 +607,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); - out.typeParameters = copyTypeParams(typeParams, source); + //out.typeParameters = copyTypeParams(typeParams, source); AllocationExpression invoke = new AllocationExpression(); invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; @@ -770,6 +770,22 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } + private TypeParameter[] copyTypeParamsAndAddBuilderParams(TypeParameter[] typeParams, ASTNode source, String classGenericName, String builderGenericName) { + TypeParameter[] result = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2); + + TypeParameter o = new TypeParameter(); + setGeneratedBy(o, source); + o.name = classGenericName.toCharArray(); + result[result.length - 2] = o; + + o = new TypeParameter(); + setGeneratedBy(o, source); + o.name = builderGenericName.toCharArray(); + result[result.length - 1] = o; + + return result; + } + public EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { @@ -778,8 +794,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract; - builder.typeParameters = copyTypeParams(typeParams, source); builder.name = builderClass.toCharArray(); + builder.typeParameters = copyTypeParamsAndAddBuilderParams(typeParams, source, classGenericName, builderGenericName); builder.superclass = superclassBuilderClass; // // Keep any type params of the annotated class. -- cgit From ee51d77dd664ec13d3d3748798bfc74531594171 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 11 May 2018 14:41:38 +0200 Subject: ecj: type params for constructor, abstract builder class, self method --- .../eclipse/handlers/HandleSuperBuilder.java | 79 ++++++---------- .../resource/after-ecj/SuperBuilderBasic.java | 100 +++++++-------------- 2 files changed, 58 insertions(+), 121 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 486e8f91..3a0b76f9 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -58,6 +58,7 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; @@ -393,7 +394,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; - constructor.arguments = null; + + TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; + TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), wildcards, 0, p); + constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)}; List statements = new ArrayList(); List nullChecks = new ArrayList(); @@ -428,7 +432,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { nullChecks.addAll(statements); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassName.toCharArray(), p), Modifier.FINAL)}; constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); @@ -448,21 +451,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // } private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { -// JavacTreeMaker maker = tdParent.getTreeMaker(); -// List annotations = List.nil(); -// if (override) { -// JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(tdParent, "Override"), List.nil()); -// annotations = List.of(overrideAnnotation); -// } -// JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); -// Name name = tdParent.toName(SELF_METHOD); -// JCExpression returnType = maker.Ident(tdParent.toName(builderGenericName)); -// -// return maker.MthodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + // TODO: @Override annotation if override == true. MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccAbstract; + out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected; out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); return out; } @@ -770,22 +763,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } - private TypeParameter[] copyTypeParamsAndAddBuilderParams(TypeParameter[] typeParams, ASTNode source, String classGenericName, String builderGenericName) { - TypeParameter[] result = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2); - - TypeParameter o = new TypeParameter(); - setGeneratedBy(o, source); - o.name = classGenericName.toCharArray(); - result[result.length - 2] = o; - - o = new TypeParameter(); - setGeneratedBy(o, source); - o.name = builderGenericName.toCharArray(); - result[result.length - 1] = o; - - return result; - } - public EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { @@ -795,28 +772,26 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract; builder.name = builderClass.toCharArray(); - builder.typeParameters = copyTypeParamsAndAddBuilderParams(typeParams, source, classGenericName, builderGenericName); - builder.superclass = superclassBuilderClass; -// // Keep any type params of the annotated class. -// ListBuffer allTypeParams = new ListBuffer(); -// allTypeParams.addAll(copyTypeParams(source, typeParams)); -// // Add builder-specific type params required for inheritable builders. -// // 1. The return type for the build() method, named "C", which extends the annotated class. -// JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); -// if (typeParams.nonEmpty()) { -// // Add type params of the annotated class. -// annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); -// } -// allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.of(annotatedClass))); -// // 2. The return type for all setter methods, named "B", which extends this builder class. -// Name builderClassName = tdParent.toName(builderClass); -// ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); -// typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); -// 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))); -// + // 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. + // 1. The return type for the build() method, named "C", which extends the annotated class. + TypeParameter o = new TypeParameter(); + o.name = classGenericName.toCharArray(); + o.type = cloneSelfType(tdParent, source); + builder.typeParameters[builder.typeParameters.length - 2] = o; + // 2. The return type for all setter methods, named "B", which extends this builder class. + o = new TypeParameter(); + o.name = builderGenericName.toCharArray(); + TypeReference[] typerefs = new TypeReference[] { + new SingleTypeReference(classGenericName.toCharArray(), 0), + new SingleTypeReference(builderGenericName.toCharArray(), 0) + }; + o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0); + builder.typeParameters[builder.typeParameters.length - 1] = o; + + builder.superclass = superclassBuilderClass; // JCExpression extending = null; // if (superclassBuilderClassExpression != null) { // // If the annotated class extends another class, we want this builder to extend the builder of the superclass. @@ -827,8 +802,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // 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()); builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index cc86e05c..cecf82f7 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -1,99 +1,63 @@ import java.util.List; - public class SuperBuilderBasic { public static @lombok.experimental.SuperBuilder class Parent { - int field1; - @lombok.Singular List items; - - protected Parent(final ParentBuilder b) { - this.field1 = b.field1; - java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); - } - this.items = items; - } - - - @java.lang.SuppressWarnings("all") - public static abstract class ParentBuilder> { - @java.lang.SuppressWarnings("all") - private int field1; - @java.lang.SuppressWarnings("all") - private java.util.ArrayList items; - - @java.lang.SuppressWarnings("all") - protected abstract B self(); - - @java.lang.SuppressWarnings("all") - public abstract C build(); - - @java.lang.SuppressWarnings("all") - public B field1(final int field1) { + public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder> { + private @java.lang.SuppressWarnings("all") int field1; + private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + protected abstract @java.lang.SuppressWarnings("all") B self(); + public abstract @java.lang.SuppressWarnings("all") C build(); + public @java.lang.SuppressWarnings("all") B field1(final int field1) { this.field1 = field1; return self(); } - - @java.lang.SuppressWarnings("all") - public B item(final String item) { + public @java.lang.SuppressWarnings("all") B item(final String item) { if (this.items == null) this.items = new java.util.ArrayList(); this.items.add(item); return self(); } - - @java.lang.SuppressWarnings("all") - public B items(final java.util.Collection items) { + public @java.lang.SuppressWarnings("all") B items(final java.util.Collection items) { if (this.items == null) this.items = new java.util.ArrayList(); this.items.addAll(items); return self(); } - - @java.lang.SuppressWarnings("all") - public B clearItems() { + public @java.lang.SuppressWarnings("all") B clearItems() { if (this.items != null) this.items.clear(); return self(); } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public java.lang.String toString() { + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { return "SuperBuilderBasic.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")"; } } - - - @java.lang.SuppressWarnings("all") - private static final class ParentBuilderImpl extends ParentBuilder { - @java.lang.SuppressWarnings("all") - private ParentBuilderImpl() { + private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { + private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - protected ParentBuilderImpl self() { + protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public Parent build() { + public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() { return new Parent(this); } } - - @java.lang.SuppressWarnings("all") - public static ParentBuilder builder() { + public @java.lang.SuppressWarnings("all") static ParentBuilder builder() { return new ParentBuilderImpl(); } + int field1; + @lombok.Singular List items; + protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } } public static @lombok.experimental.SuperBuilder class Child extends Parent { -- cgit From fb401d4887895d1ebb8529d6323797f1bc8072a2 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 11 May 2018 17:19:47 +0200 Subject: ecj: setter methods now return self() --- .../eclipse/handlers/EclipseSingularsRecipes.java | 35 +++++++++- src/core/lombok/eclipse/handlers/HandleSetter.java | 34 ++++++---- .../eclipse/handlers/HandleSuperBuilder.java | 79 +++++++++------------- .../singulars/EclipseGuavaSingularizer.java | 18 ++--- .../EclipseJavaUtilListSetSingularizer.java | 20 ++---- .../singulars/EclipseJavaUtilMapSingularizer.java | 20 ++---- .../javac/handlers/JavacSingularsRecipes.java | 2 +- .../resource/after-ecj/SuperBuilderBasic.java | 13 ++-- 8 files changed, 114 insertions(+), 107 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 2cec2388..ac4e5c7b 100644 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -30,6 +30,7 @@ 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; @@ -46,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.Reference; +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; @@ -55,6 +57,7 @@ import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; @@ -217,7 +220,37 @@ public class EclipseSingularsRecipes { } public abstract List generateFields(SingularData data, EclipseNode builderType); - public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain); + + /** + * 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)}. + */ + public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { + // TODO: Make these lambdas when switching to a source level >= 1.8. + Supplier returnType = new Supplier() { + @Override public TypeReference get() { + return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); + } + }; + Supplier returnStatement = new Supplier() { + @Override public ReturnStatement get() { + return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; + } + }; + generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); + } + + /** + * Generates the singular, plural, and clear methods for the given + * {@link SingularData}.
+ * Uses the given returnType and returnStatement for the generated methods. + */ + public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement); + 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 6a9a5123..434939a6 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -119,7 +119,7 @@ public class HandleSetter extends EclipseAnnotationHandler { createSetterForField(level, fieldNode, sourceNode, false, empty, empty); } - public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.SETTER_FLAG_USAGE, "@Setter"); EclipseNode node = annotationNode.up(); @@ -196,22 +196,34 @@ public class HandleSetter extends EclipseAnnotationHandler { MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam); injectMethod(fieldNode.up(), method); } - + static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { + ASTNode source = sourceNode.get(); + int pS = source.sourceStart, pE = source.sourceEnd; + + TypeReference returnType = null; + ReturnStatement returnThis = null; + if (shouldReturnThis) { + returnType = cloneSelfType(fieldNode, source); + ThisReference thisRef = new ThisReference(pS, pE); + returnThis = new ReturnStatement(thisRef, pS, pE); + } + + 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) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); ASTNode source = sourceNode.get(); int pS = source.sourceStart, pE = source.sourceEnd; long p = (long)pS << 32 | pE; MethodDeclaration method = new MethodDeclaration(parent.compilationResult); method.modifiers = modifier; - if (shouldReturnThis) { - method.returnType = cloneSelfType(fieldNode, source); - } - - if (method.returnType == null) { + if (returnType != null) { + method.returnType = returnType; + } else { method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); method.returnType.sourceStart = pS; method.returnType.sourceEnd = pE; - shouldReturnThis = false; } Annotation[] deprecated = null; if (isFieldDeprecated(fieldNode) || deprecate) { @@ -248,10 +260,8 @@ public class HandleSetter extends EclipseAnnotationHandler { statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE)); } - if (shouldReturnThis) { - ThisReference thisRef = new ThisReference(pS, pE); - ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE); - statements.add(returnThis); + if (returnType != null && returnStatement != null) { + statements.add(returnStatement); } method.statements = statements.toArray(new Statement[0]); param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0])); diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3a0b76f9..473a2b9b 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -30,6 +30,7 @@ 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; @@ -45,6 +46,7 @@ import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; @@ -208,7 +210,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // } if (extendsClause instanceof QualifiedTypeReference) { QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause; - String superclassClassName = qualifiedTypeReference.getLastToken().toString(); + 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(); @@ -216,8 +218,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { Arrays.fill(poss, p); superclassBuilderClass = new QualifiedTypeReference(tokens, poss); } else if (extendsClause != null) { - String superclassBuilderClassName = extendsClause.getTypeName().toString() + "Builder"; - char[][] tokens = new char[][] {extendsClause.getTypeName().toString().toCharArray(), superclassBuilderClassName.toCharArray()}; + 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); superclassBuilderClass = new QualifiedTypeReference(tokens, poss); @@ -563,7 +566,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); // } - public MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { + private MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); @@ -591,7 +594,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.nil(), List.nil(), body, null); // } - public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { + 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; @@ -635,7 +638,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); // } - public void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) { + private void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) { List existing = new ArrayList(); for (EclipseNode child : builderType.down()) { if (child.getKind() == Kind.FIELD) { @@ -679,28 +682,32 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } - public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, String builderGenericName) { + private void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, 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() { + return new SingleTypeReference(builderGenericName.toCharArray(), 0); + } + }; + Supplier returnStatement = new Supplier() { + @Override public ReturnStatement get() { + MessageSend returnCall = new MessageSend(); + returnCall.receiver = ThisReference.implicitThis(); + returnCall.selector = SELF_METHOD.toCharArray(); + return new ReturnStatement(returnCall, 0, 0); + } + }; if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode); + makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnType.get(), returnStatement.get(), sourceNode); } else { - bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, true); + bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnType, returnStatement); } } -// public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData fieldNode, EclipseNode source, String builderGenericName) { -// boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); -// JavacTreeMaker maker = builderType.getTreeMaker(); -// JCExpression returnType = maker.Ident(builderType.toName(builderGenericName)); -// JCReturn returnStatement = maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); -// if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { -// makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType, returnStatement); -// } else { -// fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); -// } -// } private static final AbstractMethodDeclaration[] EMPTY = {}; - private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode) { + private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, ReturnStatement returnStatement, EclipseNode sourceNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) { @@ -722,34 +729,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { String setterName = fieldNode.getName(); - MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, true, ClassFileConstants.AccPublic, + MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, sourceNode, Collections.emptyList(), Collections.emptyList()); injectMethod(builderType, setter); } -// private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, Name nameOfSetFlag, EclipseNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { -// Name fieldName = ((JCVariableDecl) fieldNode.get()).name; -// -// for (EclipseNode child : builderType.down()) { -// if (child.getKind() != Kind.METHOD) { -// continue; -// } -// MethodDeclaration methodDecl = (MethodDeclaration) child.get(); -// Name existingName = methodDecl.name; -// if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) { -// return; -// } -// } -// -// String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); -// -// JavacTreeMaker maker = fieldNode.getTreeMaker(); -// -// MethodDeclaration newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); -// -// injectMethod(builderType, newMethod); -// } - public EclipseNode findInnerClass(EclipseNode parent, String name) { + private EclipseNode findInnerClass(EclipseNode parent, String name) { char[] c = name.toCharArray(); for (EclipseNode child : parent.down()) { if (child.getKind() != Kind.TYPE) { @@ -763,7 +748,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } - public EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, + private EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { @@ -807,7 +792,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return injectType(tdParent, builder); } - public EclipseNode makeBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { + private EclipseNode makeBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index eb2c9b35..d6c63944 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -27,6 +27,7 @@ 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; @@ -58,7 +59,6 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeIds; abstract class EclipseGuavaSingularizer extends EclipseSingularizer { protected String getSimpleTargetTypeName(SingularData data) { @@ -97,18 +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, boolean chain) { - TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(deprecate, returnType, returnStatement, data, builderType); + @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); } 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 cfa48eaf..0851757f 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -27,6 +27,7 @@ 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; @@ -52,7 +53,6 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeIds; abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingularizer { @Override public List listFieldsToBeGenerated(SingularData data, EclipseNode builderType) { @@ -88,23 +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, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); return; } - TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(deprecate, returnType, returnStatement, data, builderType); + 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); } 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 ee0e6409..976d9265 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -28,6 +28,7 @@ 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; @@ -51,7 +52,6 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.mangosdk.spi.ProviderFor; import lombok.core.LombokImmutableList; @@ -133,23 +133,15 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); return; } - TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); - - returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); - returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(deprecate, returnType, returnStatement, data, builderType); + 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); } private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index d6b1df44..67862355 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -200,7 +200,7 @@ public class JavacSingularsRecipes { * 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 + * chain == true, void otherwise. If you need more * control over the return type and value, use * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, JCExpression, JCStatement)}. */ diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index cecf82f7..7315760c 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -10,18 +10,21 @@ public class SuperBuilderBasic { this.field1 = field1; return self(); } - public @java.lang.SuppressWarnings("all") B item(final String item) { - if (this.items == null) this.items = new java.util.ArrayList(); + public @java.lang.SuppressWarnings("all") B item(String item) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); this.items.add(item); return self(); } - public @java.lang.SuppressWarnings("all") B items(final java.util.Collection items) { - if (this.items == null) this.items = new java.util.ArrayList(); + public @java.lang.SuppressWarnings("all") B items(java.util.Collection items) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); this.items.addAll(items); return self(); } public @java.lang.SuppressWarnings("all") B clearItems() { - if (this.items != null) this.items.clear(); + if ((this.items != null)) + this.items.clear(); return self(); } public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { -- cgit From 5d35fe3bfbd46de7410bca96e29731e8f5236618 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 14 May 2018 17:59:45 +0200 Subject: ecj: method body for self(); type args for BuilderImpl extends clause --- .../eclipse/handlers/HandleSuperBuilder.java | 46 ++++++---------------- .../resource/after-ecj/SuperBuilderBasic.java | 34 ++++++++-------- 2 files changed, 30 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 473a2b9b..d01e250d 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -478,8 +478,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccAbstract; + out.modifiers = ClassFileConstants.AccProtected; + out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get())}; out.returnType = new SingleTypeReference(builderImplType.getName().toCharArray(), 0); + out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)}; return out; } @@ -536,6 +538,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { 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); @@ -777,6 +780,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.typeParameters[builder.typeParameters.length - 1] = o; builder.superclass = superclassBuilderClass; + // TODO: Extends clause when there is a superclass. // JCExpression extending = null; // if (superclassBuilderClassExpression != null) { // // If the annotated class extends another class, we want this builder to extend the builder of the superclass. @@ -796,45 +800,19 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - builder.modifiers |= ClassFileConstants.AccPublic; - builder.modifiers |= ClassFileConstants.AccStatic; - builder.typeParameters = copyTypeParams(typeParams, source); + builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; builder.name = builderImplClass.toCharArray(); if (builderAbstractClass != null) { - builder.superclass = new SingleTypeReference(builderAbstractClass.toCharArray(), 0); + // Extend the abstract builder. + // TODO: 1. Add any type params of the annotated class. + // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. + // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. + TypeReference[] typeArgs = new TypeReference[] {cloneSelfType(tdParent, source), new SingleTypeReference(builderImplClass.toCharArray(), 0)}; + builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); } builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } -// public EclipseNode makeBuilderImplClass(EclipseNode source, EclipseNode 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. -// ListBuffer allTypeParams = new ListBuffer(); -// allTypeParams.addAll(copyTypeParams(source, typeParams)); -// // Add builder-specific type params required for inheritable builders. -// // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. -// JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); -// if (typeParams.nonEmpty()) { -// // Add type params of the annotated class. -// annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); -// } -// // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. -// JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass)); -// if (typeParams.nonEmpty()) { -// builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList()); -// } -// ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); -// 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); -// } private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { for (EclipseNode child : node.down()) { diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index 7315760c..d293c8f8 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -12,27 +12,28 @@ public class SuperBuilderBasic { } public @java.lang.SuppressWarnings("all") B item(String item) { if ((this.items == null)) - this.items = new java.util.ArrayList(); + this.items = new java.util.ArrayList(); this.items.add(item); return self(); } public @java.lang.SuppressWarnings("all") B items(java.util.Collection items) { if ((this.items == null)) - this.items = new java.util.ArrayList(); + this.items = new java.util.ArrayList(); this.items.addAll(items); return self(); } public @java.lang.SuppressWarnings("all") B clearItems() { if ((this.items != null)) - this.items.clear(); + this.items.clear(); 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 (((("SuperBuilderBasic.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")"); } } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -41,26 +42,27 @@ public class SuperBuilderBasic { return new Parent(this); } } - public @java.lang.SuppressWarnings("all") static ParentBuilder builder() { - return new ParentBuilderImpl(); - } int field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); this.field1 = b.field1; java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + switch (((b.items == null) ? 0 : b.items.size())) { + case 0 : + items = java.util.Collections.emptyList(); + break; + case 1 : + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default : + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); } this.items = items; } + public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { + return new ParentBuilderImpl(); + } } public static @lombok.experimental.SuperBuilder class Child extends Parent { -- cgit From 55976fec127e7674222f81e46532755081ffb454 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 17 May 2018 20:36:40 +0200 Subject: ecj: remaining type args for non-generic class builders --- .../eclipse/handlers/HandleSuperBuilder.java | 199 +++++++-------------- .../resource/after-ecj/SuperBuilderBasic.java | 71 +++----- 2 files changed, 82 insertions(+), 188 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index d01e250d..71683516 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -199,49 +199,61 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // 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()); + } + classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); + builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); TypeReference extendsClause = td.superclass; TypeReference superclassBuilderClass = null; -// if (extendsClause.getTypeArguments() != null) { -// // Remember the type arguments, because we need them for the extends clause of our abstract builder class. -// superclassTypeParams = ((JCTypeApply)extendsClause).getTypeArguments(); -// // A class name with a generics type, e.g., "Superclass
". -// extendsClause = ((JCTypeApply)extendsClause).getType(); -// } + TypeReference[] typeArguments = new TypeReference[] { + new SingleTypeReference(classGenericName.toCharArray(), 0), + new SingleTypeReference(builderGenericName.toCharArray(), 0) + }; if (extendsClause instanceof QualifiedTypeReference) { 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); - superclassBuilderClass = new QualifiedTypeReference(tokens, poss); + + // Every token may potentially have type args. Here, we only have + // type args for the last token, the superclass' builder. + TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; + typeArgsForTokens[typeArgsForTokens.length-1] = typeArguments; + + superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } 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); - superclassBuilderClass = new QualifiedTypeReference(tokens, poss); - } - // If there is no superclass, superclassBuilderClassExpression is still == null at this point. - // You can use it to check whether to inherit or not. - typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; - returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p); + // Every token may potentially have type args. Here, we only have + // type args for the last token, the superclass' builder. + TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; + typeArgsForTokens[typeArgsForTokens.length-1] = typeArguments; - // 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()); + superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } - classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); - builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); + // 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); @@ -335,14 +347,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // 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); -// recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); if (md != null) { injectMethod(tdParent, md); } } - -// recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); -// recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { @@ -441,40 +449,19 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { injectMethod(typeNode, constructor); } -// private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { -// ListBuffer typeParamsForBuilderParameter = new ListBuffer(); -// for (JCTree typeParam : typeParams) { -// if (typeParam instanceof JCTypeParameter) { -// typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); -// } else if (typeParam instanceof JCIdent) { -// typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); -// } -// } -// return typeParamsForBuilderParameter; -// } - private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { - // TODO: @Override annotation if override == true. MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected; + 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) { -// 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); MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -497,11 +484,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { if (override) { 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. - allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } @@ -550,51 +532,21 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return out; } -// private MethodDeclaration generateBuildMethod(String buildName, JCExpression returnType, EclipseNode 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 MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { - int pS = source.sourceStart, pE = source.sourceEnd; - - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); - out.typeParameters = copyTypeParams(typeParameters, source); - out.selector = methodName; - out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; - out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); - out.returnType = copyType(fd.type, source); - out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; - fd.initialization = null; - - out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); - return out; - } -// public MethodDeclaration generateDefaultProvider(Name methodName, EclipseNode fieldNode, List params) { -// JavacTreeMaker maker = fieldNode.getTreeMaker(); -// JCVariableDecl field = (JCVariableDecl) fieldNode.get(); +// private MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { +// int pS = source.sourceStart, pE = source.sourceEnd; // -// JCStatement statement = maker.Return(field.init); -// field.init = null; +// MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); +// out.typeParameters = copyTypeParams(typeParameters, source); +// out.selector = methodName; +// out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; +// out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; +// FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); +// out.returnType = copyType(fd.type, source); +// out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; +// fd.initialization = 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); +// out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); +// return out; // } private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { @@ -605,41 +557,21 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.selector = builderMethodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); + + // TODO: add type params //out.typeParameters = copyTypeParams(typeParams, source); + TypeReference[] returnTypeParams = new TypeReference[2]; + returnTypeParams[0] = new Wildcard(Wildcard.UNBOUND); + returnTypeParams[1] = new Wildcard(Wildcard.UNBOUND); + out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), returnTypeParams, 0, p); + AllocationExpression invoke = new AllocationExpression(); - invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); + 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; } -// public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode source, EclipseNode type, List typeParams) { -// JavacTreeMaker maker = type.getTreeMaker(); -// -// ListBuffer typeArgs = new ListBuffer(); -// 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)); -// // Now add the . -// JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); -// 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 void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) { List existing = new ArrayList(); @@ -779,18 +711,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0); builder.typeParameters[builder.typeParameters.length - 1] = o; - builder.superclass = superclassBuilderClass; - // TODO: Extends clause when there is a superclass. -// JCExpression extending = null; -// if (superclassBuilderClassExpression != null) { -// // If the annotated class extends another class, we want this builder to extend the builder of the superclass. -// // 1. Add the type parameters of the superclass. -// typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); -// // 2. Add the builder type params . -// typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); -// typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); -// extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList()); -// } + builder.superclass = copyType(superclassBuilderClass, source); builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index d293c8f8..88ac7e70 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -61,72 +61,45 @@ public class SuperBuilderBasic { this.items = items; } public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { - return new ParentBuilderImpl(); + return new ParentBuilderImpl(); } } - public static @lombok.experimental.SuperBuilder class Child extends Parent { - double field3; - - @java.lang.SuppressWarnings("all") - protected Child(final ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - - - @java.lang.SuppressWarnings("all") - public static abstract class ChildBuilder> extends Parent.ParentBuilder { - @java.lang.SuppressWarnings("all") - private double field3; - - @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 field3(final double field3) { + public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder> extends Parent.ParentBuilder { + private @java.lang.SuppressWarnings("all") double field3; + 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 field3(final double field3) { this.field3 = field3; return self(); } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public java.lang.String toString() { - return "SuperBuilderBasic.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")"; + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderBasic.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")"); } } - - - @java.lang.SuppressWarnings("all") - private static final class ChildBuilderImpl extends ChildBuilder { - @java.lang.SuppressWarnings("all") - private ChildBuilderImpl() { + private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder { + private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + super(); } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - protected ChildBuilderImpl self() { + protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public Child build() { + public @java.lang.Override @java.lang.SuppressWarnings("all") Child build() { return new Child(this); } } - - @java.lang.SuppressWarnings("all") - public static ChildBuilder builder() { + double field3; + protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + public static @java.lang.SuppressWarnings("all") ChildBuilder builder() { return new ChildBuilderImpl(); } } - + public SuperBuilderBasic() { + super(); + } public static void test() { Child x = Child.builder().field3(0.0).field1(5).item("").build(); } -- cgit From 9ee2491427f1522a7689d1c28a816294bcff315b Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 17 May 2018 21:26:42 +0200 Subject: ecj: SuperBuilderBasic test passes (removed superconstructor calls) --- src/core/lombok/eclipse/handlers/HandleConstructor.java | 12 +++++++----- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 13 ++++++------- test/transform/resource/after-ecj/SuperBuilderBasic.java | 3 --- 3 files changed, 13 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index a2940b88..ccf71158 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -259,7 +259,7 @@ public class HandleConstructor { ConstructorDeclaration constr = createConstructor( staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, - sourceNode, onConstructor); + true, sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.emptyList() : fields, source); @@ -301,7 +301,7 @@ public class HandleConstructor { @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection fields, boolean allToDefault, - EclipseNode sourceNode, List onConstructor) { + boolean superConstructorCall, EclipseNode sourceNode, List onConstructor) { ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); @@ -324,9 +324,11 @@ public class HandleConstructor { constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; + if (superConstructorCall) { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; + } constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 71683516..5586bc6b 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -63,6 +63,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; @@ -335,7 +336,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } // Create a simple constructor for the BuilderImpl class. - ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, annotationNode, Collections.emptyList()); + ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, false, annotationNode, Collections.emptyList()); if (cd != null) { injectMethod(builderImplType, cd); } @@ -395,11 +396,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; - } else { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; } - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -453,7 +452,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected; + out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; if (override) { out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; } @@ -477,7 +476,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; + 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); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index 88ac7e70..fc5f045b 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -33,7 +33,6 @@ public class SuperBuilderBasic { } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { - super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -45,7 +44,6 @@ public class SuperBuilderBasic { int field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { - super(); this.field1 = b.field1; java.util.List items; switch (((b.items == null) ? 0 : b.items.size())) { @@ -79,7 +77,6 @@ public class SuperBuilderBasic { } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder { private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { - super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; -- cgit From 61f0689f6bfb8de28c90eb786cd9e9e06a776287 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 11:52:42 +0200 Subject: ecj: copy type parameters to builder class --- .../eclipse/handlers/HandleSuperBuilder.java | 33 ++- .../after-ecj/SuperBuilderWithGenerics.java | 261 ++++++++------------- 2 files changed, 125 insertions(+), 169 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 5586bc6b..40c84a3e 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -681,6 +681,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } return null; } + + private TypeReference[] appendBuilderTypeReferences(TypeParameter[] typeParams, String classGenericName, String builderGenericName) { + TypeReference[] typerefs = new TypeReference[typeParams.length + 2]; + for (int i = 0; i < typeParams.length; i++) { + typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + typerefs[typerefs.length - 2] = new SingleTypeReference(classGenericName.toCharArray(), 0); + typerefs[typerefs.length - 1] = new SingleTypeReference(builderGenericName.toCharArray(), 0); + return typerefs; + } private EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, @@ -703,10 +713,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // 2. The return type for all setter methods, named "B", which extends this builder class. o = new TypeParameter(); o.name = builderGenericName.toCharArray(); - TypeReference[] typerefs = new TypeReference[] { - new SingleTypeReference(classGenericName.toCharArray(), 0), - new SingleTypeReference(builderGenericName.toCharArray(), 0) - }; + TypeReference[] typerefs = appendBuilderTypeReferences(typeParams, classGenericName, builderGenericName); o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0); builder.typeParameters[builder.typeParameters.length - 1] = o; @@ -722,12 +729,26 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; builder.name = builderImplClass.toCharArray(); + builder.typeParameters = copyTypeParams(typeParams, source); if (builderAbstractClass != null) { // Extend the abstract builder. - // TODO: 1. Add any type params of the annotated class. + // 1. Add any type params of the annotated class. + TypeReference[] typeArgs = new TypeReference[typeParams.length + 2]; + for (int i = 0; i < typeParams.length; i++) { + typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0); + } // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - TypeReference[] typeArgs = new TypeReference[] {cloneSelfType(tdParent, source), new SingleTypeReference(builderImplClass.toCharArray(), 0)}; + typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source); + if (typeParams.length > 0) { + TypeReference[] typerefs = new TypeReference[typeParams.length]; + for (int i = 0; i < typeParams.length; i++) { + typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + typeArgs[typeArgs.length - 1] = new ParameterizedSingleTypeReference(builderImplClass.toCharArray(), typerefs, 0, 0); + } else { + typeArgs[typeArgs.length - 1] = new SingleTypeReference(builderImplClass.toCharArray(), 0); + } builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); } builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java index 9f71be5a..0ec8efe5 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java @@ -1,166 +1,101 @@ import java.util.List; - public class SuperBuilderWithGenerics { - public static @lombok.experimental.SuperBuilder class Parent { - A field1; - List items; - - @java.lang.SuppressWarnings("all") - protected Parent(final ParentBuilder b) { - this.field1 = b.field1; - java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); - } - this.items = items; - } - - - @java.lang.SuppressWarnings("all") - public static abstract class ParentBuilder, B extends ParentBuilder> { - @java.lang.SuppressWarnings("all") - private A field1; - @java.lang.SuppressWarnings("all") - private java.util.ArrayList items; - - @java.lang.SuppressWarnings("all") - protected abstract B self(); - - @java.lang.SuppressWarnings("all") - public abstract C build(); - - @java.lang.SuppressWarnings("all") - public B field1(final A field1) { - this.field1 = field1; - return self(); - } - - @java.lang.SuppressWarnings("all") - public B item(final String item) { - if (this.items == null) this.items = new java.util.ArrayList(); - this.items.add(item); - return self(); - } - - @java.lang.SuppressWarnings("all") - public B items(final java.util.Collection items) { - if (this.items == null) this.items = new java.util.ArrayList(); - this.items.addAll(items); - return self(); - } - - @java.lang.SuppressWarnings("all") - public B clearItems() { - if (this.items != null) this.items.clear(); - return self(); - } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public java.lang.String toString() { - return "SuperBuilderWithGenerics.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")"; - } - } - - - @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") - public static ParentBuilder builder() { - return new ParentBuilderImpl(); - } - } - - @lombok.experimental.SuperBuilder - public static class Child extends Parent { - double field3; - - @java.lang.SuppressWarnings("all") - protected Child(final ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - - - @java.lang.SuppressWarnings("all") - public static abstract class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { - @java.lang.SuppressWarnings("all") - private double field3; - - @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 field3(final double field3) { - this.field3 = field3; - return self(); - } - - @java.lang.Override - @java.lang.SuppressWarnings("all") - public java.lang.String toString() { - return "SuperBuilderWithGenerics.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")"; - } - } - - - @java.lang.SuppressWarnings("all") - private static final class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { - @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") - public static ChildBuilder builder() { - return new ChildBuilderImpl(); - } - } - - public static void test() { - Child x = Child.builder().field3(0.0).field1(5).item("").build(); - } + public static @lombok.experimental.SuperBuilder class Parent { + public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> { + private @java.lang.SuppressWarnings("all") A field1; + private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + protected abstract @java.lang.SuppressWarnings("all") B self(); + public abstract @java.lang.SuppressWarnings("all") C build(); + public @java.lang.SuppressWarnings("all") B field1(final A field1) { + this.field1 = field1; + return self(); + } + public @java.lang.SuppressWarnings("all") B item(String item) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); + this.items.add(item); + return self(); + } + public @java.lang.SuppressWarnings("all") B items(java.util.Collection items) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); + this.items.addAll(items); + return self(); + } + public @java.lang.SuppressWarnings("all") B clearItems() { + if ((this.items != null)) + this.items.clear(); + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderWithGenerics.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> { + private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + } + 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); + } + } + A field1; + @lombok.Singular List items; + protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + @lombok.experimental.SuperBuilder + public static class Child extends Parent { + double field3; + protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + public static abstract class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { + private @java.lang.SuppressWarnings("all") double field3; + protected @java.lang.Override @java.lang.SuppressWarnings("all") abstract B self(); + public @java.lang.Override @java.lang.SuppressWarnings("all") abstract C build(); + public @java.lang.SuppressWarnings("all") B field3(final double field3) { + this.field3 = field3; + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return "SuperBuilderWithGenerics.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")"; + } + } + private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { + private ChildBuilderImpl() { + } + 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); + } + } + public static @java.lang.SuppressWarnings("all") ChildBuilder builder() { + return new ChildBuilderImpl(); + } + } + public static void test() { + Child x = Child.builder().field3(0.0).field1(5).item("").build(); + } } -- cgit From b2f44c326430fd2e46e2830af7bbaa010f571a44 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 13:10:34 +0200 Subject: ecj: type parameters for methods and constructor param --- .../eclipse/handlers/HandleSuperBuilder.java | 100 ++++++++++++--------- .../after-ecj/SuperBuilderWithGenerics.java | 48 +++++----- 2 files changed, 84 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 40c84a3e..2e7d26a9 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -232,10 +232,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { 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 // type args for the last token, the superclass' builder. TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; - typeArgsForTokens[typeArgsForTokens.length-1] = typeArguments; + typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments); superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } else if (extendsClause != null) { @@ -245,12 +247,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { char[][] tokens = new char[][] {superClass.toCharArray(), 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 // type args for the last token, the superclass' builder. TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; - typeArgsForTokens[typeArgsForTokens.length-1] = typeArguments; - + typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments); + superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } // If there is no superclass, superclassBuilderClassExpression is still == null at this point. @@ -365,7 +369,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return classGenericName + counter; } - + private TypeReference[] getTypeParametersFrom(TypeReference typeRef) { + TypeReference[][] typeArgss = typeRef.getTypeArguments(); + TypeReference[] typeArgs = new TypeReference[0]; + if (typeArgss != null && typeArgss.length > 0) { + typeArgs = typeArgss[typeArgss.length - 1]; + } + return typeArgs; + } + /** * 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. @@ -406,7 +418,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; - TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), wildcards, 0, p); + 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(); @@ -531,23 +543,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return out; } -// private MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { -// int pS = source.sourceStart, pE = source.sourceEnd; -// -// MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); -// out.typeParameters = copyTypeParams(typeParameters, source); -// out.selector = methodName; -// out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; -// out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; -// FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); -// out.returnType = copyType(fd.type, source); -// out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; -// fd.initialization = null; -// -// out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); -// return out; -// } - 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; @@ -558,11 +553,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; // TODO: add type params - //out.typeParameters = copyTypeParams(typeParams, source); - TypeReference[] returnTypeParams = new TypeReference[2]; - returnTypeParams[0] = new Wildcard(Wildcard.UNBOUND); - returnTypeParams[1] = new Wildcard(Wildcard.UNBOUND); - out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), returnTypeParams, 0, p); + 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); @@ -683,15 +676,48 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } private TypeReference[] appendBuilderTypeReferences(TypeParameter[] typeParams, String classGenericName, String builderGenericName) { - TypeReference[] typerefs = new TypeReference[typeParams.length + 2]; + 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[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) { + TypeReference[] typerefs = new TypeReference[typeParams.length + typeReferencesToAppend.length]; for (int i = 0; i < typeParams.length; i++) { typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); } - typerefs[typerefs.length - 2] = new SingleTypeReference(classGenericName.toCharArray(), 0); - typerefs[typerefs.length - 1] = new SingleTypeReference(builderGenericName.toCharArray(), 0); + for (int i = 0; i < typeReferencesToAppend.length; i++) { + typerefs[typeParams.length + i] = typeReferencesToAppend[i]; + } 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]; + } + return result; + } + + private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) { + if (typeParams.length > 0) { + TypeReference[] typerefs = new TypeReference[typeParams.length]; + for (int i = 0; i < typeParams.length; i++) { + typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + return new ParameterizedSingleTypeReference(referenceName.toCharArray(), typerefs, 0, 0); + } else { + return new SingleTypeReference(referenceName.toCharArray(), 0); + } + + } + + private EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { @@ -722,7 +748,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } - + private EclipseNode makeBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); @@ -740,15 +766,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source); - if (typeParams.length > 0) { - TypeReference[] typerefs = new TypeReference[typeParams.length]; - for (int i = 0; i < typeParams.length; i++) { - typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); - } - typeArgs[typeArgs.length - 1] = new ParameterizedSingleTypeReference(builderImplClass.toCharArray(), typerefs, 0, 0); - } else { - typeArgs[typeArgs.length - 1] = new SingleTypeReference(builderImplClass.toCharArray(), 0); - } + typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams); builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); } builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java index 0ec8efe5..cb717bcf 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java @@ -46,43 +46,37 @@ public class SuperBuilderWithGenerics { protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { this.field1 = b.field1; java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + switch (((b.items == null) ? 0 : b.items.size())) { + case 0 : + items = java.util.Collections.emptyList(); + break; + case 1 : + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default : + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); } this.items = items; } - public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { + public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { return new ParentBuilderImpl(); } } - @lombok.experimental.SuperBuilder - public static class Child extends Parent { - double field3; - protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - public static abstract class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { + public static @lombok.experimental.SuperBuilder class Child extends Parent { + public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { private @java.lang.SuppressWarnings("all") double field3; - protected @java.lang.Override @java.lang.SuppressWarnings("all") abstract B self(); - public @java.lang.Override @java.lang.SuppressWarnings("all") abstract C build(); + 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 field3(final double field3) { this.field3 = field3; return self(); } public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { - return "SuperBuilderWithGenerics.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")"; + return (((("SuperBuilderWithGenerics.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")"); } } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { - private ChildBuilderImpl() { + private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; @@ -91,10 +85,18 @@ public class SuperBuilderWithGenerics { return new Child(this); } } - public static @java.lang.SuppressWarnings("all") ChildBuilder builder() { + double field3; + protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + public static @java.lang.SuppressWarnings("all") ChildBuilder builder() { return new ChildBuilderImpl(); } } + public SuperBuilderWithGenerics() { + super(); + } public static void test() { Child x = Child.builder().field3(0.0).field1(5).item("").build(); } -- cgit From a74f4bdda04efcfea36f7b602fe2bdda2ef613fa Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 13:23:57 +0200 Subject: ecj: no empty <> if there are no type params --- .../eclipse/handlers/HandleSuperBuilder.java | 40 ++++++++++++---------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 2e7d26a9..fa069939 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -91,8 +91,6 @@ import lombok.experimental.SuperBuilder; @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 HandleConstructor handleConstructor = new HandleConstructor(); - 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', '$'}; @@ -552,8 +550,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - // TODO: add type params - out.typeParameters = copyTypeParams(typeParams, source); + // Add type params if there are any. + if (typeParams != null && typeParams.length > 0) { + out.typeParameters = copyTypeParams(typeParams, source); + } TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) }; out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p); @@ -704,20 +704,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return result; } - private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) { - if (typeParams.length > 0) { - TypeReference[] typerefs = new TypeReference[typeParams.length]; - for (int i = 0; i < typeParams.length; i++) { - typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); - } - return new ParameterizedSingleTypeReference(referenceName.toCharArray(), typerefs, 0, 0); - } else { - return new SingleTypeReference(referenceName.toCharArray(), 0); - } - - } - - private EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { @@ -755,7 +741,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; builder.name = builderImplClass.toCharArray(); - builder.typeParameters = copyTypeParams(typeParams, source); + // Add type params if there are any. + 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. @@ -850,6 +839,19 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } + private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) { + if (typeParams.length > 0) { + TypeReference[] typerefs = new TypeReference[typeParams.length]; + for (int i = 0; i < typeParams.length; i++) { + typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + return new ParameterizedSingleTypeReference(referenceName.toCharArray(), typerefs, 0, 0); + } else { + return new SingleTypeReference(referenceName.toCharArray(), 0); + } + + } + 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); -- cgit From 9470ca650ef6eb977047464041bff26fcafdeae3 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 13:30:54 +0200 Subject: ecj: revert changes to HandleBuilder (everything in HandleSuperBuilder) --- .../lombok/eclipse/handlers/HandleBuilder.java | 244 ++++----------------- 1 file changed, 48 insertions(+), 196 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 08ef91b5..d4cdc654 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -25,7 +25,6 @@ import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -41,7 +40,6 @@ 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.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -52,7 +50,6 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; @@ -84,7 +81,6 @@ import lombok.core.HandlerPriority; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; -import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; @@ -106,7 +102,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { } private static class BuilderFieldData { - EclipseNode fieldNode; TypeReference type; char[] rawName; char[] name; @@ -160,10 +155,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); - - boolean inherit = builderInstance.inherit(); - boolean extensible = inherit || builderInstance.extensible(); // inherit implies extendable - String toBuilderMethodName = "toBuilder"; boolean toBuilder = builderInstance.toBuilder(); List typeArgsForToBuilder = null; @@ -191,11 +182,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { boolean addCleaning = false; boolean isStatic = true; - TypeDeclaration td = null; - if (parent.get() instanceof TypeDeclaration) { tdParent = parent; - td = (TypeDeclaration) tdParent.get(); + TypeDeclaration td = (TypeDeclaration) tdParent.get(); List allFields = new ArrayList(); boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); @@ -205,7 +194,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); BuilderFieldData bfd = new BuilderFieldData(); - bfd.fieldNode = fieldNode; bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; @@ -239,29 +227,15 @@ public class HandleBuilder extends EclipseAnnotationHandler { allFields.add(fieldNode); } - if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder"; - - if (extensible) { - boolean callBuilderBasedSuperConstructor = td.superclass != null; - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); - } else { - handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, - Collections.emptyList(), annotationNode); - } + handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, + Collections.emptyList(), annotationNode); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; thrownExceptions = null; nameOfStaticBuilderMethod = null; + if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder"; } else if (parent.get() instanceof ConstructorDeclaration) { - if (inherit) { - annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); - return; - } - if (extensible) { - annotationNode.addError("@Builder(extensible=true) is only supported for type builders."); - return; - } ConstructorDeclaration cd = (ConstructorDeclaration) parent.get(); if (cd.typeParameters != null && cd.typeParameters.length > 0) { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); @@ -269,21 +243,13 @@ public class HandleBuilder extends EclipseAnnotationHandler { } tdParent = parent.up(); - td = (TypeDeclaration) tdParent.get(); + TypeDeclaration td = (TypeDeclaration) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; thrownExceptions = cd.thrownExceptions; nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = new String(cd.selector) + "Builder"; } else if (parent.get() instanceof MethodDeclaration) { - if (inherit) { - annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); - return; - } - if (extensible) { - annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); - return; - } MethodDeclaration md = (MethodDeclaration) parent.get(); tdParent = parent.up(); isStatic = md.isStatic(); @@ -414,20 +380,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - String superclassBuilderClassName = null; - if (inherit) { - if (td.superclass == null) { - annotationNode.addError("@Builder(inherit = true) requires that your class has an 'extends' clause."); - return; - } - - superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - if (superclassBuilderClassName == null || superclassBuilderClassName.isEmpty()) { - superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; - } - } - - builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, superclassBuilderClassName); + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast); } else { TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { @@ -491,7 +444,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, extensible); + MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); if (md != null) injectMethod(builderType, md); } @@ -539,7 +492,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration out = new MethodDeclaration( + ((CompilationUnitDeclaration) type.top().get()).compilationResult); out.selector = methodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -577,92 +531,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { } - /** - * 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. - * - * @param typeNode - * the type (with the {@code @Builder} annotation) for which a - * constructor should be generated. - * @param builderFields a list of fields in the builder which should be assigned to new instances. - * @param sourceNode the annotation (used for setting source code locations for the generated code). - * @param builderClassnameAsParameter - * If {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * If {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassAsParameter != null}. - */ - private void generateBuilderBasedConstructor(EclipseNode typeNode, List builderFields, EclipseNode sourceNode, - String builderClassnameAsParameter, 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) { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); - constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; - } else { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - } - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; - constructor.thrownExceptions = null; - constructor.typeParameters = null; - constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; - constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; - constructor.arguments = null; - - List statements = new ArrayList(); - List nullChecks = new ArrayList(); - - for (BuilderFieldData fieldNode : builderFields) { - char[] fieldName = removePrefixFromField(fieldNode.fieldNode); - 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"); - assignmentExpr = new SingleNameReference(fieldNode.name, p); - } else { - char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; - long[] positions = new long[] {p, p}; - assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); - } - - Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); - statements.add(assignment); - Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.fieldNode.get(), NON_NULL_PATTERN); - if (nonNulls.length != 0) { - Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.fieldNode.get(), sourceNode); - if (nullCheck != null) { - nullChecks.add(nullCheck); - } - } - } - - nullChecks.addAll(statements); - constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; - - constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); - - injectMethod(typeNode, constructor); - } - private MethodDeclaration generateCleanMethod(List builderFields, EclipseNode builderType, ASTNode source) { List statements = new ArrayList(); @@ -685,60 +553,51 @@ public class HandleBuilder extends EclipseAnnotationHandler { return decl; } - /** - * @param useBuilderBasedConstructor - * if true, the {@code build()} method will use a constructor - * that takes the builder instance as parameter (instead of a - * constructor with all relevant fields as parameters) - */ - public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, boolean useBuilderBasedConstructor) { + public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); - List args = new ArrayList(); - // Extendable builders assign their values in the constructor, not in this build() method. - if (!useBuilderBasedConstructor) { - if (addCleaning) { - FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); - thisUnclean.receiver = new ThisReference(0, 0); - Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT); - MessageSend invokeClean = new MessageSend(); - invokeClean.selector = CLEAN_METHOD_NAME; - statements.add(new IfStatement(notClean, invokeClean, 0, 0)); - } - - for (BuilderFieldData bfd : builderFields) { - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this"); - } - } - - for (BuilderFieldData bfd : builderFields) { - if (bfd.nameOfSetFlag != null) { - MessageSend inv = new MessageSend(); - inv.sourceStart = source.sourceStart; - inv.sourceEnd = source.sourceEnd; - inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); - inv.selector = bfd.nameOfDefaultProvider; - inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); - - args.add(new ConditionalExpression( - new SingleNameReference(bfd.nameOfSetFlag, 0L), - new SingleNameReference(bfd.name, 0L), - inv)); - } else { - args.add(new SingleNameReference(bfd.name, 0L)); - } + if (addCleaning) { + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); + thisUnclean.receiver = new ThisReference(0, 0); + Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT); + MessageSend invokeClean = new MessageSend(); + invokeClean.selector = CLEAN_METHOD_NAME; + statements.add(new IfStatement(notClean, invokeClean, 0, 0)); + } + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name); } - - if (addCleaning) { - FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); - thisUnclean.receiver = new ThisReference(0, 0); - statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); + } + + List args = new ArrayList(); + for (BuilderFieldData bfd : builderFields) { + if (bfd.nameOfSetFlag != null) { + MessageSend inv = new MessageSend(); + inv.sourceStart = source.sourceStart; + inv.sourceEnd = source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); + inv.selector = bfd.nameOfDefaultProvider; + inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); + + args.add(new ConditionalExpression( + new SingleNameReference(bfd.nameOfSetFlag, 0L), + new SingleNameReference(bfd.name, 0L), + inv)); + } else { + args.add(new SingleNameReference(bfd.name, 0L)); } } + if (addCleaning) { + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); + thisUnclean.receiver = new ThisReference(0, 0); + statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); + } + out.modifiers = ClassFileConstants.AccPublic; out.selector = name.toCharArray(); out.thrownExceptions = copyTypes(thrownExceptions); @@ -748,13 +607,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (staticName == null) { AllocationExpression allocationStatement = new AllocationExpression(); allocationStatement.type = copyType(out.returnType); - if (useBuilderBasedConstructor) { - // Use a constructor that only has this builder as parameter. - allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; - } else { - // Use a constructor with all the fields. - allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]); - } + allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]); statements.add(new ReturnStatement(allocationStatement, 0, 0)); } else { MessageSend invoke = new MessageSend(); @@ -903,7 +756,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { return null; } - public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source, String parentBuilderClassName) { + public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -911,7 +764,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic; builder.typeParameters = copyTypeParams(typeParams, source); builder.name = builderClassName.toCharArray(); - if (parentBuilderClassName != null) builder.superclass = new SingleTypeReference(parentBuilderClassName.toCharArray(), 0); builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } -- cgit From cdee277f11becb5a3629158e0ffbe7292a85d255 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 13:38:07 +0200 Subject: ecj: fixed compiler errors after revert of HandleBuilder --- src/core/lombok/eclipse/handlers/HandleBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index d4cdc654..331fbde4 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -434,7 +434,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( - AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, + AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, true, annotationNode, Collections.emptyList()); if (cd != null) injectMethod(builderType, cd); } @@ -569,7 +569,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this"); } } @@ -640,7 +640,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { return trs; } - public MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { + public static MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); -- cgit From 04269ef14bf463a1ebe2dd2b376e0a3ff71535e5 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 15:27:47 +0200 Subject: fixed compiler errors in ant --- src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java | 2 +- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index ac4e5c7b..e24cd5b5 100644 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -229,7 +229,7 @@ public class EclipseSingularsRecipes { * control over the return type and value, use * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, Supplier, Supplier)}. */ - public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { + 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() { diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index fa069939..a473a084 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -368,7 +368,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } private TypeReference[] getTypeParametersFrom(TypeReference typeRef) { - TypeReference[][] typeArgss = typeRef.getTypeArguments(); + TypeReference[][] typeArgss = null; + if (typeRef instanceof ParameterizedQualifiedTypeReference) { + typeArgss = ((ParameterizedQualifiedTypeReference)typeRef).typeArguments; + } else if (typeRef instanceof ParameterizedSingleTypeReference) { + typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference)typeRef).typeArguments}; + } TypeReference[] typeArgs = new TypeReference[0]; if (typeArgss != null && typeArgss.length > 0) { typeArgs = typeArgss[typeArgss.length - 1]; @@ -609,7 +614,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } - private void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, String builderGenericName) { + private void makeSetterMethodsForBuilder(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. -- cgit From 4c5a7cddab109391d7494be2a0fc575b466dfa43 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 22:38:08 +0200 Subject: code formatting, reordering, rename etc. --- .../lombok/javac/handlers/HandleSuperBuilder.java | 532 +++++++++++---------- .../resource/after-delombok/SuperBuilderBasic.java | 42 +- .../after-delombok/SuperBuilderWithGenerics.java | 42 +- 3 files changed, 329 insertions(+), 287 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 0004ddcb..0bb16d56 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -1,16 +1,16 @@ /* * Copyright (C) 2013-2018 The Project Lombok Authors. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -88,33 +88,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) { - 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; - + SuperBuilder superbuilderAnnotation = annotation.getInstance(); + + 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; + } + 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; @@ -134,28 +142,32 @@ 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.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); + if (md != null) { + injectMethod(tdParent, md); + } } addObtainVia(bfd, fieldNode); builderFields.add(bfd); @@ -172,11 +184,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { superclassTypeParams = ((JCTypeApply)extendsClause).getTypeArguments(); // A class name with a generics type, e.g., "Superclass". 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"; @@ -187,7 +199,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; - + // are the generics for our builder. String classGenericName = "C"; String builderGenericName = "B"; @@ -199,22 +211,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); - - thrownExceptions = List.nil(); - generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClassExpression != null); + thrownExceptions = List.nil(); - // Create the abstract builder class. - JavacNode builderType = findInnerClass(tdParent, builderClassName); - if (builderType == null) { - builderType = makeBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, - typeParams, superclassTypeParams, 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) { @@ -235,7 +234,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } - // Generate the fields in the abstract builder class that hold the values for the instance. + // Create the abstract builder class. + JavacNode builderType = findInnerClass(tdParent, builderClassName); + if (builderType == null) { + builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, + typeParams, superclassTypeParams, ast, classGenericName, builderGenericName); + } else { + 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) { JavacTreeMaker maker = builderType.getTreeMaker(); @@ -249,9 +258,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); + 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(); @@ -260,15 +269,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast); - if (md != null) injectMethod(builderType, md); + if (md != null) { + injectMethod(builderType, md); + } + } + + if (addCleaning) { + injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); } - - if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); - + // Create the builder implementation class. JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { - builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -276,33 +289,107 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // 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)); - + + // Generate a constructor in the annotated class that takes a builder as argument. + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClassExpression != null); + // 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 md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - if (md != null) injectMethod(tdParent, md); + if (md != null) { + injectMethod(tdParent, md); + } } - + recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } - - private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { - if (!typeParamStrings.contains(classGenericName)) - return classGenericName; - int counter = 2; - while (typeParamStrings.contains(classGenericName + counter)) { - counter++; + + /** + * 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)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method, named "C", which extends the annotated class. + JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + if (typeParams.nonEmpty()) { + // Add type params of the annotated class. + annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); } - return classGenericName + counter; + allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.of(annotatedClass))); + // 2. The return type for all setter methods, named "B", which extends this builder class. + Name builderClassName = tdParent.toName(builderClass); + ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); + 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. + // 1. Add the type parameters of the superclass. + typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); + // 2. Add the builder type params . + typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); + 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. + ListBuffer allTypeParams = new ListBuffer(); + allTypeParams.addAll(copyTypeParams(source, typeParams)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. + JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + if (typeParams.nonEmpty()) { + // Add type params of the annotated class. + annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); + } + // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. + JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass)); + if (typeParams.nonEmpty()) { + builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList()); + } + ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); + 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); } - /** * Generates a constructor that has a builder as the only parameter. @@ -311,7 +398,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { * @param typeNode * the type (with the {@code @Builder} annotation) for which a * constructor should be generated. - * @param typeParams + * @param typeParams * @param builderFields a list of fields in the builder which should be assigned to new instances. * @param source the annotation (used for setting source code locations for the generated code). * @param callBuilderBasedSuperConstructor @@ -321,16 +408,12 @@ 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; - boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; - if (isEnum) { - level = AccessLevel.PRIVATE; - } - + 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); @@ -340,7 +423,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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"); @@ -348,15 +431,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } else { rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); } - JCFieldAccess thisX = maker.Select(maker.Ident(bfd.originalFieldNode.toName("this")), bfd.rawName); - + JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName); + JCExpression assign = maker.Assign(thisX, rhs); - + statements.append(maker.Exec(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()); @@ -387,16 +470,31 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } - private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { - ListBuffer typeParamsForBuilderParameter = new ListBuffer(); - for (JCTree typeParam : typeParams) { - if (typeParam instanceof JCTypeParameter) { - typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); - } else if (typeParam instanceof JCIdent) { - typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); - } + 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)); } - return typeParamsForBuilderParameter; + + 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)); + // Now add the . + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + 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) { @@ -412,7 +510,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } - + private JCMethodDecl generateSelfMethod(JavacNode builderImplType) { JavacTreeMaker maker = builderImplType.getTreeMaker(); @@ -426,7 +524,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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(); @@ -440,87 +538,62 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); } - - private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { - JavacTreeMaker maker = type.getTreeMaker(); - ListBuffer statements = new ListBuffer(); - - for (BuilderFieldData bfd : builderFields) { - if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements); - } - } - - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); - JCBlock body = maker.Block(0, statements.toList()); - 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 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); } - - public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List params) { + + private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { + JavacTreeMaker maker = type.getTreeMaker(); + ListBuffer statements = new ListBuffer(); + + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements); + } + } + + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); + JCBlock body = maker.Block(0, statements.toList()); + 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); } - - public 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)); - } - - 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)); - // Now add the . - JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - 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); - } - - public void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { + private void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { int len = builderFields.size(); java.util.List existing = new ArrayList(); for (JavacNode child : builderType.down()) { - if (child.getKind() == Kind.FIELD) existing.add(child); + if (child.getKind() == Kind.FIELD) { + existing.add(child); + } } - + for (int i = len - 1; i >= 0; i--) { BuilderFieldData bfd = builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { @@ -529,8 +602,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { Name n = ((JCVariableDecl) exists.get()).name; - if (n.equals(bfd.name)) field = exists; - if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; + if (n.equals(bfd.name)) { + field = exists; + } + if (n.equals(bfd.nameOfSetFlag)) { + setFlag = exists; + } } JavacTreeMaker maker = builderType.getTreeMaker(); if (field == null) { @@ -547,8 +624,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } } - - public void makeSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { + + private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); final JavacTreeMaker maker = builderType.getTreeMaker(); // TODO: Make these lambdas when switching to a source level >= 1.8. @@ -559,113 +636,40 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get()); + generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get()); } else { fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); } } - - private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { + + private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; - + for (JavacNode child : builderType.down()) { - if (child.getKind() != Kind.METHOD) continue; + if (child.getKind() != Kind.METHOD) { + continue; + } JCMethodDecl methodDecl = (JCMethodDecl) child.get(); Name existingName = methodDecl.name; - if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return; + if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) { + return; + } } - + String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); - + JavacTreeMaker maker = fieldNode.getTreeMaker(); - + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); - + injectMethod(builderType, newMethod); } - - public JavacNode findInnerClass(JavacNode parent, String name) { - for (JavacNode child : parent.down()) { - if (child.getKind() != Kind.TYPE) continue; - JCClassDecl td = (JCClassDecl) child.get(); - if (td.name.contentEquals(name)) return child; - } - return null; - } - - public JavacNode makeBuilderAbstractClass(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)); - // Add builder-specific type params required for inheritable builders. - // 1. The return type for the build() method, named "C", which extends the annotated class. - JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); - if (typeParams.nonEmpty()) { - // Add type params of the annotated class. - annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); - } - allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.of(annotatedClass))); - // 2. The return type for all setter methods, named "B", which extends this builder class. - Name builderClassName = tdParent.toName(builderClass); - ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); - typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); - 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. - // 1. Add the type parameters of the superclass. - typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker); - // 2. Add the builder type params . - typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); - 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); - } - - public JavacNode makeBuilderImplClass(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. - ListBuffer allTypeParams = new ListBuffer(); - allTypeParams.addAll(copyTypeParams(source, typeParams)); - // Add builder-specific type params required for inheritable builders. - // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. - JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); - if (typeParams.nonEmpty()) { - // Add type params of the annotated class. - annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList()); - } - // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass)); - if (typeParams.nonEmpty()) { - builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList()); - } - ListBuffer typeParamsForBuilder = getTypeParamExpressions(typeParams, maker); - 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); - } - private void addObtainVia(BuilderFieldData bfd, JavacNode node) { for (JavacNode 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; @@ -673,16 +677,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return; } } - + /** * Returns the explicitly requested singular annotation on this node (field * or parameter), or null if there's no {@code @Singular} annotation on it. - * + * * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. */ private SingularData getSingularData(JavacNode node) { for (JavacNode child : node.down()) { - if (!annotationTypeMatches(Singular.class, child)) continue; + if (!annotationTypeMatches(Singular.class, child)) { + continue; + } Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); deleteAnnotationIfNeccessary(child, Singular.class); @@ -700,31 +706,67 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } Name singularName = node.toName(explicitSingular); - + JCExpression type = null; if (node.get() instanceof JCVariableDecl) { type = ((JCVariableDecl) node.get()).vartype; } - + String name = null; List typeArgs = List.nil(); if (type instanceof JCTypeApply) { typeArgs = ((JCTypeApply) type).arguments; type = ((JCTypeApply) type).clazz; } - + name = type.toString(); - + String targetFqn = JavacSingularsRecipes.get().toQualified(name); JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn); if (singularizer == null) { node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated."); return null; } - + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer); } - + return null; } + + private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { + if (!typeParamStrings.contains(classGenericName)) { + return classGenericName; + } + int counter = 2; + while (typeParamStrings.contains(classGenericName + counter)) { + counter++; + } + return classGenericName + counter; + } + + private JavacNode findInnerClass(JavacNode parent, String name) { + for (JavacNode child : parent.down()) { + if (child.getKind() != Kind.TYPE) { + continue; + } + JCClassDecl td = (JCClassDecl) child.get(); + if (td.name.contentEquals(name)) { + return child; + } + } + return null; + } + + private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { + ListBuffer typeParamsForBuilderParameter = new ListBuffer(); + for (JCTree typeParam : typeParams) { + if (typeParam instanceof JCTypeParameter) { + typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); + } else if (typeParam instanceof JCIdent) { + typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); + } + } + return typeParamsForBuilderParameter; + } } diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java index acad9c03..47f0ff3f 100644 --- a/test/transform/resource/after-delombok/SuperBuilderBasic.java +++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java @@ -5,22 +5,6 @@ public class SuperBuilderBasic { int field1; List items; @java.lang.SuppressWarnings("all") - protected Parent(final ParentBuilder b) { - this.field1 = b.field1; - java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); - } - this.items = items; - } - @java.lang.SuppressWarnings("all") public static abstract class ParentBuilder> { @java.lang.SuppressWarnings("all") private int field1; @@ -75,6 +59,22 @@ public class SuperBuilderBasic { } } @java.lang.SuppressWarnings("all") + protected Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + @java.lang.SuppressWarnings("all") public static ParentBuilder builder() { return new ParentBuilderImpl(); } @@ -83,11 +83,6 @@ public class SuperBuilderBasic { public static class Child extends Parent { double field3; @java.lang.SuppressWarnings("all") - protected Child(final ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - @java.lang.SuppressWarnings("all") public static abstract class ChildBuilder> extends Parent.ParentBuilder { @java.lang.SuppressWarnings("all") private double field3; @@ -125,6 +120,11 @@ public class SuperBuilderBasic { } } @java.lang.SuppressWarnings("all") + protected Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + @java.lang.SuppressWarnings("all") public static ChildBuilder builder() { return new ChildBuilderImpl(); } diff --git a/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java b/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java index e7615c66..34a61f09 100644 --- a/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java +++ b/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java @@ -5,22 +5,6 @@ public class SuperBuilderWithGenerics { A field1; List items; @java.lang.SuppressWarnings("all") - protected Parent(final ParentBuilder b) { - this.field1 = b.field1; - java.util.List items; - switch (b.items == null ? 0 : b.items.size()) { - case 0: - items = java.util.Collections.emptyList(); - break; - case 1: - items = java.util.Collections.singletonList(b.items.get(0)); - break; - default: - items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); - } - this.items = items; - } - @java.lang.SuppressWarnings("all") public static abstract class ParentBuilder, B extends ParentBuilder> { @java.lang.SuppressWarnings("all") private A field1; @@ -75,6 +59,22 @@ public class SuperBuilderWithGenerics { } } @java.lang.SuppressWarnings("all") + protected Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + @java.lang.SuppressWarnings("all") public static ParentBuilder builder() { return new ParentBuilderImpl(); } @@ -83,11 +83,6 @@ public class SuperBuilderWithGenerics { public static class Child extends Parent { double field3; @java.lang.SuppressWarnings("all") - protected Child(final ChildBuilder b) { - super(b); - this.field3 = b.field3; - } - @java.lang.SuppressWarnings("all") public static abstract class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { @java.lang.SuppressWarnings("all") private double field3; @@ -125,6 +120,11 @@ public class SuperBuilderWithGenerics { } } @java.lang.SuppressWarnings("all") + protected Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + @java.lang.SuppressWarnings("all") public static ChildBuilder builder() { return new ChildBuilderImpl(); } -- cgit From 36c75b2d68551df3f297e8dcf10a8909307c854a Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Wed, 30 May 2018 22:49:34 +0200 Subject: no custom builders allowed, so there cannot be a toString() method --- .../lombok/javac/handlers/HandleSuperBuilder.java | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 0bb16d56..116de47b 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -262,18 +262,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // Create the toString() method for the abstract builder. - if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { - java.util.List fieldNodes = new ArrayList(); - 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 md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast); - if (md != null) { - injectMethod(builderType, md); - } + java.util.List fieldNodes = new ArrayList(); + 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 clean methods are requested, add them now. if (addCleaning) { injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); } @@ -304,10 +303,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // 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 md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - if (md != null) { - injectMethod(tdParent, md); + JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); + if (builderMethod != null) { + injectMethod(tdParent, builderMethod); } } -- cgit From 664ed48460eba30b9cab7f9298ac563c4a23c993 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 31 May 2018 14:53:20 +0200 Subject: javac: delete @SuperBuilder when processing it --- src/core/lombok/javac/handlers/HandleSuperBuilder.java | 1 + test/transform/resource/after-delombok/SuperBuilderBasic.java | 2 -- .../resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java | 2 -- test/transform/resource/after-delombok/SuperBuilderWithGenerics.java | 2 -- 4 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 116de47b..1055a11a 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -95,6 +95,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { SuperBuilder superbuilderAnnotation = annotation.getInstance(); + deleteAnnotationIfNeccessary(annotationNode, SuperBuilder.class); String builderMethodName = superbuilderAnnotation.builderMethodName(); String buildMethodName = superbuilderAnnotation.buildMethodName(); diff --git a/test/transform/resource/after-delombok/SuperBuilderBasic.java b/test/transform/resource/after-delombok/SuperBuilderBasic.java index 47f0ff3f..18a98af3 100644 --- a/test/transform/resource/after-delombok/SuperBuilderBasic.java +++ b/test/transform/resource/after-delombok/SuperBuilderBasic.java @@ -1,6 +1,5 @@ import java.util.List; public class SuperBuilderBasic { - @lombok.experimental.SuperBuilder public static class Parent { int field1; List items; @@ -79,7 +78,6 @@ public class SuperBuilderBasic { return new ParentBuilderImpl(); } } - @lombok.experimental.SuperBuilder public static class Child extends Parent { double field3; @java.lang.SuppressWarnings("all") diff --git a/test/transform/resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java b/test/transform/resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java index 4d451558..21a9db07 100644 --- a/test/transform/resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java +++ b/test/transform/resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java @@ -1,6 +1,5 @@ import java.util.List; public class SuperBuilderWithCustomBuilderMethod { - @lombok.experimental.SuperBuilder public static class Parent { A field1; List items; @@ -79,7 +78,6 @@ public class SuperBuilderWithCustomBuilderMethod { return new ParentBuilderImpl(); } } - @lombok.experimental.SuperBuilder public static class Child extends Parent { double field3; public static ChildBuilder builder() { diff --git a/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java b/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java index 34a61f09..c24b473e 100644 --- a/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java +++ b/test/transform/resource/after-delombok/SuperBuilderWithGenerics.java @@ -1,6 +1,5 @@ import java.util.List; public class SuperBuilderWithGenerics { - @lombok.experimental.SuperBuilder public static class Parent { A field1; List items; @@ -79,7 +78,6 @@ public class SuperBuilderWithGenerics { return new ParentBuilderImpl(); } } - @lombok.experimental.SuperBuilder public static class Child extends Parent { double field3; @java.lang.SuppressWarnings("all") -- cgit From 4735f99d2856068d18292532eace281b3efe809b Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 31 May 2018 15:02:08 +0200 Subject: ecj: reordering of methods --- .../eclipse/handlers/HandleSuperBuilder.java | 347 +++++++++++---------- 1 file changed, 174 insertions(+), 173 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index a473a084..d452261a 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -97,6 +97,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { private static final char[] SET_PREFIX = {'$', 's', 'e', 't'}; private static final String SELF_METHOD = "self"; + private static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; + private static class BuilderFieldData { TypeReference type; char[] rawName; @@ -264,7 +266,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // Create the abstract builder class. EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, + builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, typeParams, superclassTypeParams, ast, classGenericName, builderGenericName); } else { @@ -308,7 +310,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); + generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); } // Create the toString() method for the abstract builder. @@ -331,7 +333,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { // Create the builder implementation class. EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { - builderImplType = makeBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); + builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; @@ -356,31 +358,64 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } - private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { - if (!typeParamStrings.contains(classGenericName)) { - return classGenericName; - } - int counter = 2; - while (typeParamStrings.contains(classGenericName + counter)) { - counter++; - } - return classGenericName + counter; + private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass, + TypeReference superclassBuilderClass, TypeParameter[] typeParams, + TypeParameter[] superclassTypeParams, 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. + // 1. The return type for the build() method, named "C", which extends the annotated class. + TypeParameter o = new TypeParameter(); + o.name = classGenericName.toCharArray(); + o.type = cloneSelfType(tdParent, source); + builder.typeParameters[builder.typeParameters.length - 2] = o; + // 2. The return type for all setter methods, named "B", which extends this builder class. + o = new TypeParameter(); + o.name = builderGenericName.toCharArray(); + 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.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return injectType(tdParent, builder); } - private TypeReference[] getTypeParametersFrom(TypeReference typeRef) { - TypeReference[][] typeArgss = null; - if (typeRef instanceof ParameterizedQualifiedTypeReference) { - typeArgss = ((ParameterizedQualifiedTypeReference)typeRef).typeArguments; - } else if (typeRef instanceof ParameterizedSingleTypeReference) { - typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference)typeRef).typeArguments}; + private EclipseNode generateBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { + TypeDeclaration parent = (TypeDeclaration) tdParent.get(); + TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); + builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; + builder.name = builderImplClass.toCharArray(); + // Add type params if there are any. + if (typeParams != null && typeParams.length > 0) { + builder.typeParameters = copyTypeParams(typeParams, source); } - TypeReference[] typeArgs = new TypeReference[0]; - if (typeArgss != null && typeArgss.length > 0) { - typeArgs = typeArgss[typeArgss.length - 1]; + if (builderAbstractClass != null) { + // Extend the abstract builder. + // 1. Add any type params of the annotated class. + TypeReference[] typeArgs = new TypeReference[typeParams.length + 2]; + for (int i = 0; i < typeParams.length; i++) { + typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. + // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. + typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source); + typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams); + builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); } - return typeArgs; + 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. @@ -463,6 +498,30 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { 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); + } + 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.toCharArray(); @@ -502,40 +561,18 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { 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)); - MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); - decl.selector = CLEAN_METHOD_NAME; - decl.modifiers = ClassFileConstants.AccPrivate; - decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); - decl.statements = statements.toArray(new Statement[0]); - decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return decl; - } - 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. @@ -546,28 +583,26 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return out; } - 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; + private MethodDeclaration generateCleanMethod(List builderFields, EclipseNode builderType, ASTNode source) { + List statements = new ArrayList(); - // Add type params if there are any. - if (typeParams != null && typeParams.length > 0) { - out.typeParameters = copyTypeParams(typeParams, source); + for (BuilderFieldData bfd : builderFields) { + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements); + } } - 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; + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); + thisUnclean.receiver = new ThisReference(0, 0); + statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0)); + MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + decl.selector = CLEAN_METHOD_NAME; + decl.modifiers = ClassFileConstants.AccPrivate; + decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); + decl.statements = statements.toArray(new Statement[0]); + decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + return decl; } private void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) { @@ -614,7 +649,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } - private void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) { + 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. @@ -632,18 +667,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } }; if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnType.get(), returnStatement.get(), sourceNode); + generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnType.get(), returnStatement.get(), sourceNode); } else { bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnType, returnStatement); } } - private static final AbstractMethodDeclaration[] EMPTY = {}; - private void makeSimpleSetterMethodForBuilder(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, ReturnStatement returnStatement, EclipseNode sourceNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) { - existing = EMPTY; + existing = EMPTY_METHODS; } int len = existing.length; FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); @@ -666,107 +700,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { injectMethod(builderType, setter); } - private EclipseNode findInnerClass(EclipseNode parent, String name) { - char[] c = name.toCharArray(); - for (EclipseNode child : parent.down()) { - if (child.getKind() != Kind.TYPE) { - continue; - } - TypeDeclaration td = (TypeDeclaration) child.get(); - if (Arrays.equals(td.name, c)) { - return child; - } - } - return null; - } - - 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[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) { - TypeReference[] typerefs = new TypeReference[typeParams.length + typeReferencesToAppend.length]; - for (int i = 0; i < typeParams.length; i++) { - typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); - } - for (int i = 0; i < typeReferencesToAppend.length; i++) { - typerefs[typeParams.length + i] = typeReferencesToAppend[i]; - } - 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]; - } - return result; - } - - private EclipseNode makeBuilderAbstractClass(EclipseNode tdParent, String builderClass, - TypeReference superclassBuilderClass, TypeParameter[] typeParams, - TypeParameter[] superclassTypeParams, 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. - // 1. The return type for the build() method, named "C", which extends the annotated class. - TypeParameter o = new TypeParameter(); - o.name = classGenericName.toCharArray(); - o.type = cloneSelfType(tdParent, source); - builder.typeParameters[builder.typeParameters.length - 2] = o; - // 2. The return type for all setter methods, named "B", which extends this builder class. - o = new TypeParameter(); - o.name = builderGenericName.toCharArray(); - 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.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return injectType(tdParent, builder); - } - - private EclipseNode makeBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { - TypeDeclaration parent = (TypeDeclaration) tdParent.get(); - TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); - builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; - builder.name = builderImplClass.toCharArray(); - // Add type params if there are any. - 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. - TypeReference[] typeArgs = new TypeReference[typeParams.length + 2]; - for (int i = 0; i < typeParams.length; i++) { - typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0); - } - // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. - // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source); - typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams); - builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); - } - builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return injectType(tdParent, builder); - } - private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { for (EclipseNode child : node.down()) { if (!annotationTypeMatches(ObtainVia.class, child)) { @@ -844,6 +777,38 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } + private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { + if (!typeParamStrings.contains(classGenericName)) { + return classGenericName; + } + int counter = 2; + 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; + } else if (typeRef instanceof ParameterizedSingleTypeReference) { + typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference)typeRef).typeArguments}; + } + TypeReference[] typeArgs = new TypeReference[0]; + if (typeArgss != null && typeArgss.length > 0) { + typeArgs = typeArgss[typeArgss.length - 1]; + } + return typeArgs; + } + private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) { if (typeParams.length > 0) { TypeReference[] typerefs = new TypeReference[typeParams.length]; @@ -857,6 +822,42 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } + private TypeReference[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) { + TypeReference[] typerefs = new TypeReference[typeParams.length + typeReferencesToAppend.length]; + for (int i = 0; i < typeParams.length; i++) { + typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + } + for (int i = 0; i < typeReferencesToAppend.length; i++) { + typerefs[typeParams.length + i] = typeReferencesToAppend[i]; + } + 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]; + } + return result; + } + + private EclipseNode findInnerClass(EclipseNode parent, String name) { + char[] c = name.toCharArray(); + for (EclipseNode child : parent.down()) { + if (child.getKind() != Kind.TYPE) { + continue; + } + TypeDeclaration td = (TypeDeclaration) child.get(); + 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); -- cgit From 7bd60ea6e68699b9b75f6fb4ce8c68abfa69f5de Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 31 May 2018 16:32:14 +0200 Subject: second generics test case --- .../eclipse/handlers/HandleSuperBuilder.java | 5 +- .../after-delombok/SuperBuilderWithGenerics2.java | 133 +++++++++++++++++++++ .../after-ecj/SuperBuilderWithGenerics2.java | 103 ++++++++++++++++ .../resource/before/SuperBuilderWithGenerics2.java | 18 +++ 4 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 test/transform/resource/after-delombok/SuperBuilderWithGenerics2.java create mode 100644 test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java create mode 100644 test/transform/resource/before/SuperBuilderWithGenerics2.java (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index d452261a..d4441831 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -141,7 +141,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { java.util.List builderFields = new ArrayList(); TypeReference returnType; TypeParameter[] typeParams; - TypeParameter[] superclassTypeParams = new TypeParameter[] {}; //TODO boolean addCleaning = false; @@ -267,7 +266,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, - typeParams, superclassTypeParams, ast, classGenericName, builderGenericName); + typeParams, ast, classGenericName, builderGenericName); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); @@ -360,7 +359,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, - TypeParameter[] superclassTypeParams, ASTNode source, String classGenericName, String builderGenericName) { + ASTNode source, String classGenericName, String builderGenericName) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); diff --git a/test/transform/resource/after-delombok/SuperBuilderWithGenerics2.java b/test/transform/resource/after-delombok/SuperBuilderWithGenerics2.java new file mode 100644 index 00000000..811fd7e8 --- /dev/null +++ b/test/transform/resource/after-delombok/SuperBuilderWithGenerics2.java @@ -0,0 +1,133 @@ +import java.util.List; +public class SuperBuilderWithGenerics2 { + public static class Parent { + A field1; + List items; + @java.lang.SuppressWarnings("all") + public static abstract class ParentBuilder, B extends ParentBuilder> { + @java.lang.SuppressWarnings("all") + private A field1; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList items; + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @java.lang.SuppressWarnings("all") + public abstract C build(); + @java.lang.SuppressWarnings("all") + public B field1(final A field1) { + this.field1 = field1; + return self(); + } + @java.lang.SuppressWarnings("all") + public B item(final String item) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.add(item); + return self(); + } + @java.lang.SuppressWarnings("all") + public B items(final java.util.Collection items) { + if (this.items == null) this.items = new java.util.ArrayList(); + this.items.addAll(items); + return self(); + } + @java.lang.SuppressWarnings("all") + public B clearItems() { + if (this.items != null) this.items.clear(); + return self(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderWithGenerics2.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")"; + } + } + @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) { + this.field1 = b.field1; + java.util.List items; + switch (b.items == null ? 0 : b.items.size()) { + case 0: + items = java.util.Collections.emptyList(); + break; + case 1: + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default: + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + @java.lang.SuppressWarnings("all") + public static ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static class Child extends Parent { + A field3; + @java.lang.SuppressWarnings("all") + public static abstract class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { + @java.lang.SuppressWarnings("all") + private A field3; + @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 field3(final A field3) { + this.field3 = field3; + return self(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderWithGenerics2.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")"; + } + } + @java.lang.SuppressWarnings("all") + private static final class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { + @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); + this.field3 = b.field3; + } + @java.lang.SuppressWarnings("all") + public static ChildBuilder builder2() { + return new ChildBuilderImpl(); + } + } + public static void test() { + Child x = Child.builder2().field3(1).field1("value").item("").build(); + } +} diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java b/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java new file mode 100644 index 00000000..74cf376e --- /dev/null +++ b/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java @@ -0,0 +1,103 @@ +import java.util.List; +public class SuperBuilderWithGenerics2 { + public static @lombok.experimental.SuperBuilder class Parent { + public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> { + private @java.lang.SuppressWarnings("all") A field1; + private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + protected abstract @java.lang.SuppressWarnings("all") B self(); + public abstract @java.lang.SuppressWarnings("all") C build(); + public @java.lang.SuppressWarnings("all") B field1(final A field1) { + this.field1 = field1; + return self(); + } + public @java.lang.SuppressWarnings("all") B item(String item) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); + this.items.add(item); + return self(); + } + public @java.lang.SuppressWarnings("all") B items(java.util.Collection items) { + if ((this.items == null)) + this.items = new java.util.ArrayList(); + this.items.addAll(items); + return self(); + } + public @java.lang.SuppressWarnings("all") B clearItems() { + if ((this.items != null)) + this.items.clear(); + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderWithGenerics2.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> { + private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + } + 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); + } + } + A field1; + @lombok.Singular List items; + protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + this.field1 = b.field1; + java.util.List items; + switch (((b.items == null) ? 0 : b.items.size())) { + case 0 : + items = java.util.Collections.emptyList(); + break; + case 1 : + items = java.util.Collections.singletonList(b.items.get(0)); + break; + default : + items = java.util.Collections.unmodifiableList(new java.util.ArrayList(b.items)); + } + this.items = items; + } + public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static @lombok.experimental.SuperBuilder(builderMethodName = "builder2") class Child extends Parent { + public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { + private @java.lang.SuppressWarnings("all") A field3; + 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 field3(final A field3) { + this.field3 = field3; + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderWithGenerics2.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { + private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + } + 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); + } + } + A field3; + protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + this.field3 = b.field3; + } + public static @java.lang.SuppressWarnings("all") ChildBuilder builder2() { + return new ChildBuilderImpl(); + } + } + public SuperBuilderWithGenerics2() { + super(); + } + public static void test() { + Child x = Child.builder2().field3(1).field1("value").item("").build(); + } +} diff --git a/test/transform/resource/before/SuperBuilderWithGenerics2.java b/test/transform/resource/before/SuperBuilderWithGenerics2.java new file mode 100644 index 00000000..f84ff1f5 --- /dev/null +++ b/test/transform/resource/before/SuperBuilderWithGenerics2.java @@ -0,0 +1,18 @@ +import java.util.List; + +public class SuperBuilderWithGenerics2 { + @lombok.experimental.SuperBuilder + public static class Parent { + A field1; + @lombok.Singular List items; + } + + @lombok.experimental.SuperBuilder(builderMethodName="builder2") + public static class Child extends Parent { + A field3; + } + + public static void test() { + Child x = Child.builder2().field3(1).field1("value").item("").build(); + } +} -- cgit From 795e3c84875de0a01f4d5e1321312f592139c7aa Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 31 May 2018 16:40:08 +0200 Subject: Revert "ecj: SuperBuilderBasic test passes (removed superconstructor calls)" This reverts commit 9ee2491427f1522a7689d1c28a816294bcff315b. --- src/core/lombok/eclipse/handlers/HandleConstructor.java | 12 +++++------- src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 13 +++++++------ test/transform/resource/after-ecj/SuperBuilderBasic.java | 3 +++ 3 files changed, 15 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index ccf71158..a2940b88 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -259,7 +259,7 @@ public class HandleConstructor { ConstructorDeclaration constr = createConstructor( staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, - true, sourceNode, onConstructor); + sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.emptyList() : fields, source); @@ -301,7 +301,7 @@ public class HandleConstructor { @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection fields, boolean allToDefault, - boolean superConstructorCall, EclipseNode sourceNode, List onConstructor) { + EclipseNode sourceNode, List onConstructor) { ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); @@ -324,11 +324,9 @@ public class HandleConstructor { constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; - if (superConstructorCall) { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; - } + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index d4441831..3b7e95ca 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -63,7 +63,6 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; -import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; @@ -339,7 +338,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } // Create a simple constructor for the BuilderImpl class. - ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, false, annotationNode, Collections.emptyList()); + ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, annotationNode, Collections.emptyList()); if (cd != null) { injectMethod(builderImplType, cd); } @@ -445,9 +444,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; + } else { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); } + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -525,7 +526,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; + out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected; if (override) { out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; } @@ -549,7 +550,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { 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.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; out.selector = methodName.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index fc5f045b..88ac7e70 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -33,6 +33,7 @@ public class SuperBuilderBasic { } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -44,6 +45,7 @@ public class SuperBuilderBasic { int field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); this.field1 = b.field1; java.util.List items; switch (((b.items == null) ? 0 : b.items.size())) { @@ -77,6 +79,7 @@ public class SuperBuilderBasic { } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder { private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; -- cgit From 32bed51fede1eebef2006d54b3e364de22ae9ac0 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Thu, 31 May 2018 18:01:43 +0200 Subject: ecj: generate default constructor for builder classes --- src/core/lombok/eclipse/handlers/HandleBuilder.java | 2 +- .../lombok/eclipse/handlers/HandleSuperBuilder.java | 17 +++++++++-------- .../transform/resource/after-ecj/SuperBuilderBasic.java | 10 ++++++++-- .../after-ecj/SuperBuilderWithCustomBuilderMethod.java | 13 +++++++++++-- .../resource/after-ecj/SuperBuilderWithGenerics.java | 13 +++++++++++-- .../resource/after-ecj/SuperBuilderWithGenerics2.java | 13 +++++++++++-- 6 files changed, 51 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 331fbde4..fe19decd 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -434,7 +434,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( - AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, true, + AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, annotationNode, Collections.emptyList()); if (cd != null) injectMethod(builderType, cd); } diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3b7e95ca..04c3b2fe 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -63,6 +63,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; @@ -337,12 +338,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return; } - // Create a simple constructor for the BuilderImpl class. - ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, builderImplType, Collections.emptyList(), false, annotationNode, Collections.emptyList()); - if (cd != null) { - injectMethod(builderImplType, cd); - } - // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType)); injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); @@ -383,6 +378,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.superclass = copyType(superclassBuilderClass, source); + builder.createDefaultConstructor(false, true); + builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } @@ -393,6 +390,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; builder.name = builderImplClass.toCharArray(); + // Add type params if there are any. if (typeParams != null && typeParams.length > 0) { builder.typeParameters = copyTypeParams(typeParams, source); @@ -410,6 +408,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams); builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0); } + + builder.createDefaultConstructor(false, true); + builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } @@ -526,7 +527,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected; + out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; if (override) { out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; } @@ -550,7 +551,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; + 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); diff --git a/test/transform/resource/after-ecj/SuperBuilderBasic.java b/test/transform/resource/after-ecj/SuperBuilderBasic.java index 88ac7e70..11fd8498 100644 --- a/test/transform/resource/after-ecj/SuperBuilderBasic.java +++ b/test/transform/resource/after-ecj/SuperBuilderBasic.java @@ -4,6 +4,9 @@ public class SuperBuilderBasic { public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder> { private @java.lang.SuppressWarnings("all") int field1; private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + 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 field1(final int field1) { @@ -32,7 +35,7 @@ public class SuperBuilderBasic { } } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { - private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + private ParentBuilderImpl() { super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { @@ -67,6 +70,9 @@ public class SuperBuilderBasic { 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 field3; + 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 field3(final double field3) { @@ -78,7 +84,7 @@ public class SuperBuilderBasic { } } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder { - private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + private ChildBuilderImpl() { super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { diff --git a/test/transform/resource/after-ecj/SuperBuilderWithCustomBuilderMethod.java b/test/transform/resource/after-ecj/SuperBuilderWithCustomBuilderMethod.java index 119fb5bb..fe4938bf 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithCustomBuilderMethod.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithCustomBuilderMethod.java @@ -4,6 +4,9 @@ public class SuperBuilderWithCustomBuilderMethod { public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> { private @java.lang.SuppressWarnings("all") A field1; private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + 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 field1(final A field1) { @@ -32,7 +35,8 @@ public class SuperBuilderWithCustomBuilderMethod { } } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> { - private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + private ParentBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -44,6 +48,7 @@ public class SuperBuilderWithCustomBuilderMethod { A field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); this.field1 = b.field1; java.util.List items; switch (((b.items == null) ? 0 : b.items.size())) { @@ -65,6 +70,9 @@ public class SuperBuilderWithCustomBuilderMethod { public static @lombok.experimental.SuperBuilder class Child extends Parent { public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { private @java.lang.SuppressWarnings("all") double field3; + 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 field3(final double field3) { @@ -76,7 +84,8 @@ public class SuperBuilderWithCustomBuilderMethod { } } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { - private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + private ChildBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java index cb717bcf..b82f25cb 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithGenerics.java @@ -4,6 +4,9 @@ public class SuperBuilderWithGenerics { public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> { private @java.lang.SuppressWarnings("all") A field1; private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + 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 field1(final A field1) { @@ -32,7 +35,8 @@ public class SuperBuilderWithGenerics { } } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> { - private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + private ParentBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -44,6 +48,7 @@ public class SuperBuilderWithGenerics { A field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); this.field1 = b.field1; java.util.List items; switch (((b.items == null) ? 0 : b.items.size())) { @@ -65,6 +70,9 @@ public class SuperBuilderWithGenerics { public static @lombok.experimental.SuperBuilder class Child extends Parent { public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { private @java.lang.SuppressWarnings("all") double field3; + 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 field3(final double field3) { @@ -76,7 +84,8 @@ public class SuperBuilderWithGenerics { } } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { - private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + private ChildBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java b/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java index 74cf376e..7db95ea0 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java @@ -4,6 +4,9 @@ public class SuperBuilderWithGenerics2 { public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder, B extends ParentBuilder> { private @java.lang.SuppressWarnings("all") A field1; private @java.lang.SuppressWarnings("all") java.util.ArrayList items; + 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 field1(final A field1) { @@ -32,7 +35,8 @@ public class SuperBuilderWithGenerics2 { } } private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder, ParentBuilderImpl> { - private @java.lang.SuppressWarnings("all") ParentBuilderImpl() { + private ParentBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() { return this; @@ -44,6 +48,7 @@ public class SuperBuilderWithGenerics2 { A field1; @lombok.Singular List items; protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); this.field1 = b.field1; java.util.List items; switch (((b.items == null) ? 0 : b.items.size())) { @@ -65,6 +70,9 @@ public class SuperBuilderWithGenerics2 { public static @lombok.experimental.SuperBuilder(builderMethodName = "builder2") class Child extends Parent { public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder, B extends ChildBuilder> extends Parent.ParentBuilder { private @java.lang.SuppressWarnings("all") A field3; + 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 field3(final A field3) { @@ -76,7 +84,8 @@ public class SuperBuilderWithGenerics2 { } } private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder, ChildBuilderImpl> { - private @java.lang.SuppressWarnings("all") ChildBuilderImpl() { + private ChildBuilderImpl() { + super(); } protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() { return this; -- cgit From 51c0c40ac422ddd2828bb0ec79e252e85f1511c9 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 1 Jun 2018 10:50:26 +0200 Subject: handle experimental flag usage --- src/core/lombok/ConfigurationKeys.java | 9 +++++++++ src/core/lombok/eclipse/handlers/HandleSuperBuilder.java | 2 ++ src/core/lombok/javac/handlers/HandleSuperBuilder.java | 2 ++ 3 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 29c43d3f..ade86e27 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -508,6 +508,15 @@ public class ConfigurationKeys { */ public static final ConfigurationKey WITHER_FLAG_USAGE = new ConfigurationKey("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {}; + // ----- SuperBuilder ----- + + /** + * lombok configuration: {@code lombok.superBuilder.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, any usage of {@code @SuperBuilder} results in a warning / error. + */ + public static final ConfigurationKey SUPERBUILDER_FLAG_USAGE = new ConfigurationKey("lombok.superBuilder.flagUsage", "Emit a warning or error if @SuperBuilder is used.") {}; + // ----- Configuration System ----- /** diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 04c3b2fe..1b17e3df 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -115,6 +115,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { @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(); diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 1055a11a..c48e4761 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -94,6 +94,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { @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); -- 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 'src') 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 ebbe169f1a4e2747ca38763cb5a00929d1d70f23 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Fri, 1 Jun 2018 16:08:16 +0200 Subject: reverted unnecessary import reordering --- src/core/lombok/javac/handlers/HandleSetter.java | 27 +++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 77919b8e..50967121 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -21,14 +21,23 @@ */ package lombok.javac.handlers; -import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import static lombok.javac.handlers.JavacHandlerUtil.toAllSetterNames; -import static lombok.javac.handlers.JavacHandlerUtil.toSetterName; import java.util.Collection; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.Setter; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; + import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; @@ -48,18 +57,6 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; -import lombok.AccessLevel; -import lombok.ConfigurationKeys; -import lombok.Setter; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.javac.Javac; -import lombok.javac.JavacAnnotationHandler; -import lombok.javac.JavacNode; -import lombok.javac.JavacTreeMaker; -import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; -import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; - /** * Handles the {@code lombok.Setter} annotation for javac. */ -- cgit From e2fc6fa56433e5a62939bc2a3d92a4372539f3db Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 4 Jun 2018 17:52:38 +0200 Subject: SuperBuilder support for abstract classes --- .../eclipse/handlers/HandleSuperBuilder.java | 5 + .../lombok/javac/handlers/HandleSuperBuilder.java | 61 +++++----- .../after-delombok/SuperBuilderAbstract.java | 129 +++++++++++++++++++++ .../resource/after-ecj/SuperBuilderAbstract.java | 102 ++++++++++++++++ .../resource/before/SuperBuilderAbstract.java | 20 ++++ 5 files changed, 291 insertions(+), 26 deletions(-) create mode 100644 test/transform/resource/after-delombok/SuperBuilderAbstract.java create mode 100644 test/transform/resource/after-ecj/SuperBuilderAbstract.java create mode 100644 test/transform/resource/before/SuperBuilderAbstract.java (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3a92da4a..1100cbf0 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -333,6 +333,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); } + if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) { + // Abstract classes to not get the Builder implementation nor the builder() method. + return; + } + // Create the builder implementation class. EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index beee47a9..1835bd48 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -282,42 +282,51 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (addCleaning) { injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); } + + recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); - // Create the builder implementation class. - JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); - if (builderImplType == null) { - builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); - } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; - } + if ((td.mods.flags & Flags.ABSTRACT) == 0) { + // Only non-abstract classes get the Builder implementation. - // 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); - } + // Create the builder implementation class. + JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); + if (builderImplType == null) { + builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + } else { + 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); + } - // Create the self() and build() methods in the BuilderImpl. - injectMethod(builderImplType, generateSelfMethod(builderImplType)); - injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions)); + // 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); - // 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 ((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); + } } } - - recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); - recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } /** diff --git a/test/transform/resource/after-delombok/SuperBuilderAbstract.java b/test/transform/resource/after-delombok/SuperBuilderAbstract.java new file mode 100644 index 00000000..d1e82c27 --- /dev/null +++ b/test/transform/resource/after-delombok/SuperBuilderAbstract.java @@ -0,0 +1,129 @@ +public class SuperBuilderAbstract { + public static class Parent { + int parentField; + @java.lang.SuppressWarnings("all") + public static abstract class ParentBuilder> { + @java.lang.SuppressWarnings("all") + private int parentField; + @java.lang.SuppressWarnings("all") + protected abstract B self(); + @java.lang.SuppressWarnings("all") + public abstract C build(); + @java.lang.SuppressWarnings("all") + public B parentField(final int parentField) { + this.parentField = parentField; + return self(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderAbstract.Parent.ParentBuilder(parentField=" + this.parentField + ")"; + } + } + @java.lang.SuppressWarnings("all") + private static final class ParentBuilderImpl extends ParentBuilder { + @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) { + this.parentField = b.parentField; + } + @java.lang.SuppressWarnings("all") + public static ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static abstract class Child extends Parent { + double childField; + @java.lang.SuppressWarnings("all") + public static abstract class ChildBuilder> extends Parent.ParentBuilder { + @java.lang.SuppressWarnings("all") + private double childField; + @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 childField(final double childField) { + this.childField = childField; + return self(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderAbstract.Child.ChildBuilder(super=" + super.toString() + ", childField=" + this.childField + ")"; + } + } + @java.lang.SuppressWarnings("all") + protected Child(final ChildBuilder b) { + super(b); + this.childField = b.childField; + } + } + public static class GrandChild extends Child { + String grandChildField; + @java.lang.SuppressWarnings("all") + public static abstract class GrandChildBuilder> extends Child.ChildBuilder { + @java.lang.SuppressWarnings("all") + private String grandChildField; + @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 grandChildField(final String grandChildField) { + this.grandChildField = grandChildField; + return self(); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "SuperBuilderAbstract.GrandChild.GrandChildBuilder(super=" + super.toString() + ", grandChildField=" + this.grandChildField + ")"; + } + } + @java.lang.SuppressWarnings("all") + private static final class GrandChildBuilderImpl extends GrandChildBuilder { + @java.lang.SuppressWarnings("all") + private GrandChildBuilderImpl() { + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + protected GrandChildBuilderImpl self() { + return this; + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public GrandChild build() { + return new GrandChild(this); + } + } + @java.lang.SuppressWarnings("all") + protected GrandChild(final GrandChildBuilder b) { + super(b); + this.grandChildField = b.grandChildField; + } + @java.lang.SuppressWarnings("all") + public static GrandChildBuilder builder() { + return new GrandChildBuilderImpl(); + } + } + public static void test() { + GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build(); + } +} diff --git a/test/transform/resource/after-ecj/SuperBuilderAbstract.java b/test/transform/resource/after-ecj/SuperBuilderAbstract.java new file mode 100644 index 00000000..2839babd --- /dev/null +++ b/test/transform/resource/after-ecj/SuperBuilderAbstract.java @@ -0,0 +1,102 @@ +public class SuperBuilderAbstract { + public static @lombok.experimental.SuperBuilder class Parent { + public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder> { + private @java.lang.SuppressWarnings("all") int parentField; + 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 parentField(final int parentField) { + this.parentField = parentField; + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("SuperBuilderAbstract.Parent.ParentBuilder(parentField=" + this.parentField) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder { + 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); + } + } + int parentField; + protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder b) { + super(); + this.parentField = b.parentField; + } + public static @java.lang.SuppressWarnings("all") ParentBuilder builder() { + return new ParentBuilderImpl(); + } + } + public static abstract @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 childField; + 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 childField(final double childField) { + this.childField = childField; + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderAbstract.Child.ChildBuilder(super=" + super.toString()) + ", childField=") + this.childField) + ")"); + } + } + double childField; + protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder b) { + super(b); + this.childField = b.childField; + } + } + public static @lombok.experimental.SuperBuilder class GrandChild extends Child { + public static abstract @java.lang.SuppressWarnings("all") class GrandChildBuilder> extends Child.ChildBuilder { + private @java.lang.SuppressWarnings("all") String grandChildField; + public GrandChildBuilder() { + 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 grandChildField(final String grandChildField) { + this.grandChildField = grandChildField; + return self(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((("SuperBuilderAbstract.GrandChild.GrandChildBuilder(super=" + super.toString()) + ", grandChildField=") + this.grandChildField) + ")"); + } + } + private static final @java.lang.SuppressWarnings("all") class GrandChildBuilderImpl extends GrandChildBuilder { + private GrandChildBuilderImpl() { + super(); + } + protected @java.lang.Override @java.lang.SuppressWarnings("all") GrandChildBuilderImpl self() { + return this; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") GrandChild build() { + return new GrandChild(this); + } + } + String grandChildField; + protected @java.lang.SuppressWarnings("all") GrandChild(final GrandChildBuilder b) { + super(b); + this.grandChildField = b.grandChildField; + } + public static @java.lang.SuppressWarnings("all") GrandChildBuilder builder() { + return new GrandChildBuilderImpl(); + } + } + public SuperBuilderAbstract() { + super(); + } + public static void test() { + GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build(); + } +} diff --git a/test/transform/resource/before/SuperBuilderAbstract.java b/test/transform/resource/before/SuperBuilderAbstract.java new file mode 100644 index 00000000..23f284e2 --- /dev/null +++ b/test/transform/resource/before/SuperBuilderAbstract.java @@ -0,0 +1,20 @@ +public class SuperBuilderAbstract { + @lombok.experimental.SuperBuilder + public static class Parent { + int parentField; + } + + @lombok.experimental.SuperBuilder + public abstract static class Child extends Parent { + double childField; + } + + @lombok.experimental.SuperBuilder + public static class GrandChild extends Child { + String grandChildField; + } + + public static void test() { + GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build(); + } +} -- 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 'src') 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 statements = new ListBuffer(); - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements); } } - + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); JCBlock body = maker.Block(0, statements.toList()); 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 void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { int len = builderFields.size(); java.util.List existing = new ArrayList(); for (JavacNode child : builderType.down()) { - if (child.getKind() == Kind.FIELD) { - existing.add(child); - } + if (child.getKind() == Kind.FIELD) existing.add(child); } - + for (int i = len - 1; i >= 0; i--) { BuilderFieldData bfd = builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { @@ -611,12 +584,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { Name n = ((JCVariableDecl) exists.get()).name; - if (n.equals(bfd.name)) { - field = exists; - } - if (n.equals(bfd.nameOfSetFlag)) { - setFlag = exists; - } + if (n.equals(bfd.name)) field = exists; + if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; } JavacTreeMaker maker = builderType.getTreeMaker(); if (field == null) { @@ -633,52 +602,47 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } } - + private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); final JavacTreeMaker maker = builderType.getTreeMaker(); - // TODO: Make these lambdas when switching to a source level >= 1.8. - Supplier returnType = new Supplier() { @Override public JCExpression get() { + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { return maker.Ident(builderType.toName(builderGenericName)); }}; - Supplier returnStatement = new Supplier() { @Override public JCReturn get() { + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { return maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); }}; + if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get()); + generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make()); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker); } } - - private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) { + + private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; - + for (JavacNode child : builderType.down()) { - if (child.getKind() != Kind.METHOD) { - continue; - } + if (child.getKind() != Kind.METHOD) continue; JCMethodDecl methodDecl = (JCMethodDecl) child.get(); Name existingName = methodDecl.name; - if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) { - return; - } + if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return; } - + String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); - + JavacTreeMaker maker = fieldNode.getTreeMaker(); - + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); - + injectMethod(builderType, newMethod); } - + private void addObtainVia(BuilderFieldData bfd, JavacNode node) { for (JavacNode 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; @@ -686,7 +650,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return; } } - + /** * Returns the explicitly requested singular annotation on this node (field * or parameter), or null if there's no {@code @Singular} annotation on it. @@ -695,9 +659,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { */ private SingularData getSingularData(JavacNode node) { for (JavacNode child : node.down()) { - if (!annotationTypeMatches(Singular.class, child)) { - continue; - } + if (!annotationTypeMatches(Singular.class, child)) continue; Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); deleteAnnotationIfNeccessary(child, Singular.class); @@ -715,58 +677,48 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } Name singularName = node.toName(explicitSingular); - + JCExpression type = null; - if (node.get() instanceof JCVariableDecl) { - type = ((JCVariableDecl) node.get()).vartype; - } - + if (node.get() instanceof JCVariableDecl) type = ((JCVariableDecl) node.get()).vartype; + String name = null; List typeArgs = List.nil(); if (type instanceof JCTypeApply) { typeArgs = ((JCTypeApply) type).arguments; type = ((JCTypeApply) type).clazz; } - + name = type.toString(); - + String targetFqn = JavacSingularsRecipes.get().toQualified(name); JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn); if (singularizer == null) { node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated."); return null; } - + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer); } - + 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 JavacNode findInnerClass(JavacNode parent, String name) { for (JavacNode child : parent.down()) { - if (child.getKind() != Kind.TYPE) { - continue; - } + if (child.getKind() != Kind.TYPE) continue; JCClassDecl td = (JCClassDecl) child.get(); - if (td.name.contentEquals(name)) { - return child; - } + if (td.name.contentEquals(name)) return child; } return null; } - + private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { ListBuffer typeParamsForBuilderParameter = new ListBuffer(); for (JCTree typeParam : typeParams) { diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 67862355..ab8c3a42 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.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 @@ -29,7 +29,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; @@ -54,6 +53,14 @@ import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; public class JavacSingularsRecipes { + public interface ExpressionMaker { + JCExpression make(); + } + + public interface StatementMaker { + JCStatement make(); + } + private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes(); private final Map singularizers = new HashMap(); private final TypeLibrary singularizableTypes = new TypeLibrary(); @@ -197,32 +204,32 @@ public class JavacSingularsRecipes { public abstract java.util.List generateFields(SingularData data, JavacNode builderType, JCTree source); /** - * 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, JavacNode, JCTree, boolean, JCExpression, JCStatement)}. + * 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, JavacNode, JCTree, boolean, ExpressionMaker, StatementMaker)}. */ public void generateMethods(SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain) { final JavacTreeMaker maker = builderType.getTreeMaker(); - // TODO: Make these lambdas when switching to a source level >= 1.8. - Supplier returnType = new Supplier() { @Override public JCExpression get() { + + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { return chain ? - cloneSelfType(builderType) : - maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); + cloneSelfType(builderType) : + maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); }}; - Supplier returnStatement = new Supplier() { @Override public JCStatement get() { + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; }}; - generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); + + generateMethods(data, deprecate, builderType, source, 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, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement); + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker); public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName, String builderVariable); diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index ecaf5c87..ffaf6674 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.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 @@ -25,7 +25,6 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; -import java.util.function.Supplier; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; @@ -33,8 +32,10 @@ import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -70,11 +71,11 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index bd0f740e..39e53ebb 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.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 @@ -25,13 +25,14 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; -import java.util.function.Supplier; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -76,16 +77,16 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 1b4dd960..34350f40 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.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 @@ -25,15 +25,16 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Arrays; -import java.util.function.Supplier; import lombok.core.LombokImmutableList; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import org.mangosdk.spi.ProviderFor; @@ -101,16 +102,16 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html index 4801d373..0249e19a 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 v1.18.1. + @SuperBuilder was introduced as experimental feature in lombok v1.18.2.

@@ -19,10 +19,7 @@

@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. + @SuperBuilder generates a private constructor on the class that takes a builder instance as a parameter. This constructor sets the fields of the new instance to the values from the builder.

@SuperBuilder is not compatible with @Builder.

@@ -41,8 +38,6 @@

- <@f.snippets name="Builder" /> - <@f.confKeys>
lombok.superBuilder.flagUsage = [warning | error] (default: not set) @@ -66,6 +61,8 @@ 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. +

+ The generated builder code heavily relies on generics to avoid class casting when using the builder.

diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html index 309303a0..988a7154 100644 --- a/website/templates/features/experimental/index.html +++ b/website/templates/features/experimental/index.html @@ -64,7 +64,7 @@ <@main.feature title="@SuperBuilder" href="SuperBuilder"> - Bob now knows his ancestors: Builders with fields from superclasses, too. + Bob now knows his ancestors: Builders with fields from superclasses, too. -- cgit