Search in sources :

Example 1 with BuilderFieldData

use of lombok.javac.handlers.HandleBuilder.BuilderFieldData in project lombok by rzwitserloot.

the class HandleSuperBuilder method generateCleanMethod.

private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JavacNode 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);
}
Also used : BuilderFieldData(lombok.javac.handlers.HandleBuilder.BuilderFieldData) JCBlock(com.sun.tools.javac.tree.JCTree.JCBlock) JavacTreeMaker(lombok.javac.JavacTreeMaker) ListBuffer(com.sun.tools.javac.util.ListBuffer) JCStatement(com.sun.tools.javac.tree.JCTree.JCStatement)

Example 2 with BuilderFieldData

use of lombok.javac.handlers.HandleBuilder.BuilderFieldData in project lombok by rzwitserloot.

the class HandleSuperBuilder method generateStaticFillValuesMethod.

/**
 * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
 * the builder implementation class that copies all fields from the instance
 * to the builder. It looks like this:
 *
 * <pre>
 * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
 * 	b.field(instance.field);
 * }
 * </pre>
 */
private JCMethodDecl generateStaticFillValuesMethod(SuperBuilderJob job, String setterPrefix) {
    JavacTreeMaker maker = job.getTreeMaker();
    List<JCAnnotation> annotations = List.nil();
    JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations);
    Name name = job.toName(STATIC_FILL_VALUES_METHOD_NAME);
    JCExpression returnType = maker.TypeIdent(CTC_VOID);
    // 1st parameter: "Foobar instance"
    JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(job.parentType), null);
    // 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type)
    // First add all generics that are present on the parent type.
    ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(job.typeParams, maker, job.sourceNode);
    // Now add the <?, ?>.
    JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
    typeParamsForBuilderParameter.append(wildcard);
    wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
    typeParamsForBuilderParameter.append(wildcard);
    JCTypeApply builderType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), typeParamsForBuilderParameter.toList());
    JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(BUILDER_VARIABLE_NAME), builderType, null);
    ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
    // Call the builder's setter methods to fill the values from the instance.
    for (BuilderFieldData bfd : job.builderFields) {
        JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, job, setterPrefix);
        body.append(exec);
    }
    JCBlock bodyBlock = maker.Block(0, body.toList());
    return maker.MethodDef(modifiers, name, returnType, copyTypeParams(job.builderType, job.typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
}
Also used : JCWildcard(com.sun.tools.javac.tree.JCTree.JCWildcard) BuilderFieldData(lombok.javac.handlers.HandleBuilder.BuilderFieldData) JCBlock(com.sun.tools.javac.tree.JCTree.JCBlock) JavacTreeMaker(lombok.javac.JavacTreeMaker) ListBuffer(com.sun.tools.javac.util.ListBuffer) JCTypeApply(com.sun.tools.javac.tree.JCTree.JCTypeApply) JCStatement(com.sun.tools.javac.tree.JCTree.JCStatement) JCExpressionStatement(com.sun.tools.javac.tree.JCTree.JCExpressionStatement) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) Name(com.sun.tools.javac.util.Name) JCTypeParameter(com.sun.tools.javac.tree.JCTree.JCTypeParameter) JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) JCModifiers(com.sun.tools.javac.tree.JCTree.JCModifiers) JCAnnotation(com.sun.tools.javac.tree.JCTree.JCAnnotation)

Example 3 with BuilderFieldData

use of lombok.javac.handlers.HandleBuilder.BuilderFieldData in project lombok by rzwitserloot.

the class HandleSuperBuilder method handle.

