mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-13 10:15:53 +00:00
Allow @Builder on instance methods
This commit is contained in:
1 parent
95303949ca
commit
14af3bec3b
9 files changed
+205
-60
No files matched your search
@@ -1,6 +1,7 @@
|
||||
Lombok contributors in alphabetical order:
|
||||
|
||||
Christian Sterzl <christian.sterzl@gmail.com>
|
||||
Enrique da Costa Cambio <enrique.dacostacambio@gmail.com>
|
||||
Jappe van der Hel <jappe.vanderhel@gmail.com>
|
||||
Luan Nico <luannico27@gmail.com>
|
||||
Maarten Mulders <mthmulders@users.noreply.github.com>
|
||||
|
||||
@@ -31,21 +31,21 @@ import java.lang.annotation.Target;
|
||||
* The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
|
||||
* that contains a member which is annotated with {@code @Builder}.
|
||||
* <p>
|
||||
* If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
|
||||
* If a member is annotated, it must be either a constructor or a method. If a class is annotated,
|
||||
* then a private constructor is generated with all fields as arguments
|
||||
* (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
|
||||
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
|
||||
* <p>
|
||||
* The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
|
||||
* with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
|
||||
* with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the
|
||||
* method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
|
||||
* <p>
|
||||
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
|
||||
* constructor / static method (each field, when annotating a class), which returns the builder itself.
|
||||
* constructor / method (each field, when annotating a class), which returns the builder itself.
|
||||
* The builder also has a <code>build()</code> method which returns a completed instance of the original type,
|
||||
* created by passing all parameters as set via the various other methods in the builder to the constructor
|
||||
* or static method that was annotated with {@code @Builder}. The return type of this method will be the same
|
||||
* as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
|
||||
* or method that was annotated with {@code @Builder}. The return type of this method will be the same
|
||||
* as the relevant class, unless a method has been annotated, in which case it'll be equal to the
|
||||
* return type of that method.
|
||||
* <p>
|
||||
* Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>.
|
||||
@@ -107,15 +107,15 @@ import java.lang.annotation.Target;
|
||||
@Target({TYPE, METHOD, CONSTRUCTOR})
|
||||
@Retention(SOURCE)
|
||||
public @interface Builder {
|
||||
/** Name of the static method that creates a new builder instance. Default: {@code builder}. */
|
||||
/** Name of the method that creates a new builder instance. Default: {@code builder}. */
|
||||
String builderMethodName() default "builder";
|
||||
|
||||
/** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
|
||||
/** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
|
||||
String buildMethodName() default "build";
|
||||
|
||||
/** Name of the builder class.
|
||||
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
|
||||
* Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
|
||||
* Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
|
||||
*/
|
||||
String builderClassName() default "";
|
||||
}
|
||||
@@ -49,6 +49,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.QualifiedThisReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
|
||||
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
|
||||
@@ -138,6 +139,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
|
||||
EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null;
|
||||
boolean addCleaning = false;
|
||||
boolean isStatic = true;
|
||||
|
||||
if (parent.get() instanceof TypeDeclaration) {
|
||||
tdParent = parent;
|
||||
@@ -185,10 +187,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
} else if (parent.get() instanceof MethodDeclaration) {
|
||||
MethodDeclaration md = (MethodDeclaration) parent.get();
|
||||
tdParent = parent.up();
|
||||
if (!md.isStatic()) {
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
|
||||
return;
|
||||
}
|
||||
isStatic = md.isStatic();
|
||||
returnType = copyType(md.returnType, ast);
|
||||
typeParams = md.typeParameters;
|
||||
thrownExceptions = md.thrownExceptions;
|
||||
@@ -223,7 +222,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
builderClassName = new String(token) + "Builder";
|
||||
}
|
||||
} else {
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -241,8 +240,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
|
||||
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
|
||||
if (builderType == null) {
|
||||
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
|
||||
builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast);
|
||||
} else {
|
||||
TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
|
||||
if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
|
||||
annotationNode.addError("Existing Builder must be a static inner class.");
|
||||
return;
|
||||
} else if (!isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) {
|
||||
annotationNode.addError("Existing Builder must be a non-static inner class.");
|
||||
return;
|
||||
}
|
||||
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
|
||||
/* generate errors for @Singular BFDs that have one already defined node. */ {
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
@@ -288,7 +295,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
|
||||
MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
|
||||
if (md != null) injectMethod(builderType, md);
|
||||
}
|
||||
|
||||
@@ -307,7 +314,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
|
||||
MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
|
||||
if (md != null) injectMethod(tdParent, md);
|
||||
}
|
||||
}
|
||||
@@ -334,7 +341,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
return decl;
|
||||
}
|
||||
|
||||
public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
|
||||
public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> 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;
|
||||
@@ -380,7 +387,10 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
} else {
|
||||
MessageSend invoke = new MessageSend();
|
||||
invoke.selector = staticName;
|
||||
invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
|
||||
if (isStatic)
|
||||
invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
|
||||
else
|
||||
invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0);
|
||||
TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
|
||||
if (tps != null) {
|
||||
TypeReference[] trs = new TypeReference[tps.length];
|
||||
@@ -401,14 +411,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
return out;
|
||||
}
|
||||
|
||||
public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
|
||||
public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, 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.modifiers = ClassFileConstants.AccPublic;
|
||||
if (isStatic) out.modifiers |= ClassFileConstants.AccStatic;
|
||||
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
|
||||
out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
|
||||
out.typeParameters = copyTypeParams(typeParams, source);
|
||||
@@ -490,11 +501,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
|
||||
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;
|
||||
builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
|
||||
builder.modifiers |= ClassFileConstants.AccPublic;
|
||||
if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic;
|
||||
builder.typeParameters = copyTypeParams(typeParams, source);
|
||||
builder.name = builderClassName.toCharArray();
|
||||
builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
|
||||
|
||||
@@ -31,21 +31,21 @@ import java.lang.annotation.Target;
|
||||
* The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
|
||||
* that contains a member which is annotated with {@code @Builder}.
|
||||
* <p>
|
||||
* If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
|
||||
* If a member is annotated, it must be either a constructor or a method. If a class is annotated,
|
||||
* then a private constructor is generated with all fields as arguments
|
||||
* (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
|
||||
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
|
||||
* <p>
|
||||
* The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
|
||||
* with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
|
||||
* with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the
|
||||
* method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
|
||||
* <p>
|
||||
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
|
||||
* constructor / static method (each field, when annotating a class), which returns the builder itself.
|
||||
* constructor / method (each field, when annotating a class), which returns the builder itself.
|
||||
* The builder also has a <code>build()</code> method which returns a completed instance of the original type,
|
||||
* created by passing all parameters as set via the various other methods in the builder to the constructor
|
||||
* or static method that was annotated with {@code @Builder}. The return type of this method will be the same
|
||||
* as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
|
||||
* or method that was annotated with {@code @Builder}. The return type of this method will be the same
|
||||
* as the relevant class, unless a method has been annotated, in which case it'll be equal to the
|
||||
* return type of that method.
|
||||
* <p>
|
||||
* Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>.
|
||||
@@ -110,15 +110,15 @@ import java.lang.annotation.Target;
|
||||
@Retention(SOURCE)
|
||||
@Deprecated
|
||||
public @interface Builder {
|
||||
/** Name of the static method that creates a new builder instance. Default: {@code builder}. */
|
||||
/** Name of the method that creates a new builder instance. Default: {@code builder}. */
|
||||
String builderMethodName() default "builder";
|
||||
|
||||
/** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
|
||||
/** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
|
||||
String buildMethodName() default "build";
|
||||
|
||||
/** Name of the builder class.
|
||||
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
|
||||
* Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
|
||||
* Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
|
||||
*/
|
||||
String builderClassName() default "";
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ package lombok.javac.handlers;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.mangosdk.spi.ProviderFor;
|
||||
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
@@ -116,11 +118,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
JCExpression returnType;
|
||||
List<JCTypeParameter> typeParams = List.nil();
|
||||
List<JCExpression> thrownExceptions = List.nil();
|
||||
Name nameOfStaticBuilderMethod;
|
||||
Name nameOfBuilderMethod;
|
||||
JavacNode tdParent;
|
||||
|
||||
JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
|
||||
boolean addCleaning = false;
|
||||
boolean isStatic = true;
|
||||
|
||||
if (parent.get() instanceof JCClassDecl) {
|
||||
tdParent = parent;
|
||||
@@ -147,7 +150,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
|
||||
typeParams = td.typarams;
|
||||
thrownExceptions = List.nil();
|
||||
nameOfStaticBuilderMethod = null;
|
||||
nameOfBuilderMethod = null;
|
||||
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
|
||||
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
|
||||
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
|
||||
@@ -161,20 +164,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
|
||||
typeParams = td.typarams;
|
||||
thrownExceptions = jmd.thrown;
|
||||
nameOfStaticBuilderMethod = null;
|
||||
nameOfBuilderMethod = null;
|
||||
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
|
||||
} else if (fillParametersFrom != null) {
|
||||
tdParent = parent.up();
|
||||
JCClassDecl td = (JCClassDecl) tdParent.get();
|
||||
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
|
||||
if ((jmd.mods.flags & Flags.STATIC) == 0) {
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
|
||||
return;
|
||||
}
|
||||
isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
|
||||
returnType = jmd.restype;
|
||||
typeParams = jmd.typarams;
|
||||
thrownExceptions = jmd.thrown;
|
||||
nameOfStaticBuilderMethod = jmd.name;
|
||||
nameOfBuilderMethod = jmd.name;
|
||||
if (builderClassName.isEmpty()) {
|
||||
if (returnType instanceof JCTypeApply) {
|
||||
returnType = ((JCTypeApply) returnType).clazz;
|
||||
@@ -204,7 +204,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
|
||||
annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -222,8 +222,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
|
||||
JavacNode builderType = findInnerClass(tdParent, builderClassName);
|
||||
if (builderType == null) {
|
||||
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
|
||||
builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast);
|
||||
} else {
|
||||
JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get();
|
||||
if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
|
||||
annotationNode.addError("Existing Builder must be a static inner class.");
|
||||
return;
|
||||
} else if (!isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
|
||||
annotationNode.addError("Existing Builder must be a non-static inner class.");
|
||||
return;
|
||||
}
|
||||
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
|
||||
/* generate errors for @Singular BFDs that have one already defined node. */ {
|
||||
for (BuilderFieldData bfd : builderFields) {
|
||||
@@ -265,7 +273,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
}
|
||||
|
||||
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
|
||||
JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
|
||||
if (md != null) injectMethod(builderType, md);
|
||||
}
|
||||
|
||||
@@ -281,7 +289,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
|
||||
|
||||
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
|
||||
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
|
||||
JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams);
|
||||
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
|
||||
if (md != null) injectMethod(tdParent, md);
|
||||
}
|
||||
@@ -316,7 +324,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
*/
|
||||
}
|
||||
|
||||
private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
|
||||
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) {
|
||||
JavacTreeMaker maker = type.getTreeMaker();
|
||||
|
||||
JCExpression call;
|
||||
@@ -344,16 +352,19 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
|
||||
}
|
||||
|
||||
if (staticName == null) {
|
||||
if (builderName == null) {
|
||||
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
|
||||
statements.append(maker.Return(call));
|
||||
} else {
|
||||
|
||||
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
|
||||
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
|
||||
typeParams.append(maker.Ident(tp.name));
|
||||
}
|
||||
|
||||
JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
|
||||
JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name);
|
||||
if (!isStatic)
|
||||
callee = maker.Select(callee, type.up().toName("this"));
|
||||
JCExpression fn = maker.Select(callee, builderName);
|
||||
call = maker.Apply(typeParams.toList(), fn, args.toList());
|
||||
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
|
||||
statements.append(maker.Exec(call));
|
||||
@@ -364,10 +375,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
|
||||
JCBlock body = maker.Block(0, statements.toList());
|
||||
|
||||
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
|
||||
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
|
||||
}
|
||||
|
||||
public JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
|
||||
public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
|
||||
JavacTreeMaker maker = type.getTreeMaker();
|
||||
|
||||
ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
|
||||
@@ -379,7 +390,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
JCStatement statement = maker.Return(call);
|
||||
|
||||
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
|
||||
return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
|
||||
int modifiers = Flags.PUBLIC;
|
||||
if (isStatic) modifiers |= Flags.STATIC;
|
||||
return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
|
||||
}
|
||||
|
||||
public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
|
||||
@@ -443,9 +456,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
|
||||
public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
|
||||
JavacTreeMaker maker = tdParent.getTreeMaker();
|
||||
JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC);
|
||||
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());
|
||||
return injectType(tdParent, builder);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import java.util.List;
|
||||
class BuilderInstanceMethod<T> {
|
||||
public String create(int show, final int yes, List<T> also, int $andMe) {
|
||||
return "" + show + yes + also + $andMe;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public class StringBuilder {
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
private int show;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
private int yes;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
private List<T> also;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
private int $andMe;
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
StringBuilder() {
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public StringBuilder show(final int show) {
|
||||
this.show = show;
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public StringBuilder yes(final int yes) {
|
||||
this.yes = yes;
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public StringBuilder also(final List<T> also) {
|
||||
this.also = also;
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public StringBuilder $andMe(final int $andMe) {
|
||||
this.$andMe = $andMe;
|
||||
return this;
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public String build() {
|
||||
return BuilderInstanceMethod.this.create(show, yes, also, $andMe);
|
||||
}
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public java.lang.String toString() {
|
||||
return "BuilderInstanceMethod.StringBuilder(show=" + this.show + ", yes=" + this.yes + ", also=" + this.also + ", $andMe=" + this.$andMe + ")";
|
||||
}
|
||||
}
|
||||
@java.lang.SuppressWarnings("all")
|
||||
@javax.annotation.Generated("lombok")
|
||||
public StringBuilder builder() {
|
||||
return new StringBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import java.util.List;
|
||||
class BuilderInstanceMethod<T> {
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") class StringBuilder {
|
||||
private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int show;
|
||||
private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int yes;
|
||||
private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") List<T> also;
|
||||
private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int $andMe;
|
||||
@java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder() {
|
||||
super();
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder show(final int show) {
|
||||
this.show = show;
|
||||
return this;
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder yes(final int yes) {
|
||||
this.yes = yes;
|
||||
return this;
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder also(final List<T> also) {
|
||||
this.also = also;
|
||||
return this;
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder $andMe(final int $andMe) {
|
||||
this.$andMe = $andMe;
|
||||
return this;
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String build() {
|
||||
return BuilderInstanceMethod.this.create(show, yes, also, $andMe);
|
||||
}
|
||||
public @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") java.lang.String toString() {
|
||||
return (((((((("BuilderInstanceMethod.StringBuilder(show=" + this.show) + ", yes=") + this.yes) + ", also=") + this.also) + ", $andMe=") + this.$andMe) + ")");
|
||||
}
|
||||
}
|
||||
BuilderInstanceMethod() {
|
||||
super();
|
||||
}
|
||||
public @lombok.Builder String create(int show, final int yes, List<T> also, int $andMe) {
|
||||
return (((("" + show) + yes) + also) + $andMe);
|
||||
}
|
||||
public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder builder() {
|
||||
return new StringBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.List;
|
||||
|
||||
class BuilderInstanceMethod<T> {
|
||||
@lombok.Builder
|
||||
public String create(int show, final int yes, List<T> also, int $andMe) {
|
||||
return "" + show + yes + also + $andMe;
|
||||
}
|
||||
}
|
||||
@@ -28,16 +28,16 @@
|
||||
<code>@Builder</code> lets you automatically produce the code required to have your class be instantiable with code such as:<br />
|
||||
<code>Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();</code>
|
||||
</p><p>
|
||||
<code>@Builder</code> can be placed on a class, or on a constructor, or on a static method. While the "on a class" and "on a constructor"
|
||||
mode are the most common use-case, <code>@Builder</code> is most easily explained with the "static method" use-case.
|
||||
<code>@Builder</code> can be placed on a class, or on a constructor, or on a method. While the "on a class" and "on a constructor"
|
||||
mode are the most common use-case, <code>@Builder</code> is most easily explained with the "method" use-case.
|
||||
</p><p>
|
||||
A static method annotated with <code>@Builder</code> (from now on called the <em>target</em>) causes the following 7 things to be generated:<ul>
|
||||
<li>An inner static class named <code><em>Foo</em>Builder</code>, with the same type arguments as the static method (called the <em>builder</em>).</li>
|
||||
A method annotated with <code>@Builder</code> (from now on called the <em>target</em>) causes the following 7 things to be generated:<ul>
|
||||
<li>An inner class named <code><em>Foo</em>Builder</code>, with the same type arguments as the method (called the <em>builder</em>).</li>
|
||||
<li>In the <em>builder</em>: One private non-static non-final field for each parameter of the <em>target</em>.</li>
|
||||
<li>In the <em>builder</em>: A package private no-args empty constructor.</li>
|
||||
<li>In the <em>builder</em>: A 'setter'-like method for each parameter of the <em>target</em>: It has the same type as that parameter and the same name.
|
||||
It returns the builder itself, so that the setter calls can be chained, as in the above example.</li>
|
||||
<li>In the <em>builder</em>: A <code>build()</code> method which calls the static method, passing in each field. It returns the same type that the
|
||||
<li>In the <em>builder</em>: A <code>build()</code> method which calls the method, passing in each field. It returns the same type that the
|
||||
<em>target</em> returns.</li>
|
||||
<li>In the <em>builder</em>: A sensible <code>toString()</code> implementation.</li>
|
||||
<li>In the class containing the <em>target</em>: A <code>builder()</code> method, which creates a new instance of the <em>builder</em>.</li>
|
||||
@@ -51,7 +51,7 @@
|
||||
element to the list. For example: <code>Person.builder().job("Mythbusters").job("Unchained Reaction").build();</code> would result in the <code>List<String> jobs</code>
|
||||
field to have 2 strings in it. To get this behaviour, the field/parameter needs to be annotated with <code>@Singular</code>. The feature has <a href="#singular">its own documentation</a>.
|
||||
</p><p>
|
||||
Now that the "static method" mode is clear, putting a <code>@Builder</code> annotation on a constructor functions similarly; effectively,
|
||||
Now that the "method" mode is clear, putting a <code>@Builder</code> annotation on a constructor functions similarly; effectively,
|
||||
constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their
|
||||
type parameters are the same as the type parameters of the class itself.
|
||||
</p><p>
|
||||
@@ -61,8 +61,8 @@
|
||||
</p><p>
|
||||
The name of the builder class is <code><em>Foobar</em>Builder</code>, where <em>Foobar</em> is the simplified, title-cased form of the return type of the
|
||||
<em>target</em> - that is, the name of your type for <code>@Builder</code> on constructors and types, and the name of the return type for <code>@Builder</code>
|
||||
on static methods. For example, if <code>@Builder</code> is applied to a class named <code>com.yoyodyne.FancyList<T></code>, then the builder name will be
|
||||
<code>FancyListBuilder<T></code>. If <code>@Builder</code> is applied to a static method that returns <code>void</code>, the builder will be named
|
||||
on methods. For example, if <code>@Builder</code> is applied to a class named <code>com.yoyodyne.FancyList<T></code>, then the builder name will be
|
||||
<code>FancyListBuilder<T></code>. If <code>@Builder</code> is applied to a method that returns <code>void</code>, the builder will be named
|
||||
<code>VoidBuilder</code>.
|
||||
</p><p>
|
||||
The configurable aspects of builder are:<ul>
|
||||
@@ -77,7 +77,7 @@
|
||||
<div class="overview">
|
||||
<h3><a name="singular">@Singular</a></h3>
|
||||
<p>
|
||||
By annotating one of the parameters (if annotating a static method or constructor with <code>@Builder</code>) or fields (if annotating a class with <code>@Builder</code>) with the
|
||||
By annotating one of the parameters (if annotating a method or constructor with <code>@Builder</code>) or fields (if annotating a class with <code>@Builder</code>) with the
|
||||
<code>@Singular</code> annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one
|
||||
which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. These 'singular' builders
|
||||
are very complicated in order to guarantee the following properties:
|
||||
|
||||
Reference in new issue
Block a user