diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2018-06-13 23:26:15 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2018-06-13 23:26:15 +0200 |
commit | 5dea27f56fef57eef91697b25d630163193b2979 (patch) | |
tree | e7591c0579162a9598b97d1e9349f7a9a7d990d6 /src/core/lombok/javac/handlers | |
parent | 249256224a8bb928eb9037f2c111854896f39014 (diff) | |
parent | 19ad4fd57d32afad1a33f20613fbb2e7607cfee0 (diff) | |
download | lombok-5dea27f56fef57eef91697b25d630163193b2979.tar.gz lombok-5dea27f56fef57eef91697b25d630163193b2979.tar.bz2 lombok-5dea27f56fef57eef91697b25d630163193b2979.zip |
Merge branch 'janrieke-superBuilder'
Diffstat (limited to 'src/core/lombok/javac/handlers')
11 files changed, 852 insertions, 100 deletions
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index fb3b45a4..d56d6ac2 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -550,7 +550,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); } } diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java index 4c4ba0e8..af45a620 100644 --- a/src/core/lombok/javac/handlers/HandleBuilderDefault.java +++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java @@ -31,6 +31,7 @@ import lombok.Builder; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; +import lombok.experimental.SuperBuilder; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -41,8 +42,9 @@ public class HandleBuilderDefault extends JavacAnnotationHandler<Builder.Default JavacNode annotatedField = annotationNode.up(); if (annotatedField.getKind() != Kind.FIELD) return; JavacNode classWithAnnotatedField = annotatedField.up(); - if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) { - annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything."); + if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField) + && !hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) { + annotationNode.addWarning("@Builder.Default requires @Builder or @SuperBuilder on the class for it to mean anything."); deleteAnnotationIfNeccessary(annotationNode, Builder.Default.class); } } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 4e3c9576..0ddaa7d7 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -207,6 +207,17 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { + JCExpression returnType = null; + JCReturn returnStatement = null; + if (shouldReturnThis) { + returnType = cloneSelfType(field); + returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + } + + return createSetter(access, deprecate, field, treeMaker, setterName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); + } + + public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -237,21 +248,13 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { statements.append(treeMaker.Exec(setBool)); } - JCExpression methodType = null; - if (shouldReturnThis) { - methodType = cloneSelfType(field); - } - if (methodType == null) { //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6. methodType = treeMaker.Type(Javac.createVoidType(field.getSymbolTable(), CTC_VOID)); - shouldReturnThis = false; + returnStatement = null; } - if (shouldReturnThis) { - JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); - statements.append(returnStatement); - } + if (returnStatement != null) statements.append(returnStatement); JCBlock methodBody = treeMaker.Block(0, statements.toList()); List<JCTypeParameter> methodGenericParams = List.nil(); diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java new file mode 100644 index 00000000..38fec2f6 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -0,0 +1,738 @@ +/* + * 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 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.Javac.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import java.util.ArrayList; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.code.BoundKind; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +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.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; +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.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCWildcard; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Builder.ObtainVia; +import lombok.ConfigurationKeys; +import lombok.Singular; +import lombok.ToString; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.InclusionExclusionUtils.Included; +import lombok.experimental.NonFinal; +import lombok.experimental.SuperBuilder; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; +import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; +import lombok.javac.handlers.JavacSingularsRecipes.SingularData; + +@ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. +public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { + private static final String SELF_METHOD = "self"; + + private static class BuilderFieldData { + JCExpression type; + Name rawName; + Name name; + Name nameOfSetFlag; + SingularData singularData; + 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) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); + + SuperBuilder superbuilderAnnotation = annotation.getInstance(); + deleteAnnotationIfNeccessary(annotationNode, SuperBuilder.class); + + 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; + } + + // Gather all fields of the class that should be set by the builder. + JCClassDecl td = (JCClassDecl) tdParent.get(); + ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); + boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); + for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); + JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); + boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); + BuilderFieldData bfd = new BuilderFieldData(); + bfd.rawName = fd.name; + bfd.name = removePrefixFromField(fieldNode); + 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; + 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.nameOfSetFlag = tdParent.toName(bfd.name + "$set"); + // The @Builder annotation removes the initializing expression on the field and moves + // it to a method called "$default$FIELDNAME". This method is then called upon building. + // We do NOT do this, because this is unexpected and may lead to bugs when using other + // constructors (see, e.g., issue #1347). + // Instead, we keep the init expression and only set a new value in the builder-based + // constructor if it was set in the builder. Drawback is that the init expression is + // always executed, even if it was unnecessary because its value is overwritten by the + // builder. + // TODO: Once the issue is resolved in @Builder, we can adapt the solution here. + } + addObtainVia(bfd, fieldNode); + builderFields.add(bfd); + allFields.append(fieldNode); + } + + // Set the names of the builder classes. + String builderClassName = td.name.toString() + "Builder"; + String builderImplClassName = builderClassName + "Impl"; + JCTree extendsClause = Javac.getExtendsClause(td); + JCExpression superclassBuilderClassExpression = null; + if (extendsClause instanceof JCTypeApply) { + // Remember the type arguments, because we need them for the extends clause of our abstract builder class. + 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, + tdParent.toName(superclassBuilderClassName)); + } else if (extendsClause != null) { + String superclassBuilderClassName = extendsClause.toString() + "Builder"; + superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName); + } + // If there is no superclass, superclassBuilderClassExpression is still == null at this point. + // You can use it to check whether to inherit or not. + + 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"; + // If these generics' names collide with any generics on the annotated class, modify them. + // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder. + java.util.List<String> typeParamStrings = new ArrayList<String>(); + for (JCTypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.getName().toString()); + classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); + builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); + + thrownExceptions = List.nil(); + + // 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) { + if (bfd.singularData.getSingularizer().requiresCleaning()) { + addCleaning = true; + break; + } + } + if (bfd.obtainVia != null) { + if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) { + bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\")."); + return; + } + if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) { + bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set."); + return; + } + } + } + + // 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(); + JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, uncleanField); + } + + // Generate abstract self() and build() methods in the abstract builder. + injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName)); + + // Create the setter methods in the abstract builder. + for (BuilderFieldData bfd : builderFields) { + generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); + } + + // Create the toString() method for the abstract builder. + java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>(); + for (BuilderFieldData bfd : builderFields) { + for (JavacNode f : bfd.createdFields) { + fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true)); + } + } + + // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. + JCMethodDecl toStringMethod = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast); + if (toStringMethod != null) injectMethod(builderType, toStringMethod); + + // If clean methods are requested, add them now. + if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + + recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); + + if ((td.mods.flags & Flags.ABSTRACT) == 0) { + // Only non-abstract classes get the Builder implementation. + + // Create the builder implementation class. + JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); + if (builderImplType == null) { + builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + } else { + annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); + return; + } + + // 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); + + // Create the self() and build() methods in the BuilderImpl. + injectMethod(builderImplType, generateSelfMethod(builderImplType)); + injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions)); + + recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); + } + + // Generate a constructor in the annotated class that takes a builder as argument. + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClassExpression != null); + + if ((td.mods.flags & Flags.ABSTRACT) == 0) { + // Only non-abstract classes get the Builder implementation and the builder() method. + + // 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 builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); + if (builderMethod != null) injectMethod(tdParent, builderMethod); + } + } + } + + /** + * 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()); + } + 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. + * The values from the builder are used to initialize the fields of new instances. + * + * @param typeNode + * the type (with the {@code @Builder} annotation) for which a + * constructor should be generated. + * @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 + * If {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + 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; + + 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); + if (!nonNulls.isEmpty()) { + JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, source); + if (nullCheck != null) 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"); + rhs = maker.Ident(bfd.singularData.getPluralName()); + } else { + rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName); + } + JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName); + + JCStatement assign = maker.Exec(maker.Assign(thisX, rhs)); + + // In case of @Builder.Default, only set the value if it really was set in the builder. + if (bfd.nameOfSetFlag != null) { + JCFieldAccess setField = maker.Select(maker.Ident(builderVariableName), bfd.nameOfSetFlag); + JCIf ifSet = maker.If(setField, assign, null); + statements.append(ifSet); + } else { + statements.append(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()); + Name builderClassname = typeNode.toName(builderClassName); + // First add all generics that are present on the parent type. + ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker); + // Now add the <?, ?>. + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParamsForBuilderParameter.add(wildcard); + wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParamsForBuilderParameter.add(wildcard); + JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParamsForBuilderParameter.toList()); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, 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))); + statements.prepend(maker.Exec(callToSuperConstructor)); + } + + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), + null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), + maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext()); + + injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } + + 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)); + + 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) { + JavacTreeMaker maker = type.getTreeMaker(); + List<JCAnnotation> annotations = List.nil(); + if (override) { + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + annotations = List.of(overrideAnnotation); + } + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); + Name name = type.toName(SELF_METHOD); + JCExpression returnType = maker.Ident(type.toName(builderGenericName)); + + 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(); + + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.<JCExpression>nil()); + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, List.of(overrideAnnotation)); + Name name = builderImplType.toName(SELF_METHOD); + JCExpression returnType = maker.Ident(builderImplType.toName(builderImplType.getName())); + + JCStatement statement = maker.Return(maker.Ident(builderImplType.toName("this"))); + JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); + + 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(); + if (override) { + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + annotations = List.of(overrideAnnotation); + } + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations); + Name name = type.toName(methodName); + JCExpression returnType = maker.Ident(type.toName(classGenericName)); + + return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, 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); + } + + 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 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); + } + + for (int i = len - 1; i >= 0; i--) { + BuilderFieldData bfd = builderFields.get(i); + if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { + bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); + } else { + 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; + } + JavacTreeMaker maker = builderType.getTreeMaker(); + if (field == null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); + field = injectFieldAndMarkGenerated(builderType, newField); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, newField); + } + bfd.createdFields.add(field); + } + } + } + + private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) { + boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); + final JavacTreeMaker maker = builderType.getTreeMaker(); + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { + return maker.Ident(builderType.toName(builderGenericName)); + }}; + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { + 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) { + generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make()); + } else { + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker); + } + } + + private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement) { + Name fieldName = ((JCVariableDecl) fieldNode.get()).name; + + for (JavacNode child : builderType.down()) { + if (child.getKind() != Kind.METHOD) continue; + JCMethodDecl methodDecl = (JCMethodDecl) child.get(); + Name existingName = methodDecl.name; + 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); + } + + private void addObtainVia(BuilderFieldData bfd, JavacNode node) { + for (JavacNode child : node.down()) { + if (!annotationTypeMatches(ObtainVia.class, child)) continue; + AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child); + bfd.obtainVia = ann.getInstance(); + bfd.obtainViaNode = child; + deleteAnnotationIfNeccessary(child, ObtainVia.class); + 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; + Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; + AnnotationValues<Singular> ann = createAnnotation(Singular.class, child); + deleteAnnotationIfNeccessary(child, Singular.class); + String explicitSingular = ann.getInstance().value(); + if (explicitSingular.isEmpty()) { + if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { + node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); + explicitSingular = pluralName.toString(); + } else { + explicitSingular = autoSingularize(pluralName.toString()); + if (explicitSingular == null) { + node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))"); + explicitSingular = pluralName.toString(); + } + } + } + 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; + } +} diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 6a76e1dd..ab8c3a42 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 The Project Lombok Authors. + * Copyright (C) 2015-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 @@ -53,6 +53,14 @@ import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; public class JavacSingularsRecipes { + public interface ExpressionMaker { + JCExpression make(); + } + + public interface StatementMaker { + JCStatement make(); + } + private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes(); private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>(); private final TypeLibrary singularizableTypes = new TypeLibrary(); @@ -194,8 +202,36 @@ public class JavacSingularsRecipes { } public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source); - public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain); - public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName); + + /** + * Generates the singular, plural, and clear methods for the given {@link SingularData}. + * Uses the given {@code builderType} as return type if {@code chain == true}, {@code void} otherwise. + * If you need more control over the return type and value, use + * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, ExpressionMaker, StatementMaker)}. + */ + public void generateMethods(SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain) { + final JavacTreeMaker maker = builderType.getTreeMaker(); + + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { + return chain ? + cloneSelfType(builderType) : + maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); + }}; + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { + return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + }}; + + generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); + } + + /** + * Generates the singular, plural, and clear methods for the given {@link SingularData}. + * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods. + */ + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker); + + public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable); public boolean requiresCleaning() { try { @@ -274,9 +310,9 @@ public class JavacSingularsRecipes { return arguments.toList(); } - /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ - protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens) { - Name thisName = builderType.toName("this"); + /** Generates '<em>builderVariable</em>.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ + protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens, String builderVariable) { + Name thisName = builderType.toName(builderVariable); JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size")); JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil()); if (nullGuard) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 0ab7da54..ffaf6674 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 The Project Lombok Authors. + * Copyright (C) 2015-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 @@ -32,11 +32,12 @@ import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -70,20 +71,11 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { @@ -168,7 +160,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); List<JCExpression> jceBlank = List.nil(); @@ -185,12 +177,12 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { JCExpression invokeBuild; { //this.pluralName.build(); - invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank); + invokeBuild = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName().toString(), "build"), jceBlank); } JCExpression isNull; { //this.pluralName == null - isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), maker.Literal(CTC_BOT, null)); + isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName(builderVariable)), data.getPluralName()), maker.Literal(CTC_BOT, null)); } JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build() diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 196ce45d..39e53ebb 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 The Project Lombok Authors. + * Copyright (C) 2015-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 @@ -30,10 +30,11 @@ import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -76,27 +77,16 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - Name thisName = builderType.toName("this"); - - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java index 3002a98f..b1375151 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java @@ -46,9 +46,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable"); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } @@ -71,7 +71,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari JCStatement assignStat; { // pluralName = java.util.Collections.singletonList(this.pluralName.get(0)); JCExpression zeroLiteral = maker.Literal(CTC_INT, 0); - JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "get"), List.of(zeroLiteral)); + JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName().toString(), "get"), List.of(zeroLiteral)); List<JCExpression> args = List.of(arg); JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "singletonList"), args); assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke)); @@ -82,12 +82,12 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari } /* default: Create with right size, then addAll */ { - List<JCStatement> defStats = createListCopy(maker, data, builderType, source); + List<JCStatement> defStats = createListCopy(maker, data, builderType, source, builderVariable); JCCase defaultCase = maker.Case(null, defStats); cases.append(defaultCase); } - JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList()); + JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source); JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); @@ -95,9 +95,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari statements.append(switchStat); } - private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); - Name thisName = builderType.toName("this"); + Name thisName = builderType.toName(builderVariable); JCExpression argToUnmodifiable; { // new java.util.ArrayList<Generics>(this.pluralName); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index fd699275..34350f40 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 The Project Lombok Authors. + * Copyright (C) 2015-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 @@ -31,13 +31,14 @@ import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil; +import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -101,26 +102,16 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - Symtab symbolTable = builderType.getSymbolTable(); - - JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); - - returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); - returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); + generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent); + generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source); } private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { @@ -212,18 +203,18 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Map")) { - statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source)); + statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source, builderVariable)); } else { - statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source)); + statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source, builderVariable)); } } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java index 317233cb..71a36c0e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java @@ -40,18 +40,18 @@ public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingulariz return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet"); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName); + guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable); return; } JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Set")) { - statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source)); + statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source, builderVariable)); } else { - statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source)); + statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source, builderVariable)); } } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 0589ac34..df521fd8 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -46,7 +46,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } - protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) { + protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); ListBuffer<JCCase> cases = new ListBuffer<JCCase>(); @@ -66,11 +66,11 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { // !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0)); // mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); JCExpression zeroLiteral = maker.Literal(CTC_INT, 0); - JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral)); + JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral)); List<JCExpression> args; if (mapMode) { JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0); - JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone)); + JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone)); args = List.of(arg, arg2); } else { args = List.of(arg); @@ -84,12 +84,12 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { } { // default: - List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source); + List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source, builderVariable); JCCase defaultCase = maker.Case(null, statements); cases.append(defaultCase); } - JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false), cases.toList()); + JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source); JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); @@ -125,9 +125,9 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { return maker.If(cond, thenPart, null); } - protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source) { + protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); - Name thisName = builderType.toName("this"); + Name thisName = builderType.toName(builderVariable); JCStatement createStat; { // pluralName = new java.util.TargetType(initialCap); @@ -136,10 +136,10 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(); // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE; // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2 - JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 0x40000000)); + JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true, builderVariable), maker.Literal(CTC_INT, 0x40000000)); JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE"); - JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true)); - JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 3))); + JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true, builderVariable)); + JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true, builderVariable), maker.Literal(CTC_INT, 3))); JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3)); JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight); constructorArgs = List.<JCExpression>of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue)); @@ -163,14 +163,14 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { Name ivar = builderType.toName("$i"); Name keyVarName = builderType.toName(data.getPluralName() + "$key"); JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put")); - JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar))); - JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar))); + JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar))); + JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar))); // [jdk9] We add an unneccessary (V) cast here. Not doing so gives an error in javac (build 9-ea+156-jigsaw-nightly-h6072-20170212): // error: method put in interface Map<K#2,V#2> cannot be applied to given types; arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); - JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true)); + JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable)); JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar)); fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement); } else { |