@Override
public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
    handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
    SuperBuilderJob job = new SuperBuilderJob();
    job.sourceNode = annotationNode;
    job.checkerFramework = getCheckerFrameworkVersion(annotationNode);
    job.isStatic = true;
    SuperBuilder annInstance = annotation.getInstance();
    job.init(annotation, annInstance, annotationNode);
    boolean generateBuilderMethod;
    if (job.builderMethodName.isEmpty()) {
        generateBuilderMethod = false;
    } else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) {
        return;
    } else {
        generateBuilderMethod = true;
    }
    if (!checkName("buildMethodName", job.buildMethodName, annotationNode))
        return;
    // Do not delete the SuperBuilder annotation here, we need it for @Jacksonized.
    JavacNode parent = annotationNode.up();
    job.builderFields = new ArrayList<BuilderFieldData>();
    job.typeParams = List.nil();
    List<JCExpression> buildMethodThrownExceptions = List.nil();
    List<JCExpression> superclassTypeParams = List.nil();
    boolean addCleaning = false;
    if (!isClass(parent)) {
        annotationNode.addError("@SuperBuilder is only supported on classes.");
        return;
    }
    if (!isStaticAllowed(parent)) {
        annotationNode.addError("@SuperBuilder is not supported on non-static nested classes.");
        return;
    }
    job.parentType = parent;
    JCClassDecl td = (JCClassDecl) parent.get();
    // Gather all fields of the class that should be set by the builder.
    ArrayList<JavacNode> nonFinalNonDefaultedFields = null;
    boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));
    for (JavacNode fieldNode : HandleConstructor.findAllFields(parent, true)) {
        JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
        JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, false);
        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.builderFieldName = bfd.name;
        bfd.annotations = findCopyableAnnotations(fieldNode);
        bfd.type = fd.vartype;
        bfd.singularData = getSingularData(fieldNode, annInstance.setterPrefix());
        bfd.originalFieldNode = fieldNode;
        if (bfd.singularData != null && isDefault != null) {
            isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
            findAnnotation(Builder.Default.class, fieldNode, true);
            isDefault = null;
        }
        if (fd.init == null && isDefault != null) {
            isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
            findAnnotation(Builder.Default.class, fieldNode, true);
            isDefault = null;
        }
        if (fd.init != null && isDefault == null) {
            if (isFinal)
                continue;
            if (nonFinalNonDefaultedFields == null)
                nonFinalNonDefaultedFields = new ArrayList<JavacNode>();
            nonFinalNonDefaultedFields.add(fieldNode);
        }
        if (isDefault != null) {
            bfd.nameOfDefaultProvider = parent.toName(DEFAULT_PREFIX + bfd.name);
            bfd.nameOfSetFlag = parent.toName(bfd.name + SET_PREFIX);
            bfd.builderFieldName = parent.toName(bfd.name + VALUE_PREFIX);
            JCMethodDecl md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams, job);
            if (md != null)
                injectMethod(parent, md);
        }
        addObtainVia(bfd, fieldNode);
        job.builderFields.add(bfd);
    }
    job.typeParams = job.builderTypeParams = td.typarams;
    job.builderClassName = job.replaceBuilderClassName(td.name);
    if (!checkName("builderClassName", job.builderClassName, annotationNode))
        return;
    // <C, B> are the generics for our builder.
    String classGenericName = "C";
    String builderGenericName = "B";
    // We have to make sure that the generics' names do not collide with any generics on the annotated class,
    // the classname itself, or any member type name of the annotated class.
    // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder.
    java.util.HashSet<String> usedNames = gatherUsedTypeNames(job.typeParams, td);
    classGenericName = generateNonclashingNameFor(classGenericName, usedNames);
    builderGenericName = generateNonclashingNameFor(builderGenericName, usedNames);
    JavacTreeMaker maker = annotationNode.getTreeMaker();
    {
        JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, parent, job.typeParams);
        JCTypeParameter c = maker.TypeParameter(parent.toName(classGenericName), List.<JCExpression>of(annotatedClass));
        ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode);
        typeParamsForBuilder.append(maker.Ident(parent.toName(classGenericName)));
        typeParamsForBuilder.append(maker.Ident(parent.toName(builderGenericName)));
        JCTypeApply typeApply = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, parent, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), typeParamsForBuilder.toList());
        JCTypeParameter d = maker.TypeParameter(parent.toName(builderGenericName), List.<JCExpression>of(typeApply));
        if (job.typeParams == null || job.typeParams.isEmpty()) {
            job.builderTypeParams_ = List.of(c, d);
        } else {
            job.builderTypeParams_ = job.typeParams.append(c).append(d);
        }
    }
    JCTree extendsClause = Javac.getExtendsClause(td);
    JCExpression superclassBuilderClass = 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 superclassName = ((JCFieldAccess) extendsClause).getIdentifier();
        String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null);
        String superclassBuilderClassName = job.replaceBuilderClassName(superclassName.toString(), builderClassNameTemplate);
        superclassBuilderClass = parent.getTreeMaker().Select((JCFieldAccess) extendsClause, parent.toName(superclassBuilderClassName));
    } else if (extendsClause != null) {
        String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null);
        String superclassBuilderClassName = job.replaceBuilderClassName(extendsClause.toString(), builderClassNameTemplate);
        superclassBuilderClass = chainDots(parent, extendsClause.toString(), superclassBuilderClassName);
    }
    // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
    for (BuilderFieldData bfd : job.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;
            }
        }
    }
    job.builderAbstractClassName = job.builderClassName = job.replaceBuilderClassName(td.name);
    job.builderImplClassName = job.builderAbstractClassName + "Impl";
    // Create the abstract builder class.
    job.builderAbstractType = findInnerClass(parent, job.builderClassName);
    if (job.builderAbstractType == null) {
        job.builderAbstractType = generateBuilderAbstractClass(job, superclassBuilderClass, superclassTypeParams, classGenericName, builderGenericName);
        recursiveSetGeneratedBy(job.builderAbstractType.get(), annotationNode);
    } else {
        JCClassDecl builderTypeDeclaration = (JCClassDecl) job.builderAbstractType.get();
        if (!builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) || !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
            annotationNode.addError("Existing Builder must be an abstract static inner class.");
            return;
        }
        sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderAbstractType, annotationNode);
        // Generate errors for @Singular BFDs that have one already defined node.
        for (BuilderFieldData bfd : job.builderFields) {
            SingularData sd = bfd.singularData;
            if (sd == null)
                continue;
            JavacSingularizer singularizer = sd.getSingularizer();
            if (singularizer == null)
                continue;
            if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) {
                bfd.singularData = null;
            }
        }
    }
    // Generate the fields in the abstract builder class that hold the values for the instance.
    job.setBuilderToAbstract();
    generateBuilderFields(job.builderType, job.builderFields, annotationNode);
    if (addCleaning) {
        JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), job.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
        recursiveSetGeneratedBy(uncleanField, annotationNode);
        injectFieldAndMarkGenerated(job.builderType, uncleanField);
    }
    if (job.toBuilder) {
        // Generate $fillValuesFrom() method in the abstract builder.
        JCMethodDecl fvm = generateFillValuesMethod(job, superclassBuilderClass != null, builderGenericName, classGenericName);
        recursiveSetGeneratedBy(fvm, annotationNode);
        injectMethod(job.builderType, fvm);
        // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
        JCMethodDecl sfvm = generateStaticFillValuesMethod(job, annInstance.setterPrefix());
        recursiveSetGeneratedBy(sfvm, annotationNode);
        injectMethod(job.builderType, sfvm);
    }
    // Generate abstract self() and build() methods in the abstract builder.
    JCMethodDecl asm = generateAbstractSelfMethod(job, superclassBuilderClass != null, builderGenericName);
    recursiveSetGeneratedBy(asm, annotationNode);
    injectMethod(job.builderType, asm);
    JCMethodDecl abm = generateAbstractBuildMethod(job, superclassBuilderClass != null, classGenericName);
    recursiveSetGeneratedBy(abm, annotationNode);
    injectMethod(job.builderType, abm);
    // Create the setter methods in the abstract builder.
    for (BuilderFieldData bfd : job.builderFields) {
        generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix());
    }
    // 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 : job.builderFields) {
        for (JavacNode f : bfd.createdFields) {
            fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true, false));
        }
    }
    // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
    JCMethodDecl toStringMethod = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, FieldAccess.ALWAYS_FIELD, annotationNode);
    if (toStringMethod != null)
        injectMethod(job.builderType, toStringMethod);
    // If clean methods are requested, add them now.
    if (addCleaning) {
        JCMethodDecl md = generateCleanMethod(job.builderFields, job.builderType, annotationNode);
        recursiveSetGeneratedBy(md, annotationNode);
        injectMethod(job.builderType, md);
    }
    boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0;
    if (!isAbstract) {
        // Only non-abstract classes get the Builder implementation.
        // Create the builder implementation class.
        job.builderImplType = findInnerClass(parent, job.builderImplClassName);
        if (job.builderImplType == null) {
            job.builderImplType = generateBuilderImplClass(job);
            recursiveSetGeneratedBy(job.builderImplType.get(), annotationNode);
        } else {
            JCClassDecl builderImplTypeDeclaration = (JCClassDecl) job.builderImplType.get();
            if (!builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) || builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
                annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class.");
                return;
            }
            sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderImplType, annotationNode);
        }
        // Create a simple constructor for the BuilderImpl class.
        JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), job.builderImplType, List.<JavacNode>nil(), false, annotationNode);
        if (cd != null)
            injectMethod(job.builderImplType, cd);
        job.setBuilderToImpl();
        // Create the self() and build() methods in the BuilderImpl.
        JCMethodDecl selfMethod = generateSelfMethod(job);
        recursiveSetGeneratedBy(selfMethod, annotationNode);
        injectMethod(job.builderType, selfMethod);
        if (methodExists(job.buildMethodName, job.builderType, -1) == MemberExistsResult.NOT_EXISTS) {
            JCMethodDecl buildMethod = generateBuildMethod(job, buildMethodThrownExceptions);
            recursiveSetGeneratedBy(buildMethod, annotationNode);
            injectMethod(job.builderType, buildMethod);
        }
    }
    // Generate a constructor in the annotated class that takes a builder as argument.
    if (!constructorExists(job.parentType, job.builderAbstractClassName)) {
        job.setBuilderToAbstract();
        generateBuilderBasedConstructor(job, superclassBuilderClass != null);
    }
    if (!isAbstract) {
        // Allow users to specify their own builder() methods, e.g., to provide default values.
        if (generateBuilderMethod && methodExists(job.builderMethodName, job.parentType, -1) != MemberExistsResult.NOT_EXISTS)
            generateBuilderMethod = false;
        if (generateBuilderMethod) {
            JCMethodDecl builderMethod = generateBuilderMethod(job);
            if (builderMethod != null) {
                recursiveSetGeneratedBy(builderMethod, annotationNode);
                injectMethod(job.parentType, builderMethod);
            }
        }
        // Add the toBuilder() method to the annotated class.
        if (job.toBuilder) {
            switch(methodExists(TO_BUILDER_METHOD_NAME, job.parentType, 0)) {
                case EXISTS_BY_USER:
                    break;
                case NOT_EXISTS:
                    JCMethodDecl md = generateToBuilderMethod(job);
                    if (md != null) {
                        recursiveSetGeneratedBy(md, annotationNode);
                        injectMethod(job.parentType, md);
                    }
                    break;
                default:
            }
        }
    }
    if (nonFinalNonDefaultedFields != null && generateBuilderMethod) {
        for (JavacNode fieldNode : nonFinalNonDefaultedFields) {
            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.");
        }
    }
}
Also used : BuilderFieldData(lombok.javac.handlers.HandleBuilder.BuilderFieldData) SuperBuilder(lombok.experimental.SuperBuilder) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) HandleBuilder(lombok.javac.handlers.HandleBuilder) Builder(lombok.Builder) SuperBuilder(lombok.experimental.SuperBuilder) ListBuffer(com.sun.tools.javac.util.ListBuffer) ArrayList(java.util.ArrayList) JCTypeApply(com.sun.tools.javac.tree.JCTree.JCTypeApply) JavacSingularizer(lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer) ToString(lombok.ToString) Name(com.sun.tools.javac.util.Name) JavacNode(lombok.javac.JavacNode) JCClassDecl(com.sun.tools.javac.tree.JCTree.JCClassDecl) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) JavacTreeMaker(lombok.javac.JavacTreeMaker) Included(lombok.core.handlers.InclusionExclusionUtils.Included) JCTree(com.sun.tools.javac.tree.JCTree) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) JCTypeParameter(com.sun.tools.javac.tree.JCTree.JCTypeParameter) JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) SingularData(lombok.javac.handlers.JavacSingularsRecipes.SingularData) ToString(lombok.ToString)

