aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/javac/handlers/HandleSuperBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok/javac/handlers/HandleSuperBuilder.java')
-rw-r--r--src/core/lombok/javac/handlers/HandleSuperBuilder.java532
1 files changed, 287 insertions, 245 deletions
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 0004ddcb..0bb16d56 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2013-2018 The Project Lombok Authors.
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
- *
+ *
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -88,33 +88,41 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
ObtainVia obtainVia;
JavacNode obtainViaNode;
JavacNode originalFieldNode;
-
+
java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
}
-
+
@Override
public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
- SuperBuilder builderInstance = annotation.getInstance();
-
- String builderMethodName = builderInstance.builderMethodName();
- String buildMethodName = builderInstance.buildMethodName();
-
- if (builderMethodName == null) builderMethodName = "builder";
- if (buildMethodName == null) buildMethodName = "build";
-
- if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
- if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
-
+ SuperBuilder superbuilderAnnotation = annotation.getInstance();
+
+ String builderMethodName = superbuilderAnnotation.builderMethodName();
+ String buildMethodName = superbuilderAnnotation.buildMethodName();
+
+ if (builderMethodName == null) {
+ builderMethodName = "builder";
+ }
+ if (buildMethodName == null) {
+ buildMethodName = "build";
+ }
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) {
+ return;
+ }
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) {
+ return;
+ }
+
JavacNode tdParent = annotationNode.up();
-
+
java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
List<JCExpression> superclassTypeParams = List.nil();
-
+
boolean addCleaning = false;
-
+
if (!(tdParent.get() instanceof JCClassDecl)) {
annotationNode.addError("@SuperBuilder is only supported on types.");
return;
@@ -134,28 +142,32 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
bfd.originalFieldNode = fieldNode;
-
+
if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
isDefault = null;
}
-
+
if (fd.init == null && isDefault != null) {
isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
isDefault = null;
}
-
+
if (fd.init != null && isDefault == null) {
- if (isFinal) continue;
+ if (isFinal) {
+ continue;
+ }
fieldNode.addWarning("@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
}
-
+
if (isDefault != null) {
bfd.nameOfDefaultProvider = tdParent.toName("$default$" + bfd.name);
bfd.nameOfSetFlag = tdParent.toName(bfd.name + "$set");
JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams);
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
- if (md != null) injectMethod(tdParent, md);
+ if (md != null) {
+ injectMethod(tdParent, md);
+ }
}
addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
@@ -172,11 +184,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
superclassTypeParams = ((JCTypeApply)extendsClause).getTypeArguments();
// A class name with a generics type, e.g., "Superclass<A>".
extendsClause = ((JCTypeApply)extendsClause).getType();
- }
+ }
if (extendsClause instanceof JCFieldAccess) {
Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier();
String superclassBuilderClassName = superclassClassName + "Builder";
- superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess)extendsClause,
+ superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess)extendsClause,
tdParent.toName(superclassBuilderClassName));
} else if (extendsClause != null) {
String superclassBuilderClassName = extendsClause.toString() + "Builder";
@@ -187,7 +199,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
-
+
// <C, B> are the generics for our builder.
String classGenericName = "C";
String builderGenericName = "B";
@@ -199,22 +211,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);
-
- thrownExceptions = List.nil();
- generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
- superclassBuilderClassExpression != null);
+ thrownExceptions = List.nil();
- // Create the abstract builder class.
- JavacNode builderType = findInnerClass(tdParent, builderClassName);
- if (builderType == null) {
- builderType = makeBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression,
- typeParams, superclassTypeParams, ast, classGenericName, builderGenericName);
- } else {
- annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
- return;
- }
-
// Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
@@ -235,7 +234,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
}
- // Generate the fields in the abstract builder class that hold the values for the instance.
+ // Create the abstract builder class.
+ JavacNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression,
+ typeParams, superclassTypeParams, ast, classGenericName, builderGenericName);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Generate the fields in the abstract builder class that hold the values for the instance.
generateBuilderFields(builderType, builderFields, ast);
if (addCleaning) {
JavacTreeMaker maker = builderType.getTreeMaker();
@@ -249,9 +258,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
// Create the setter methods in the abstract builder.
for (BuilderFieldData bfd : builderFields) {
- makeSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
+ generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
}
-
+
// Create the toString() method for the abstract builder.
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
@@ -260,15 +269,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
// Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast);
- if (md != null) injectMethod(builderType, md);
+ if (md != null) {
+ injectMethod(builderType, md);
+ }
+ }
+
+ if (addCleaning) {
+ injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
}
-
- if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
-
+
// Create the builder implementation class.
JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
if (builderImplType == null) {
- builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast);
+ builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast);
} else {
annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
return;
@@ -276,33 +289,107 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
// Create a simple constructor for the BuilderImpl class.
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
- if (cd != null) injectMethod(builderImplType, cd);
+ if (cd != null) {
+ injectMethod(builderImplType, cd);
+ }
// Create the self() and build() methods in the BuilderImpl.
injectMethod(builderImplType, generateSelfMethod(builderImplType));
injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
-
+
+ // Generate a constructor in the annotated class that takes a builder as argument.
+ generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
+ superclassBuilderClassExpression != null);
+
// Add the builder() method to the annotated class.
+ // Allow users to specify their own builder() methods, e.g., to provide default values.
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
- if (md != null) injectMethod(tdParent, md);
+ if (md != null) {
+ injectMethod(tdParent, md);
+ }
}
-
+
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
}
-
- private String generateNonclashingNameFor(String classGenericName, java.util.List<String> typeParamStrings) {
- if (!typeParamStrings.contains(classGenericName))
- return classGenericName;
- int counter = 2;
- while (typeParamStrings.contains(classGenericName + counter)) {
- counter++;
+
+ /**
+ * Creates and returns the abstract builder class and injects it into the annotated class.
+ */
+ private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
+ JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams,
+ List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
+
+ // Keep any type params of the annotated class.
+ ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
+ allTypeParams.addAll(copyTypeParams(source, typeParams));
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method, named "C", which extends the annotated class.
+ JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
+ if (typeParams.nonEmpty()) {
+ // Add type params of the annotated class.
+ annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
}
- return classGenericName + counter;
+ allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.<JCExpression>of(annotatedClass)));
+ // 2. The return type for all setter methods, named "B", which extends this builder class.
+ Name builderClassName = tdParent.toName(builderClass);
+ ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
+ JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList());
+ allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.<JCExpression>of(typeApply)));
+
+ JCExpression extending = null;
+ if (superclassBuilderClassExpression != null) {
+ // If the annotated class extends another class, we want this builder to extend the builder of the superclass.
+ // 1. Add the type parameters of the superclass.
+ typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker);
+ // 2. Add the builder type params <C, B>.
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
+ extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList());
+ }
+
+ JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
+ }
+
+ /**
+ * Creates and returns the concrete builder implementation class and injects it into the annotated class.
+ */
+ private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
+
+ // Extend the abstract builder.
+ JCExpression extending = maker.Ident(tdParent.toName(builderAbstractClass));
+ // Add any type params of the annotated class.
+ ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
+ allTypeParams.addAll(copyTypeParams(source, typeParams));
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class.
+ JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
+ if (typeParams.nonEmpty()) {
+ // Add type params of the annotated class.
+ annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
+ }
+ // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class.
+ JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass));
+ if (typeParams.nonEmpty()) {
+ builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList());
+ }
+ ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
+ typeParamsForBuilder.add(annotatedClass);
+ typeParamsForBuilder.add(builderImplClassExpression);
+ extending = maker.TypeApply(extending, typeParamsForBuilder.toList());
+
+ JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
}
-
/**
* Generates a constructor that has a builder as the only parameter.
@@ -311,7 +398,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
* @param typeNode
* the type (with the {@code @Builder} annotation) for which a
* constructor should be generated.
- * @param typeParams
+ * @param typeParams
* @param builderFields a list of fields in the builder which should be assigned to new instances.
* @param source the annotation (used for setting source code locations for the generated code).
* @param callBuilderBasedSuperConstructor
@@ -321,16 +408,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
*/
private void generateBuilderBasedConstructor(JavacNode typeNode, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) {
JavacTreeMaker maker = typeNode.getTreeMaker();
-
+
AccessLevel level = AccessLevel.PROTECTED;
- boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
- if (isEnum) {
- level = AccessLevel.PRIVATE;
- }
-
+
ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>();
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
-
+
Name builderVariableName = typeNode.toName("b");
for (BuilderFieldData bfd : builderFields) {
List<JCAnnotation> nonNulls = findAnnotations(bfd.originalFieldNode, NON_NULL_PATTERN);
@@ -340,7 +423,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
nullChecks.append(nullCheck);
}
}
-
+
JCExpression rhs;
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name, "b");
@@ -348,15 +431,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
} else {
rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName);
}
- JCFieldAccess thisX = maker.Select(maker.Ident(bfd.originalFieldNode.toName("this")), bfd.rawName);
-
+ JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName);
+
JCExpression assign = maker.Assign(thisX, rhs);
-
+
statements.append(maker.Exec(assign));
}
-
+
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
-
+
// Create a constructor that has just the builder as parameter.
ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
@@ -387,16 +470,31 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
}
- private ListBuffer<JCExpression> getTypeParamExpressions(List<? extends JCTree> typeParams, JavacTreeMaker maker) {
- ListBuffer<JCExpression> typeParamsForBuilderParameter = new ListBuffer<JCExpression>();
- for (JCTree typeParam : typeParams) {
- if (typeParam instanceof JCTypeParameter) {
- typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName()));
- } else if (typeParam instanceof JCIdent) {
- typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName()));
- }
+ private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
}
- return typeParamsForBuilderParameter;
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
+ JCStatement statement = maker.Return(call);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ int modifiers = Flags.PUBLIC;
+ modifiers |= Flags.STATIC;
+
+ // Add any type params of the annotated class to the return type.
+ ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>();
+ typeParameterNames.addAll(typeParameterNames(maker, typeParams));
+ // Now add the <?, ?>.
+ JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParameterNames.add(wildcard);
+ typeParameterNames.add(wildcard);
+ JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
+
+ return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
@@ -412,7 +510,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
-
+
private JCMethodDecl generateSelfMethod(JavacNode builderImplType) {
JavacTreeMaker maker = builderImplType.getTreeMaker();
@@ -426,7 +524,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
-
+
private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) {
JavacTreeMaker maker = type.getTreeMaker();
List<JCAnnotation> annotations = List.nil();
@@ -440,87 +538,62 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
-
- private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
- JavacTreeMaker maker = type.getTreeMaker();
- ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
-
- for (BuilderFieldData bfd : builderFields) {
- if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
- bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
- }
- }
-
- statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0))));
- JCBlock body = maker.Block(0, statements.toList());
- return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
- }
-
+
private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List<JCExpression> thrownExceptions) {
JavacTreeMaker maker = type.getTreeMaker();
-
+
JCExpression call;
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
-
+
// 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);
statements.append(maker.Return(call));
-
+
JCBlock body = maker.Block(0, statements.toList());
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
-
+
return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
-
- public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) {
+
+ private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
+ }
+ }
+
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0))));
+ JCBlock body = maker.Block(0, statements.toList());
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ private JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) {
JavacTreeMaker maker = fieldNode.getTreeMaker();
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
-
+
JCStatement statement = maker.Return(field.init);
field.init = null;
-
+
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
int modifiers = Flags.PRIVATE | Flags.STATIC;
return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
-
- public JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
- JavacTreeMaker maker = type.getTreeMaker();
-
- ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
- for (JCTypeParameter typeParam : typeParams) {
- typeArgs.append(maker.Ident(typeParam.name));
- }
-
- JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
- JCStatement statement = maker.Return(call);
-
- JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
- int modifiers = Flags.PUBLIC;
- modifiers |= Flags.STATIC;
-
- // Add any type params of the annotated class to the return type.
- ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>();
- typeParameterNames.addAll(typeParameterNames(maker, typeParams));
- // Now add the <?, ?>.
- JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
- typeParameterNames.add(wildcard);
- typeParameterNames.add(wildcard);
- JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
- return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
- }
-
- public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
+ private void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
int len = builderFields.size();
java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
for (JavacNode child : builderType.down()) {
- if (child.getKind() == Kind.FIELD) existing.add(child);
+ if (child.getKind() == Kind.FIELD) {
+ existing.add(child);
+ }
}
-
+
for (int i = len - 1; i >= 0; i--) {
BuilderFieldData bfd = builderFields.get(i);
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
@@ -529,8 +602,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JavacNode field = null, setFlag = null;
for (JavacNode exists : existing) {
Name n = ((JCVariableDecl) exists.get()).name;
- if (n.equals(bfd.name)) field = exists;
- if (n.equals(bfd.nameOfSetFlag)) setFlag = exists;
+ if (n.equals(bfd.name)) {
+ field = exists;
+ }
+ if (n.equals(bfd.nameOfSetFlag)) {
+ setFlag = exists;
+ }
}
JavacTreeMaker maker = builderType.getTreeMaker();
if (field == null) {
@@ -547,8 +624,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
}
}
-
- public void makeSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) {
+
+ private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) {
boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode);
final JavacTreeMaker maker = builderType.getTreeMaker();
// TODO: Make these lambdas when switching to a source level >= 1.8.
@@ -559,113 +636,40 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.<JCExpression>nil()));
}};
if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
- makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get());
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnType.get(), returnStatement.get());
} else {
fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement);
}
}
-
- private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) {
+
+ private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCReturn returnStatement) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
-
+
for (JavacNode child : builderType.down()) {
- if (child.getKind() != Kind.METHOD) continue;
+ if (child.getKind() != Kind.METHOD) {
+ continue;
+ }
JCMethodDecl methodDecl = (JCMethodDecl) child.get();
Name existingName = methodDecl.name;
- if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
+ if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) {
+ return;
+ }
}
-
+
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
-
+
JavacTreeMaker maker = fieldNode.getTreeMaker();
-
+
JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
-
+
injectMethod(builderType, newMethod);
}
-
- public JavacNode findInnerClass(JavacNode parent, String name) {
- for (JavacNode child : parent.down()) {
- if (child.getKind() != Kind.TYPE) continue;
- JCClassDecl td = (JCClassDecl) child.get();
- if (td.name.contentEquals(name)) return child;
- }
- return null;
- }
-
- public JavacNode makeBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
- JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams,
- List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
- JavacTreeMaker maker = tdParent.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
- // Keep any type params of the annotated class.
- ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
- allTypeParams.addAll(copyTypeParams(source, typeParams));
- // Add builder-specific type params required for inheritable builders.
- // 1. The return type for the build() method, named "C", which extends the annotated class.
- JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
- if (typeParams.nonEmpty()) {
- // Add type params of the annotated class.
- annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
- }
- allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.<JCExpression>of(annotatedClass)));
- // 2. The return type for all setter methods, named "B", which extends this builder class.
- Name builderClassName = tdParent.toName(builderClass);
- ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
- typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
- typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
- JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList());
- allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.<JCExpression>of(typeApply)));
-
- JCExpression extending = null;
- if (superclassBuilderClassExpression != null) {
- // If the annotated class extends another class, we want this builder to extend the builder of the superclass.
- // 1. Add the type parameters of the superclass.
- typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker);
- // 2. Add the builder type params <C, B>.
- typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
- typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
- extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList());
- }
-
- JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.<JCExpression>nil(), List.<JCTree>nil());
- return injectType(tdParent, builder);
- }
-
- public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) {
- JavacTreeMaker maker = tdParent.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
-
- // Extend the abstract builder.
- JCExpression extending = maker.Ident(tdParent.toName(builderAbstractClass));
- // Add any type params of the annotated class.
- ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
- allTypeParams.addAll(copyTypeParams(source, typeParams));
- // Add builder-specific type params required for inheritable builders.
- // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class.
- JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
- if (typeParams.nonEmpty()) {
- // Add type params of the annotated class.
- annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
- }
- // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class.
- JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass));
- if (typeParams.nonEmpty()) {
- builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList());
- }
- ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
- typeParamsForBuilder.add(annotatedClass);
- typeParamsForBuilder.add(builderImplClassExpression);
- extending = maker.TypeApply(extending, typeParamsForBuilder.toList());
-
- JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil());
- return injectType(tdParent, builder);
- }
-
private void addObtainVia(BuilderFieldData bfd, JavacNode node) {
for (JavacNode child : node.down()) {
- if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ if (!annotationTypeMatches(ObtainVia.class, child)) {
+ continue;
+ }
AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
bfd.obtainVia = ann.getInstance();
bfd.obtainViaNode = child;
@@ -673,16 +677,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return;
}
}
-
+
/**
* Returns the explicitly requested singular annotation on this node (field
* or parameter), or null if there's no {@code @Singular} annotation on it.
- *
+ *
* @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
*/
private SingularData getSingularData(JavacNode node) {
for (JavacNode child : node.down()) {
- if (!annotationTypeMatches(Singular.class, child)) continue;
+ if (!annotationTypeMatches(Singular.class, child)) {
+ continue;
+ }
Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
deleteAnnotationIfNeccessary(child, Singular.class);
@@ -700,31 +706,67 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
}
Name singularName = node.toName(explicitSingular);
-
+
JCExpression type = null;
if (node.get() instanceof JCVariableDecl) {
type = ((JCVariableDecl) node.get()).vartype;
}
-
+
String name = null;
List<JCExpression> typeArgs = List.nil();
if (type instanceof JCTypeApply) {
typeArgs = ((JCTypeApply) type).arguments;
type = ((JCTypeApply) type).clazz;
}
-
+
name = type.toString();
-
+
String targetFqn = JavacSingularsRecipes.get().toQualified(name);
JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
if (singularizer == null) {
node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
return null;
}
-
+
return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
}
-
+
return null;
}
+
+ private String generateNonclashingNameFor(String classGenericName, java.util.List<String> typeParamStrings) {
+ if (!typeParamStrings.contains(classGenericName)) {
+ return classGenericName;
+ }
+ int counter = 2;
+ while (typeParamStrings.contains(classGenericName + counter)) {
+ counter++;
+ }
+ return classGenericName + counter;
+ }
+
+ private JavacNode findInnerClass(JavacNode parent, String name) {
+ for (JavacNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) {
+ continue;
+ }
+ JCClassDecl td = (JCClassDecl) child.get();
+ if (td.name.contentEquals(name)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ private ListBuffer<JCExpression> getTypeParamExpressions(List<? extends JCTree> typeParams, JavacTreeMaker maker) {
+ ListBuffer<JCExpression> typeParamsForBuilderParameter = new ListBuffer<JCExpression>();
+ for (JCTree typeParam : typeParams) {
+ if (typeParam instanceof JCTypeParameter) {
+ typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName()));
+ } else if (typeParam instanceof JCIdent) {
+ typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName()));
+ }
+ }
+ return typeParamsForBuilderParameter;
+ }
}