Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setter prefixes for SuperBuilder #2357

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec
bfd.builderFieldName = bfd.name;
bfd.annotations = copyAnnotations(fd, copyableAnnotations);
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast);
bfd.singularData = getSingularData(fieldNode, ast, superbuilderAnnotation.setterPrefix());
bfd.originalFieldNode = fieldNode;

if (bfd.singularData != null && isDefault != null) {
Expand Down Expand Up @@ -335,7 +335,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec
// Generate $fillValuesFrom() method in the abstract builder.
injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams));
// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast, superbuilderAnnotation.setterPrefix()));
}

// Generate abstract self() and build() methods in the abstract builder.
Expand All @@ -344,7 +344,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec

// Create the setter methods in the abstract builder.
for (BuilderFieldData bfd : builderFields) {
generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName);
generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName, superbuilderAnnotation.setterPrefix());
}

// Create the toString() method for the abstract builder.
Expand Down Expand Up @@ -701,8 +701,9 @@ private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean
* b.field(instance.field);
* }
* </pre>
* @param setterPrefix the prefix for setter methods
*/
private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source, String setterPrefix) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
out.selector = FILL_VALUES_STATIC_METHOD_NAME;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
Expand Down Expand Up @@ -731,7 +732,7 @@ private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, S

// Call the builder's setter methods to fill the values from the instance.
for (BuilderFieldData bfd : builderFields) {
MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source);
MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source, setterPrefix);
body.add(exec);
}

Expand All @@ -740,8 +741,8 @@ private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, S
return out;
}

private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, EclipseNode type, ASTNode source) {
char[] setterName = bfd.name;
private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, EclipseNode type, ASTNode source, String setterPrefix) {
char[] setterName = HandlerUtil.buildAccessorName(setterPrefix, String.valueOf(bfd.name)).toCharArray();
MessageSend ms = new MessageSend();
Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];

Expand Down Expand Up @@ -919,7 +920,7 @@ private void generateBuilderFields(EclipseNode builderType, List<BuilderFieldDat
}
}

private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) {
private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName, String setterPrefix) {
boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);

TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
Expand All @@ -938,29 +939,27 @@ private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, Eclips
};

if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, true, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode);
generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode, setterPrefix);
} else {
bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC);
}
}

private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, boolean fluent, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode) {
private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode, String setterPrefix) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
ASTNode source = sourceNode.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY_METHODS;
int len = existing.length;
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
char[] name = fd.name;

String setterName = HandlerUtil.buildAccessorName(setterPrefix, new String(paramName));

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;
if (Arrays.equals(setterName.toCharArray(), existingName) && !isTolerate(fieldNode, existing[i])) return;
}

String setterName = fluent ? new String(paramName) : HandlerUtil.buildAccessorName("set", new String(paramName));

List<Annotation> methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode));
if (cfv.generateReturnsReceiver()) {
methodAnnsList = new ArrayList<Annotation>(methodAnnsList);
Expand Down Expand Up @@ -997,8 +996,9 @@ private void addObtainVia(BuilderFieldData bfd, EclipseNode node) {
* 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.
* @param setterPrefix the prefix for setter methods
*/
private SingularData getSingularData(EclipseNode node, ASTNode source) {
private SingularData getSingularData(EclipseNode node, ASTNode source, String setterPrefix) {
for (EclipseNode child : node.down()) {
if (!annotationTypeMatches(Singular.class, child)) continue;

Expand Down Expand Up @@ -1047,7 +1047,7 @@ private SingularData getSingularData(EclipseNode node, ASTNode source) {
return null;
}

return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, singularInstance.ignoreNullCollections());
return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, singularInstance.ignoreNullCollections(), setterPrefix.toCharArray());
}

return null;
Expand Down
19 changes: 19 additions & 0 deletions src/core/lombok/experimental/SuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,23 @@
* @return Whether to generate a {@code toBuilder()} method.
*/
boolean toBuilder() default false;

/**
* Prefix to prepend to 'set' methods in the generated builder class. By default, generated methods do not include a prefix.
*
* For example, a method normally generated as {@code someField(String someField)} would instead be
* generated as {@code withSomeField(String someField)} if using {@code @SuperBuilder(setterPrefix = "with")}.
*
* Note that using "with" to prefix builder setter methods is strongly discouraged as as "with" normally
* suggests immutable data structures, and builders by definition are mutable objects.
*
* For {@code @Singular} fields, the generated methods are called {@code withName}, {@code withNames}, and {@code clearNames}, instead of
* the default {@code name}, {@code names}, and {@code clearNames}.
*
* This prefix only applies to the 'set' methods for the fields of the annotated class.
* For consistency reasons, you should use the same prefix on all superclasses and subclasses that use {@code @SuperBuilder}.
*
* @return The prefix to prepend to generated method names.
*/
String setterPrefix() default "";
}
37 changes: 20 additions & 17 deletions src/core/lombok/javac/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;

boolean toBuilder = superbuilderAnnotation.toBuilder();

JavacNode tdParent = annotationNode.up();

java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
Expand Down Expand Up @@ -148,7 +148,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
bfd.builderFieldName = bfd.name;
bfd.annotations = findCopyableAnnotations(fieldNode);
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
bfd.singularData = getSingularData(fieldNode, superbuilderAnnotation.setterPrefix());
bfd.originalFieldNode = fieldNode;

if (bfd.singularData != null && isDefault != null) {
Expand Down Expand Up @@ -281,7 +281,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
recursiveSetGeneratedBy(fvm, ast, annotationNode.getContext());
injectMethod(builderType, fvm);
// Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
JCMethodDecl sfvm = generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields);
JCMethodDecl sfvm = generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, superbuilderAnnotation.setterPrefix());
recursiveSetGeneratedBy(sfvm, ast, annotationNode.getContext());
injectMethod(builderType, sfvm);
}
Expand All @@ -296,7 +296,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,