Example 4 with BuilderFieldData

use of lombok.javac.handlers.HandleBuilder.BuilderFieldData in project lombok by rzwitserloot.

the class HandleSuperBuilder method generateBuilderFields.

private void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JavacNode 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);
    }
    java.util.List<JCVariableDecl> generated = new ArrayList<JCVariableDecl>();
    for (int i = len - 1; i >= 0; i--) {
        BuilderFieldData bfd = builderFields.get(i);
        if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
            java.util.List<JavacNode> fields = bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source);
            for (JavacNode field : fields) {
                generated.add((JCVariableDecl) field.get());
            }
            bfd.createdFields.addAll(fields);
        } else {
            JavacNode field = null, setFlag = null;
            for (JavacNode exists : existing) {
                Name n = ((JCVariableDecl) exists.get()).name;
                if (n.equals(bfd.builderFieldName))
                    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.builderFieldName, cloneType(maker, bfd.type, source), null);
                field = injectFieldAndMarkGenerated(builderType, newField);
                generated.add(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);
                generated.add(newField);
            }
            bfd.createdFields.add(field);
        }
    }
    for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source);
}
Also used : BuilderFieldData(lombok.javac.handlers.HandleBuilder.BuilderFieldData) JavacTreeMaker(lombok.javac.JavacTreeMaker) JavacNode(lombok.javac.JavacNode) JCModifiers(com.sun.tools.javac.tree.JCTree.JCModifiers) ArrayList(java.util.ArrayList) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) Name(com.sun.tools.javac.util.Name)

