Skip to content

Commit

Permalink
[builder] added a feature: you can now configure builder class name v…
Browse files Browse the repository at this point in the history
…ia the config system
  • Loading branch information
rzwitserloot committed Jul 8, 2019
1 parent bcf2d55 commit c103955
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 46 deletions.
1 change: 1 addition & 0 deletions doc/changelog.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Lombok Changelog
### v1.18.9 "Edgy Guinea Pig"
* ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100)
* ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) with thanks to Alexander Bulgakov.
* ENHANCEMENT: You can now configure the generated builder class name via the config system, using key `lombok.builder.className`. See the [Builder documentation](https://projectlombok.org/features/Builder) and [SuperBuilder documentation](https://projectlombok.org/features/experimental/SuperBuilder)
* BUGFIX: Delombok would turn something like `List<byte[]>...` in a method parameter to `List<byte...>...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140)
* BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165)

Expand Down
4 changes: 2 additions & 2 deletions src/core/lombok/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@
/**
* Name of the builder class.
*
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
* Default for {@code @Builder} on types and constructors: see the configkey {@code lombok.builder.className}, which if not set defaults to {@code (TypeName)Builder}.
* <p>
* Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
* Default for {@code @Builder} on methods: see the configkey {@code lombok.builder.className}, which if not set defaults to {@code (ReturnTypeName)Builder}.
*
* @return Name of the builder class that will be generated (or if it already exists, will be filled with builder elements).
*/
Expand Down
7 changes: 7 additions & 0 deletions src/core/lombok/ConfigurationKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,13 @@ private ConfigurationKeys() {}

// ----- Builder -----

/**
* lombok configuration: {@code lombok.builder.classNames} = &lt;String: aJavaIdentifier (optionally with a star as placeholder for the type name)&gt; (Default: {@code *Builder}).
*
* For any usage of the {@code @Builder} annotation without an explicit {@code builderClassName} parameter, this value is used to determine the name of the builder class to generate (or to adapt if such an inner class already exists).
*/
public static final ConfigurationKey<String> BUILDER_CLASS_NAME = new ConfigurationKey<String>("lombok.builder.className", "Default name of the generated builder class. A * is replaced with the name of the relevant type (default = *Builder).") {};

/**
* lombok configuration: {@code lombok.builder.flagUsage} = {@code WARNING} | {@code ERROR}.
*
Expand Down
16 changes: 11 additions & 5 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ private static final char[] prefixWith(char[] prefix, char[] name) {

List<EclipseNode> nonFinalNonDefaultedFields = null;

if (builderClassName.isEmpty()) builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder";
boolean replaceNameInBuilderClassName = builderClassName.contains("*");

if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
Expand Down Expand Up @@ -265,7 +269,8 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
typeParams = td.typeParameters;
thrownExceptions = null;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder";
if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", new String(td.name));
replaceNameInBuilderClassName = false;
} else if (parent.get() instanceof ConstructorDeclaration) {
ConstructorDeclaration cd = (ConstructorDeclaration) parent.get();
if (cd.typeParameters != null && cd.typeParameters.length > 0) {
Expand All @@ -279,12 +284,13 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
typeParams = td.typeParameters;
thrownExceptions = cd.thrownExceptions;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = new String(cd.selector) + "Builder";
if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", new String(cd.selector));
replaceNameInBuilderClassName = false;
} else if (parent.get() instanceof MethodDeclaration) {
MethodDeclaration md = (MethodDeclaration) parent.get();
tdParent = parent.up();
isStatic = md.isStatic();

if (toBuilder) {
final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
char[] token;
Expand Down Expand Up @@ -360,7 +366,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
typeParams = md.typeParameters;
thrownExceptions = md.thrownExceptions;
nameOfStaticBuilderMethod = md.selector;
if (builderClassName.isEmpty()) {
if (replaceNameInBuilderClassName) {
char[] token;
if (md.returnType instanceof QualifiedTypeReference) {
char[][] tokens = ((QualifiedTypeReference) md.returnType).tokens;
Expand All @@ -387,7 +393,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
token = newToken;
}

builderClassName = new String(token) + "Builder";
builderClassName = builderClassName.replace("*", new String(token));
}
} else {
annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
Expand Down
6 changes: 4 additions & 2 deletions src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec
}

// Set the names of the builder classes.
String builderClassName = String.valueOf(td.name) + "Builder";
String builderClassNameTemplate = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassNameTemplate == null || builderClassNameTemplate.isEmpty()) builderClassNameTemplate = "*Builder";
String builderClassName = builderClassNameTemplate.replace("*", String.valueOf(td.name));
String builderImplClassName = builderClassName + "Impl";

typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0];
Expand All @@ -228,7 +230,7 @@ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, Ec
if (extendsClause instanceof QualifiedTypeReference) {
QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause;
String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken());
String superclassBuilderClassName = superclassClassName + "Builder";
String superclassBuilderClassName = builderClassNameTemplate.replace("*", superclassClassName);

char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1);
tokens[tokens.length] = superclassBuilderClassName.toCharArray();
Expand Down
34 changes: 22 additions & 12 deletions src/core/lombok/javac/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ static class BuilderFieldData {

ArrayList<JavacNode> nonFinalNonDefaultedFields = null;

if (builderClassName.isEmpty()) builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder";
boolean replaceNameInBuilderClassName = builderClassName.contains("*");

if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
JCClassDecl td = (JCClassDecl) tdParent.get();
Expand Down Expand Up @@ -211,7 +215,8 @@ static class BuilderFieldData {
typeParams = td.typarams;
thrownExceptions = List.nil();
nameOfBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString());
replaceNameInBuilderClassName = false;
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
if (!jmd.typarams.isEmpty()) {
Expand All @@ -225,7 +230,8 @@ static class BuilderFieldData {
typeParams = td.typarams;
thrownExceptions = jmd.thrown;
nameOfBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString());
replaceNameInBuilderClassName = false;
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
Expand All @@ -239,9 +245,10 @@ static class BuilderFieldData {
if (returnType instanceof JCTypeApply) {
returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext());
}
if (builderClassName.isEmpty()) {
if (replaceNameInBuilderClassName) {
String replStr = null;
if (returnType instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
replStr = ((JCFieldAccess) returnType).name.toString();
} else if (returnType instanceof JCIdent) {
Name n = ((JCIdent) returnType).name;

Expand All @@ -251,27 +258,30 @@ static class BuilderFieldData {
return;
}
}
builderClassName = n.toString() + "Builder";
replStr = n.toString();
} else if (returnType instanceof JCPrimitiveTypeTree) {
builderClassName = returnType.toString() + "Builder";
if (Character.isLowerCase(builderClassName.charAt(0))) {
builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
replStr = returnType.toString();
if (Character.isLowerCase(replStr.charAt(0))) {
replStr = Character.toTitleCase(replStr.charAt(0)) + replStr.substring(1);
}
} else if (returnType instanceof JCTypeApply) {
JCExpression clazz = ((JCTypeApply) returnType).clazz;
if (clazz instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) clazz).name + "Builder";
replStr = ((JCFieldAccess) clazz).name.toString();
} else if (clazz instanceof JCIdent) {
builderClassName = ((JCIdent) clazz).name + "Builder";
replStr = ((JCIdent) clazz).name.toString();
}
}

if (builderClassName.isEmpty()) {
if (replStr == null || replStr.isEmpty()) {
// This shouldn't happen.
System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
builderClassName = td.name.toString() + "Builder";
replStr = td.name.toString();
}
builderClassName = builderClassName.replace("*", replStr);
replaceNameInBuilderClassName = false;
}
if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString());
if (toBuilder) {
final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
if (returnType instanceof JCArrayTypeTree) {
Expand Down
10 changes: 6 additions & 4 deletions src/core/lombok/javac/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
}

// Set the names of the builder classes.
String builderClassName = td.name.toString() + "Builder";
String builderClassNameTemplate = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassNameTemplate == null || builderClassNameTemplate.isEmpty()) builderClassNameTemplate = "*Builder";
String builderClassName = builderClassNameTemplate.replace("*", td.name.toString());
String builderImplClassName = builderClassName + "Impl";
JCTree extendsClause = Javac.getExtendsClause(td);
JCExpression superclassBuilderClassExpression = null;
Expand All @@ -189,11 +191,11 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
}
if (extendsClause instanceof JCFieldAccess) {
Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier();
String superclassBuilderClassName = superclassClassName + "Builder";
String superclassBuilderClassName = builderClassNameTemplate.replace("*", superclassClassName);
superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess) extendsClause,
tdParent.toName(superclassBuilderClassName));
tdParent.toName(superclassBuilderClassName));
} else if (extendsClause != null) {
String superclassBuilderClassName = extendsClause.toString() + "Builder";
String superclassBuilderClassName = builderClassNameTemplate.replace("*", extendsClause.toString());
superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName);
}
// If there is no superclass, superclassBuilderClassExpression is still == null at this point.
Expand Down
18 changes: 9 additions & 9 deletions test/transform/resource/after-delombok/BuilderComplex.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class BuilderComplex {
private static <T extends Number> void testVoidWithGenerics(T number, int arg2, String arg3, BuilderComplex selfRef) {
}
@java.lang.SuppressWarnings("all")
public static class VoidBuilder<T extends Number> {
public static class TestVoidName<T extends Number> {
@java.lang.SuppressWarnings("all")
private T number;
@java.lang.SuppressWarnings("all")
Expand All @@ -13,25 +13,25 @@ public static class VoidBuilder<T extends Number> {
@java.lang.SuppressWarnings("all")
private BuilderComplex selfRef;
@java.lang.SuppressWarnings("all")
VoidBuilder() {
TestVoidName() {
}
@java.lang.SuppressWarnings("all")
public VoidBuilder<T> number(final T number) {
public TestVoidName<T> number(final T number) {
this.number = number;
return this;
}
@java.lang.SuppressWarnings("all")
public VoidBuilder<T> arg2(final int arg2) {
public TestVoidName<T> arg2(final int arg2) {
this.arg2 = arg2;
return this;
}
@java.lang.SuppressWarnings("all")
public VoidBuilder<T> arg3(final String arg3) {
public TestVoidName<T> arg3(final String arg3) {
this.arg3 = arg3;
return this;
}
@java.lang.SuppressWarnings("all")
public VoidBuilder<T> selfRef(final BuilderComplex selfRef) {
public TestVoidName<T> selfRef(final BuilderComplex selfRef) {
this.selfRef = selfRef;
return this;
}
Expand All @@ -42,11 +42,11 @@ public void execute() {
@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
return "BuilderComplex.VoidBuilder(number=" + this.number + ", arg2=" + this.arg2 + ", arg3=" + this.arg3 + ", selfRef=" + this.selfRef + ")";
return "BuilderComplex.TestVoidName(number=" + this.number + ", arg2=" + this.arg2 + ", arg3=" + this.arg3 + ", selfRef=" + this.selfRef + ")";
}
}
@java.lang.SuppressWarnings("all")
public static <T extends Number> VoidBuilder<T> builder() {
return new VoidBuilder<T>();
public static <T extends Number> TestVoidName<T> builder() {
return new TestVoidName<T>();
}
}
47 changes: 47 additions & 0 deletions test/transform/resource/after-delombok/BuilderCustomName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import java.util.List;
class BuilderCustomName<T> {
private final int field;
@java.lang.SuppressWarnings("all")
public static abstract class SimpleTestBuilder<T, C extends BuilderCustomName<T>, B extends SimpleTestBuilder<T, C, B>> {
@java.lang.SuppressWarnings("all")
private int field;
@java.lang.SuppressWarnings("all")
protected abstract B self();
@java.lang.SuppressWarnings("all")
public abstract C build();
@java.lang.SuppressWarnings("all")
public B field(final int field) {
this.field = field;
return self();
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
return "BuilderCustomName.SimpleTestBuilder(field=" + this.field + ")";
}
}
@java.lang.SuppressWarnings("all")
private static final class SimpleTestBuilderImpl<T> extends SimpleTestBuilder<T, BuilderCustomName<T>, SimpleTestBuilderImpl<T>> {
@java.lang.SuppressWarnings("all")
private SimpleTestBuilderImpl() {
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
protected SimpleTestBuilderImpl<T> self() {
return this;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public BuilderCustomName<T> build() {
return new BuilderCustomName<T>(this);
}
}
@java.lang.SuppressWarnings("all")
protected BuilderCustomName(final SimpleTestBuilder<T, ?, ?> b) {
this.field = b.field;
}
@java.lang.SuppressWarnings("all")
public static <T> SimpleTestBuilder<T, ?, ?> builder() {
return new SimpleTestBuilderImpl<T>();
}
}
18 changes: 9 additions & 9 deletions test/transform/resource/after-ecj/BuilderComplex.java
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
import java.util.List;
import lombok.Builder;
class BuilderComplex {
public static @java.lang.SuppressWarnings("all") class VoidBuilder<T extends Number> {
public static @java.lang.SuppressWarnings("all") class TestVoidName<T extends Number> {
private @java.lang.SuppressWarnings("all") T number;
private @java.lang.SuppressWarnings("all") int arg2;
private @java.lang.SuppressWarnings("all") String arg3;
private @java.lang.SuppressWarnings("all") BuilderComplex selfRef;
@java.lang.SuppressWarnings("all") VoidBuilder() {
@java.lang.SuppressWarnings("all") TestVoidName() {
super();
}
public @java.lang.SuppressWarnings("all") VoidBuilder<T> number(final T number) {
public @java.lang.SuppressWarnings("all") TestVoidName<T> number(final T number) {
this.number = number;
return this;
}
public @java.lang.SuppressWarnings("all") VoidBuilder<T> arg2(final int arg2) {
public @java.lang.SuppressWarnings("all") TestVoidName<T> arg2(final int arg2) {
this.arg2 = arg2;
return this;
}
public @java.lang.SuppressWarnings("all") VoidBuilder<T> arg3(final String arg3) {
public @java.lang.SuppressWarnings("all") TestVoidName<T> arg3(final String arg3) {
this.arg3 = arg3;
return this;
}
public @java.lang.SuppressWarnings("all") VoidBuilder<T> selfRef(final BuilderComplex selfRef) {
public @java.lang.SuppressWarnings("all") TestVoidName<T> selfRef(final BuilderComplex selfRef) {
this.selfRef = selfRef;
return this;
}
public @java.lang.SuppressWarnings("all") void execute() {
BuilderComplex.<T>testVoidWithGenerics(number, arg2, arg3, selfRef);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (((((((("BuilderComplex.VoidBuilder(number=" + this.number) + ", arg2=") + this.arg2) + ", arg3=") + this.arg3) + ", selfRef=") + this.selfRef) + ")");
return (((((((("BuilderComplex.TestVoidName(number=" + this.number) + ", arg2=") + this.arg2) + ", arg3=") + this.arg3) + ", selfRef=") + this.selfRef) + ")");
}
}
BuilderComplex() {
super();
}
private static @Builder(buildMethodName = "execute") <T extends Number>void testVoidWithGenerics(T number, int arg2, String arg3, BuilderComplex selfRef) {
}
public static @java.lang.SuppressWarnings("all") <T extends Number>VoidBuilder<T> builder() {
return new VoidBuilder<T>();
public static @java.lang.SuppressWarnings("all") <T extends Number>TestVoidName<T> builder() {
return new TestVoidName<T>();
}
}
Loading

0 comments on commit c103955

Please sign in to comment.