mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-15 02:17:12 +00:00
builder-based constructors for type @Builders [Eclipse]
This commit is contained in:
1 parent
5f02accc06
commit
f46aa53e8c
3 files changed
+125
-20
No files matched your search
@@ -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
|
||||
|
||||
@@ -139,6 +139,10 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
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<char[]> typeArgsForToBuilder = null;
|
||||
@@ -146,6 +150,9 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
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<Builder> {
|
||||
allFields.add(fieldNode);
|
||||
}
|
||||
|
||||
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null,
|
||||
Collections.<Annotation>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.<Annotation>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<Builder> {
|
||||
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<Builder> {
|
||||
|
||||
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<Builder> {
|
||||
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
|
||||
ConstructorDeclaration cd = HandleConstructor.createConstructor(
|
||||
AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, null,
|
||||
annotationNode, Collections.<Annotation>emptyList());
|
||||
annotationNode, Collections.<Annotation>emptyList(), null, false);
|
||||
if (cd != null) injectMethod(builderType, cd);
|
||||
}
|
||||
|
||||
@@ -405,7 +427,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
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<Builder> {
|
||||
return decl;
|
||||
}
|
||||
|
||||
public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> 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<BuilderFieldData> 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<Statement> statements = new ArrayList<Statement>();
|
||||
@@ -554,7 +583,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
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<Builder> {
|
||||
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<Builder> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
|
||||
List<Annotation> 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<Annotation> 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<Annotation> 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<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
|
||||
Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
|
||||
Boolean suppressConstructorProperties, List<Annotation> 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.<EclipseNode>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<EclipseNode> fields, boolean allToDefault,
|
||||
Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
|
||||
Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> 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;
|
||||
|
||||
Reference in new issue
Block a user