Example 5 with BuilderFieldData

use of lombok.javac.handlers.HandleBuilder.BuilderFieldData in project lombok by rzwitserloot.

the class HandleSuperBuilder method generateBuilderBasedConstructor.

/**
 * 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 callBuilderBasedSuperConstructor
 *            If {@code true}, the constructor will explicitly call a super
 *            constructor with the builder as argument. Requires
 *            {@code builderClassAsParameter != null}.
 */
private void generateBuilderBasedConstructor(SuperBuilderJob job, boolean callBuilderBasedSuperConstructor) {
    JavacTreeMaker maker = job.getTreeMaker();
    AccessLevel level = AccessLevel.PROTECTED;
    ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
    Name builderVariableName = job.toName(BUILDER_VARIABLE_NAME);
    for (BuilderFieldData bfd : job.builderFields) {
        JCExpression rhs;
        if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
            bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, job.sourceNode, statements, bfd.builderFieldName, "b");
            rhs = maker.Ident(bfd.singularData.getPluralName());
        } else {
            rhs = maker.Select(maker.Ident(builderVariableName), bfd.builderFieldName);
        }
        JCFieldAccess fieldInThis = maker.Select(maker.Ident(job.toName("this")), bfd.rawName);
        JCStatement assign = maker.Exec(maker.Assign(fieldInThis, rhs));
        // In case of @Builder.Default, set the value to the default if it was not set in the builder.
        if (bfd.nameOfSetFlag != null) {
            JCFieldAccess setField = maker.Select(maker.Ident(builderVariableName), bfd.nameOfSetFlag);
            fieldInThis = maker.Select(maker.Ident(job.toName("this")), bfd.rawName);
            JCExpression parentTypeRef = namePlusTypeParamsToTypeReference(maker, job.parentType, List.<JCTypeParameter>nil());
            JCAssign assignDefault = maker.Assign(fieldInThis, maker.Apply(typeParameterNames(maker, ((JCClassDecl) job.parentType.get()).typarams), maker.Select(parentTypeRef, bfd.nameOfDefaultProvider), List.<JCExpression>nil()));
            statements.append(maker.If(setField, assign, maker.Exec(assignDefault)));
        } else {
            statements.append(assign);
        }
        if (hasNonNullAnnotations(bfd.originalFieldNode)) {
            JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, job.sourceNode);
            if (nullCheck != null)
                statements.append(nullCheck);
        }
    }
    List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil();
    JCModifiers mods = maker.Modifiers(toJavacModifier(level), annsOnMethod);
    // Create a constructor that has just the builder as parameter.
    ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
    long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, job.getContext());
    // First add all generics that are present on the parent type.
    ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(job.typeParams, maker, job.sourceNode);
    // Now add the <?, ?>.
    JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
    typeParamsForBuilderParameter.append(wildcard);
    wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
    typeParamsForBuilderParameter.append(wildcard);
    JCTypeApply paramType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), 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(job.toName("super")), List.<JCExpression>of(maker.Ident(builderVariableName)));
        statements.prepend(maker.Exec(callToSuperConstructor));
    }
    JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, job.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, statements.toList()), null), job.sourceNode);
    injectMethod(job.parentType, constr);
}
Also used : JCWildcard(com.sun.tools.javac.tree.JCTree.JCWildcard) BuilderFieldData(lombok.javac.handlers.HandleBuilder.BuilderFieldData) JavacTreeMaker(lombok.javac.JavacTreeMaker) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) JCAssign(com.sun.tools.javac.tree.JCTree.JCAssign) ListBuffer(com.sun.tools.javac.util.ListBuffer) JCTypeApply(com.sun.tools.javac.tree.JCTree.JCTypeApply) JCStatement(com.sun.tools.javac.tree.JCTree.JCStatement) AccessLevel(lombok.AccessLevel) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) Name(com.sun.tools.javac.util.Name) JCTypeParameter(com.sun.tools.javac.tree.JCTree.JCTypeParameter) JCMethodInvocation(com.sun.tools.javac.tree.JCTree.JCMethodInvocation) JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) JCModifiers(com.sun.tools.javac.tree.JCTree.JCModifiers) JCAnnotation(com.sun.tools.javac.tree.JCTree.JCAnnotation)

