diff options
Diffstat (limited to 'src/core/lombok/javac/handlers/HandleSuperBuilder.java')
-rw-r--r-- | src/core/lombok/javac/handlers/HandleSuperBuilder.java | 532 |
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; + } } |