// Create the setter methods in the abstract builder.
for (BuilderFieldData bfd : builderFields) {
generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName);
generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName, superbuilderAnnotation.setterPrefix());
}

// Create the toString() method for the abstract builder.
Expand Down Expand Up @@ -671,8 +671,9 @@ private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited,
* b.field(instance.field);
* }
* </pre>
* @param setterPrefix the prefix for setter methods
*/
private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields) {
private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, String setterPrefix) {
JavacTreeMaker maker = type.getTreeMaker();
List<JCAnnotation> annotations = List.nil();
JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations);
Expand All @@ -697,7 +698,7 @@ private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String build

// Call the builder's setter methods to fill the values from the instance.
for (BuilderFieldData bfd : builderFields) {
JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker);
JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker, setterPrefix);
body.append(exec);
}

Expand All @@ -706,7 +707,7 @@ private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String build
return maker.MethodDef(modifiers, name, returnType, copyTypeParams(type, typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
}

private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker) {
private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker, String setterPrefix) {
JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
for (int i = 0; i < tgt.length; i++) {
Expand Down Expand Up @@ -736,7 +737,9 @@ private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData
JCExpression emptyCollection = maker.Apply(List.<JCExpression>nil(), chainDots(type, emptyMaker.split("\\.")), List.<JCExpression>nil());
arg = maker.Conditional(eqNull, emptyCollection, tgt[1]);
}
JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), bfd.name), List.of(arg));

String setterName = HandlerUtil.buildAccessorName(setterPrefix, bfd.name.toString());
JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), type.toName(setterName)), List.of(arg));
JCExpressionStatement exec = maker.Exec(apply);
return exec;
}
Expand Down Expand Up @@ -872,7 +875,7 @@ private void generateBuilderFields(JavacNode builderType, java.util.List<Builder
for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext());
}

private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) {
private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName, String setterPrefix) {
boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode);
final JavacTreeMaker maker = builderType.getTreeMaker();
ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() {
Expand All @@ -884,24 +887,23 @@ private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, final
}};

if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode);
generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode, setterPrefix);
} else {
fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC);
}
}

private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, String setterPrefix) {
String setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString());
Name setterName_ = builderType.toName(setterName);

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;
if (existingName.equals(setterName_) && !isTolerate(fieldNode, methodDecl)) return;
}

String setterName = fluent ? paramName.toString() : HandlerUtil.buildAccessorName("set", paramName.toString());

JavacTreeMaker maker = fieldNode.getTreeMaker();

List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
Expand Down Expand Up @@ -940,8 +942,9 @@ private void addObtainVia(BuilderFieldData bfd, JavacNode node) {
* 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.
* @param setterPrefix the prefix for setter methods
*/
private SingularData getSingularData(JavacNode node) {
private SingularData getSingularData(JavacNode node, String setterPrefix) {
for (JavacNode child : node.down()) {
if (!annotationTypeMatches(Singular.class, child)) continue;
Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
Expand Down Expand Up @@ -982,7 +985,7 @@ private SingularData getSingularData(JavacNode node) {
return null;
}

return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, singularInstance.ignoreNullCollections());
return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, singularInstance.ignoreNullCollections(), setterPrefix);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ public B resetToDefault() {
field1 = 0;
return self();
}
public B field1(int field1) {
this.field1 = field1 + 1;
return self();
}
@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.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
Expand Down
Loading