Aggregations

JavacTreeMaker (lombok.javac.JavacTreeMaker)5 BuilderFieldData (lombok.javac.handlers.HandleBuilder.BuilderFieldData)5 JCVariableDecl (com.sun.tools.javac.tree.JCTree.JCVariableDecl)4 ListBuffer (com.sun.tools.javac.util.ListBuffer)4 Name (com.sun.tools.javac.util.Name)4 JCExpression (com.sun.tools.javac.tree.JCTree.JCExpression)3 JCModifiers (com.sun.tools.javac.tree.JCTree.JCModifiers)3 JCStatement (com.sun.tools.javac.tree.JCTree.JCStatement)3 JCTypeApply (com.sun.tools.javac.tree.JCTree.JCTypeApply)3 JCTypeParameter (com.sun.tools.javac.tree.JCTree.JCTypeParameter)3 JCAnnotation (com.sun.tools.javac.tree.JCTree.JCAnnotation)2 JCBlock (com.sun.tools.javac.tree.JCTree.JCBlock)2 JCFieldAccess (com.sun.tools.javac.tree.JCTree.JCFieldAccess)2 JCMethodDecl (com.sun.tools.javac.tree.JCTree.JCMethodDecl)2 JCWildcard (com.sun.tools.javac.tree.JCTree.JCWildcard)2 ArrayList (java.util.ArrayList)2 JavacNode (lombok.javac.JavacNode)2 JCTree (com.sun.tools.javac.tree.JCTree)1 JCAssign (com.sun.tools.javac.tree.JCTree.JCAssign)1 JCClassDecl (com.sun.tools.javac.tree.JCTree.JCClassDecl)1