mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-15 02:17:12 +00:00
builder-based constructors for type @Builders [javac]
This commit is contained in:
1 parent
f46aa53e8c
commit
4ec2e5135c
2 files changed
+126
-19
No files matched your search
@@ -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<Builder> {
|
||||
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<Name> 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<Builder> {
|
||||
allFields.append(fieldNode);
|
||||
}
|
||||
|
||||
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>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.<JCAnnotation>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("<init>")) {
|
||||
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<Builder> {
|
||||
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<Builder> {
|
||||
|
||||
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<Builder> {
|
||||
}
|
||||
|
||||
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
|
||||
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, null, annotationNode);
|
||||
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, null, annotationNode, null, false);
|
||||
if (cd != null) injectMethod(builderType, cd);
|
||||
}
|
||||
|
||||
@@ -359,7 +387,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
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<Builder> {
|
||||
*/
|
||||
}
|
||||
|
||||
private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> 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<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) {
|
||||
JavacTreeMaker maker = type.getTreeMaker();
|
||||
|
||||
JCExpression call;
|
||||
@@ -525,7 +560,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
if (builderName == null) {
|
||||
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
|
||||
if (useBuilderBasedConstructor) {
|
||||
// Use a constructor that only has this builder as parameter.
|
||||
List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this")));
|
||||
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, builderArg, null);
|
||||
} else {
|
||||
// Use a constructor with all the fields.
|
||||
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
|
||||
}
|
||||
statements.append(maker.Return(call));
|
||||
} else {
|
||||
|
||||
@@ -629,12 +671,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
|
||||
public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List<JCTypeParameter> 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.<JCExpression>nil(), List.<JCTree>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.<JCExpression>nil(), List.<JCTree>nil());
|
||||
return injectType(tdParent, builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>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.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
|
||||
generateConstructor(typeNode, level, List.<JCAnnotation>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.<JCAnnotation>nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
|
||||
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source, null, false);
|
||||
}
|
||||
|
||||
public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> 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<JCAnnotation> onConstructor, List<JavacNode> 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<Type> argTypes = new ListBuffer<Type>();
|
||||
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<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> 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<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> 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<JCStatement> assigns = new ListBuffer<JCStatement>();
|
||||
ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
|
||||
|
||||
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.<JCExpression>nil(),
|
||||
maker.Ident(typeNode.toName("super")),
|
||||
List.<JCExpression>of(maker.Ident(builderVariableName)));
|
||||
assigns.prepend(maker.Exec(callToSuperConstructor));
|
||||
}
|
||||
|
||||
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
|
||||
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
|
||||
maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
|
||||
|
||||
Reference in new issue
Block a user