From 19ad4fd57d32afad1a33f20613fbb2e7607cfee0 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 13 Jun 2018 23:21:00 +0200 Subject: [SuperBuilder] code review, style update, making it JDK1.6 compatible, documentation review, bugfix for setter auto-presuming chaining even when you didn’t configure it to chain. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eclipse/handlers/EclipseSingularsRecipes.java | 45 +-- src/core/lombok/eclipse/handlers/HandleSetter.java | 2 +- .../eclipse/handlers/HandleSuperBuilder.java | 322 +++++++++------------ .../singulars/EclipseGuavaSingularizer.java | 14 +- .../EclipseJavaUtilListSetSingularizer.java | 16 +- .../singulars/EclipseJavaUtilMapSingularizer.java | 16 +- src/core/lombok/javac/handlers/HandleSetter.java | 17 +- .../lombok/javac/handlers/HandleSuperBuilder.java | 312 +++++++++----------- .../javac/handlers/JavacSingularsRecipes.java | 43 +-- .../handlers/singulars/JavacGuavaSingularizer.java | 13 +- .../JavacJavaUtilListSetSingularizer.java | 15 +- .../singulars/JavacJavaUtilMapSingularizer.java | 15 +- .../features/experimental/SuperBuilder.html | 11 +- website/templates/features/experimental/index.html | 2 +- 14 files changed, 379 insertions(+), 464 deletions(-) diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index e24cd5b5..b9b9e07d 100644 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.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,7 +30,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -65,6 +64,14 @@ import lombok.core.TypeLibrary; import lombok.eclipse.EclipseNode; public class EclipseSingularsRecipes { + public interface TypeReferenceMaker { + TypeReference make(); + } + + public interface StatementMaker { + Statement make(); + } + private static final EclipseSingularsRecipes INSTANCE = new EclipseSingularsRecipes(); private final Map singularizers = new HashMap(); private final TypeLibrary singularizableTypes = new TypeLibrary(); @@ -220,37 +227,35 @@ public class EclipseSingularsRecipes { } public abstract List generateFields(SingularData data, EclipseNode builderType); - + /** - * Generates the singular, plural, and clear methods for the given - * {@link SingularData}.
- * Uses the given builderType as return type if - * chain == true, void otherwise. If you need more - * control over the return type and value, use - * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, Supplier, Supplier)}. + * 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, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}. */ public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain) { - // TODO: Make these lambdas when switching to a source level >= 1.8. - Supplier returnType = new Supplier() { - @Override public TypeReference get() { + TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { + @Override public TypeReference make() { return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); } }; - Supplier returnStatement = new Supplier() { - @Override public ReturnStatement get() { + + StatementMaker returnStatementMaker = new StatementMaker() { + @Override public ReturnStatement make() { return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; } }; - generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); + + generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker); } /** - * Generates the singular, plural, and clear methods for the given - * {@link SingularData}.
- * Uses the given returnType and returnStatement for the generated methods. + * 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, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement); - + public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker); + public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName, String builderVariable); public boolean requiresCleaning() { diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 434939a6..92489c09 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -212,7 +212,7 @@ public class HandleSetter extends EclipseAnnotationHandler { return createSetter(parent, deprecate, fieldNode, name, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam); } - static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, TypeReference returnType, ReturnStatement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { + static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, TypeReference returnType, Statement returnStatement, int modifier, EclipseNode sourceNode, List onMethod, List onParam) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); ASTNode source = sourceNode.get(); int pS = source.sourceStart, pE = source.sourceEnd; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 1100cbf0..12fc4761 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -85,20 +84,21 @@ import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; +import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; @ProviderFor(EclipseAnnotationHandler.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 EclipseAnnotationHandler { - private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); private static final char[] SET_PREFIX = "$set".toCharArray(); private static final char[] SELF_METHOD_NAME = "self".toCharArray(); - + private static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; - + private static class BuilderFieldData { TypeReference type; char[] rawName; @@ -108,49 +108,41 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { ObtainVia obtainVia; EclipseNode obtainViaNode; EclipseNode originalFieldNode; - + List createdFields = new ArrayList(); } - + @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); long p = (long) ast.sourceStart << 32 | ast.sourceEnd; - + 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; - } - + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + + if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; + EclipseNode tdParent = annotationNode.up(); - + java.util.List builderFields = new ArrayList(); TypeReference returnType; TypeParameter[] typeParams; - + boolean addCleaning = false; - + if (!(tdParent.get() instanceof TypeDeclaration)) { annotationNode.addError("@SuperBuilder is only supported on types."); return; } TypeDeclaration td = (TypeDeclaration) tdParent.get(); - + // Gather all fields of the class that should be set by the builder. List allFields = new ArrayList(); boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); @@ -158,31 +150,31 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); - + BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; bfd.singularData = getSingularData(fieldNode, ast); bfd.originalFieldNode = fieldNode; - + if (bfd.singularData != null && isDefault != null) { isDefault.addError("@Builder.Default and @Singular cannot be mixed."); isDefault = null; } - + if (fd.initialization == null && isDefault != null) { isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); isDefault = null; } - + if (fd.initialization != null && isDefault == null) { if (isFinal) { continue; } fieldNode.addWarning("@Builder 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 = prefixWith(bfd.name, SET_PREFIX); // The @Builder annotation removes the initializing expression on the field and moves @@ -199,26 +191,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builderFields.add(bfd); allFields.add(fieldNode); } - + // Set the names of the builder classes. String builderClassName = String.valueOf(td.name) + "Builder"; String builderImplClassName = builderClassName + "Impl"; typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p); - + // 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 on the annotated class, use "C2" and "B3" for our builder. java.util.List typeParamStrings = new ArrayList(); - for (TypeParameter typeParam : typeParams) { - typeParamStrings.add(typeParam.toString()); - } + for (TypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.toString()); classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); - + TypeReference extendsClause = td.superclass; TypeReference superclassBuilderClass = null; TypeReference[] typeArguments = new TypeReference[] { @@ -229,12 +219,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause; String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken()); String superclassBuilderClassName = superclassClassName + "Builder"; - + char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1); tokens[tokens.length] = superclassBuilderClassName.toCharArray(); long[] poss = new long[tokens.length]; Arrays.fill(poss, p); - + TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause); // Every token may potentially have type args. Here, we only have @@ -246,7 +236,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } else if (extendsClause != null) { String superClass = String.valueOf(extendsClause.getTypeName()[0]); String superclassBuilderClassName = superClass + "Builder"; - + char[][] tokens = new char[][] {superClass.toCharArray(), superclassBuilderClassName.toCharArray()}; long[] poss = new long[tokens.length]; Arrays.fill(poss, p); @@ -262,21 +252,20 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. - + generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClass != null); - + superclassBuilderClass != null); + // Create the abstract builder class. EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, - typeParams, ast, classGenericName, builderGenericName); - + typeParams, 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) { @@ -296,7 +285,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } } - + // Generate the fields in the abstract builder class that hold the values for the instance. generateBuilderFields(builderType, builderFields, ast); if (addCleaning) { @@ -306,16 +295,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); injectFieldAndMarkGenerated(builderType, cleanDecl); } - + // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast)); - + // 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. if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { java.util.List fieldNodes = new ArrayList(); @@ -328,16 +317,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { injectMethod(builderType, md); } } - - if (addCleaning) { - injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); - } - + + if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) { - // Abstract classes to not get the Builder implementation nor the builder() method. + // Only non-abstract classes get the Builder implementation. return; } - + // Create the builder implementation class. EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { @@ -346,30 +333,28 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); return; } - + // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType)); injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); - + // Add the builder() method to the annotated class. if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); - if (md != null) { - injectMethod(tdParent, md); - } + if (md != null) injectMethod(tdParent, md); } } - + private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass, TypeReference superclassBuilderClass, TypeParameter[] typeParams, ASTNode source, String classGenericName, String builderGenericName) { - + TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract; builder.name = builderClass.toCharArray(); - + // Keep any type params of the annotated class. builder.typeParameters = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2); // Add builder-specific type params required for inheritable builders. @@ -384,15 +369,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { TypeReference[] typerefs = appendBuilderTypeReferences(typeParams, classGenericName, builderGenericName); o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0); builder.typeParameters[builder.typeParameters.length - 1] = o; - + builder.superclass = copyType(superclassBuilderClass, source); - - builder.createDefaultConstructor(false, true); + builder.createDefaultConstructor(false, true); + builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } - + private EclipseNode generateBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); @@ -401,9 +386,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.name = builderImplClass.toCharArray(); // Add type params if there are any. - if (typeParams != null && typeParams.length > 0) { - builder.typeParameters = copyTypeParams(typeParams, source); - } + if (typeParams != null && typeParams.length > 0) builder.typeParameters = copyTypeParams(typeParams, source); + if (builderAbstractClass != null) { // Extend the abstract builder. // 1. Add any type params of the annotated class. @@ -423,7 +407,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); 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. @@ -441,14 +425,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { */ private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List builderFields, EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) { - + ASTNode source = sourceNode.get(); - + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); long p = (long) source.sourceStart << 32 | source.sourceEnd; - + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); - + constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED); constructor.selector = typeDeclaration.name; if (callBuilderBasedSuperConstructor) { @@ -468,17 +452,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p); constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)}; - + List statements = new ArrayList(); List nullChecks = new ArrayList(); - + for (BuilderFieldData fieldNode : builderFields) { char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode); FieldReference thisX = new FieldReference(fieldNode.rawName, p); int s = (int) (p >> 32); int e = (int) p; thisX.receiver = new ThisReference(s, e); - + Expression assignmentExpr; if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b"); @@ -507,51 +491,48 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } } - + nullChecks.addAll(statements); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); - + injectMethod(typeNode, constructor); } private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; - + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.selector = builderMethodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - + // Add type params if there are any. - if (typeParams != null && typeParams.length > 0) { - out.typeParameters = copyTypeParams(typeParams, source); - } + if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source); + TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) }; out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p); AllocationExpression invoke = new AllocationExpression(); invoke.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; - + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } - + private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; - if (override) { - out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; - } + if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); return out; } - + private MethodDeclaration generateSelfMethod(EclipseNode builderImplType) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; @@ -562,35 +543,33 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)}; return out; } - + private MethodDeclaration generateAbstractBuildMethod(EclipseNode tdParent, String methodName, boolean override, String classGenericName, ASTNode source) { + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - + out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; out.selector = methodName.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0); - if (override) { - out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; - } + if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } - - private MethodDeclaration generateBuildMethod(EclipseNode tdParent, String name, TypeReference returnType, ASTNode source) { + private MethodDeclaration generateBuildMethod(EclipseNode tdParent, String name, TypeReference returnType, ASTNode source) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); - + out.modifiers = ClassFileConstants.AccPublic; out.selector = name.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = returnType; out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)}; - + AllocationExpression allocationStatement = new AllocationExpression(); allocationStatement.type = copyType(out.returnType); // Use a constructor that only has this builder as parameter. @@ -600,16 +579,16 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } - + private MethodDeclaration generateCleanMethod(List builderFields, EclipseNode builderType, ASTNode source) { List statements = new ArrayList(); - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements); } } - + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0)); @@ -622,15 +601,13 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return decl; } - + private void generateBuilderFields(EclipseNode builderType, List builderFields, ASTNode source) { List existing = new ArrayList(); for (EclipseNode child : builderType.down()) { - if (child.getKind() == Kind.FIELD) { - existing.add(child); - } + if (child.getKind() == Kind.FIELD) existing.add(child); } - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); @@ -638,14 +615,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { EclipseNode field = null, setFlag = null; for (EclipseNode exists : existing) { char[] n = ((FieldDeclaration) exists.get()).name; - if (Arrays.equals(n, bfd.name)) { - field = exists; - } - if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) { - setFlag = exists; - } + if (Arrays.equals(n, bfd.name)) field = exists; + if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists; } - + if (field == null) { FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -666,70 +639,63 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } } - + private void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); - - // TODO: Make these lambdas when switching to a source level >= 1.8. - Supplier returnType = new Supplier() { - @Override public TypeReference get() { + + TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { + @Override public TypeReference make() { return new SingleTypeReference(builderGenericName.toCharArray(), 0); } }; - Supplier returnStatement = new Supplier() { - @Override public ReturnStatement get() { + + StatementMaker returnStatementMaker = new StatementMaker() { + @Override public ReturnStatement make() { MessageSend returnCall = new MessageSend(); returnCall.receiver = ThisReference.implicitThis(); returnCall.selector = SELF_METHOD_NAME; return new ReturnStatement(returnCall, 0, 0); } }; + if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnType.get(), returnStatement.get(), sourceNode); + generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode); } else { - bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnType, returnStatement); + bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker); } } - private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, ReturnStatement returnStatement, EclipseNode sourceNode) { + private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; - if (existing == null) { - existing = EMPTY_METHODS; - } + if (existing == null) existing = EMPTY_METHODS; int len = existing.length; FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); char[] name = fd.name; - + for (int i = 0; i < len; i++) { - if (!(existing[i] instanceof MethodDeclaration)) { - continue; - } + if (!(existing[i] instanceof MethodDeclaration)) continue; char[] existingName = existing[i].selector; - if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) { - return; - } + if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return; } - + String setterName = fieldNode.getName(); - + MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, sourceNode, Collections.emptyList(), Collections.emptyList()); injectMethod(builderType, setter); } - + private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { for (EclipseNode child : node.down()) { - if (!annotationTypeMatches(ObtainVia.class, child)) { - continue; - } + if (!annotationTypeMatches(ObtainVia.class, child)) continue; AnnotationValues ann = createAnnotation(ObtainVia.class, child); bfd.obtainVia = ann.getInstance(); bfd.obtainViaNode = child; return; } } - + /** * Returns the explicitly requested singular annotation on this node (field * or parameter), or null if there's no {@code @Singular} annotation on it. @@ -738,9 +704,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { */ private SingularData getSingularData(EclipseNode node, ASTNode source) { for (EclipseNode child : node.down()) { - if (!annotationTypeMatches(Singular.class, child)) { - continue; - } + if (!annotationTypeMatches(Singular.class, child)) continue; + char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); String explicitSingular = ann.getInstance().value(); @@ -757,7 +722,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } } char[] singularName = explicitSingular.toCharArray(); - + TypeReference type = ((AbstractVariableDeclaration) node.get()).type; TypeReference[] typeArgs = null; String typeName; @@ -766,59 +731,51 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { typeName = new String(((ParameterizedSingleTypeReference) type).token); } else if (type instanceof ParameterizedQualifiedTypeReference) { TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments; - if (tr != null) { - typeArgs = tr[tr.length - 1]; - } + if (tr != null) typeArgs = tr[tr.length - 1]; char[][] tokens = ((ParameterizedQualifiedTypeReference) type).tokens; StringBuilder sb = new StringBuilder(); for (int i = 0; i < tokens.length; i++) { - if (i > 0) { - sb.append("."); - } + if (i > 0) sb.append("."); sb.append(tokens[i]); } typeName = sb.toString(); } else { typeName = type.toString(); } - + String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName); EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn); if (singularizer == null) { node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated."); return null; } - + return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source); } - + return null; } - + private String generateNonclashingNameFor(String classGenericName, java.util.List typeParamStrings) { - if (!typeParamStrings.contains(classGenericName)) { - return classGenericName; - } + if (!typeParamStrings.contains(classGenericName)) return classGenericName; int counter = 2; - while (typeParamStrings.contains(classGenericName + counter)) { - counter++; - } + while (typeParamStrings.contains(classGenericName + counter)) counter++; return classGenericName + counter; } - + private TypeReference[] appendBuilderTypeReferences(TypeParameter[] typeParams, String classGenericName, String builderGenericName) { TypeReference[] typeReferencesToAppend = new TypeReference[2]; typeReferencesToAppend[typeReferencesToAppend.length - 2] = new SingleTypeReference(classGenericName.toCharArray(), 0); typeReferencesToAppend[typeReferencesToAppend.length - 1] = new SingleTypeReference(builderGenericName.toCharArray(), 0); return mergeToTypeReferences(typeParams, typeReferencesToAppend); } - + private TypeReference[] getTypeParametersFrom(TypeReference typeRef) { TypeReference[][] typeArgss = null; if (typeRef instanceof ParameterizedQualifiedTypeReference) { - typeArgss = ((ParameterizedQualifiedTypeReference)typeRef).typeArguments; + typeArgss = ((ParameterizedQualifiedTypeReference) typeRef).typeArguments; } else if (typeRef instanceof ParameterizedSingleTypeReference) { - typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference)typeRef).typeArguments}; + typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference) typeRef).typeArguments}; } TypeReference[] typeArgs = new TypeReference[0]; if (typeArgss != null && typeArgss.length > 0) { @@ -826,7 +783,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } return typeArgs; } - + private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) { if (typeParams.length > 0) { TypeReference[] typerefs = new TypeReference[typeParams.length]; @@ -837,7 +794,6 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } else { return new SingleTypeReference(referenceName.toCharArray(), 0); } - } private TypeReference[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) { @@ -850,32 +806,24 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { } return typerefs; } - + private TypeReference[] mergeTypeReferences(TypeReference[] refs1, TypeReference[] refs2) { TypeReference[] result = new TypeReference[refs1.length + refs2.length]; - for (int i = 0; i < refs1.length; i++) { - result[i] = refs1[i]; - } - for (int i = 0; i < refs2.length; i++) { - result[refs1.length + i] = refs2[i]; - } + for (int i = 0; i < refs1.length; i++) result[i] = refs1[i]; + for (int i = 0; i < refs2.length; i++) result[refs1.length + i] = refs2[i]; return result; } - + private EclipseNode findInnerClass(EclipseNode parent, String name) { char[] c = name.toCharArray(); for (EclipseNode child : parent.down()) { - if (child.getKind() != Kind.TYPE) { - continue; - } + if (child.getKind() != Kind.TYPE) continue; TypeDeclaration td = (TypeDeclaration) child.get(); - if (Arrays.equals(td.name, c)) { - return child; - } + if (Arrays.equals(td.name, c)) return child; } return null; } - + private static final char[] prefixWith(char[] prefix, char[] name) { char[] out = new char[prefix.length + name.length]; System.arraycopy(prefix, 0, out, 0, prefix.length); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index d6c63944..f1687c9c 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.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 @@ -27,7 +27,6 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; @@ -35,6 +34,8 @@ import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; +import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; @@ -53,7 +54,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -97,10 +97,10 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) { - generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generateClearMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType); + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) { + generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType); } void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 0851757f..5f86a4dc 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.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 @@ -27,11 +27,12 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; +import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; @@ -46,7 +47,6 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -88,15 +88,15 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker); return; } - generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generateClearMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType); + generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType); } private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 976d9265..69c2186a 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.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 @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; @@ -46,7 +45,6 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -59,6 +57,8 @@ import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; +import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; @ProviderFor(EclipseSingularizer.class) public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer { @@ -133,15 +133,15 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnType, returnStatement); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker); return; } - generateSingularMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generatePluralMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType, fluent); - generateClearMethod(deprecate, returnType.get(), returnStatement.get(), data, builderType); + generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent); + generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType); } private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 50967121..be3337db 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -210,12 +210,17 @@ public class HandleSetter extends JavacAnnotationHandler { } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List onMethod, List onParam) { - JCExpression returnType = cloneSelfType(field); - JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + 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, JCReturn returnStatement, JavacNode source, List onMethod, List onParam) { + + public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List onMethod, List onParam) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -252,9 +257,7 @@ public class HandleSetter extends JavacAnnotationHandler { returnStatement = null; } - if (returnStatement != null) { - statements.append(returnStatement); - } + if (returnStatement != null) statements.append(returnStatement); JCBlock methodBody = treeMaker.Block(0, statements.toList()); List methodGenericParams = List.nil(); diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 1835bd48..1707fb0e 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -26,7 +26,6 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; -import java.util.function.Supplier; import org.mangosdk.spi.ProviderFor; @@ -43,7 +42,6 @@ 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.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; @@ -70,15 +68,16 @@ import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; 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 { - private static final String SELF_METHOD = "self"; - + private static class BuilderFieldData { JCExpression type; Name rawName; @@ -88,49 +87,41 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { ObtainVia obtainVia; JavacNode obtainViaNode; JavacNode originalFieldNode; - + java.util.List createdFields = new ArrayList(); } - + @Override public void handle(AnnotationValues 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; - } - + + 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 builderFields = new ArrayList(); JCExpression returnType; List typeParams = List.nil(); List thrownExceptions = List.nil(); List 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 allFields = new ListBuffer(); @@ -145,24 +136,22 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nameOfSetFlag = tdParent.toName(bfd.name + "$set"); // The @Builder annotation removes the initializing expression on the field and moves @@ -179,7 +168,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { builderFields.add(bfd); allFields.append(fieldNode); } - + // Set the names of the builder classes. String builderClassName = td.name.toString() + "Builder"; String builderImplClassName = builderClassName + "Impl"; @@ -187,14 +176,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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(); + superclassTypeParams = ((JCTypeApply) extendsClause).getTypeArguments(); // A class name with a generics type, e.g., "Superclass". - extendsClause = ((JCTypeApply)extendsClause).getType(); + 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"; @@ -202,24 +191,22 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } // 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; - + // 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 on the annotated class, use "C2" and "B3" for our builder. java.util.List typeParamStrings = new ArrayList(); - for (JCTypeParameter typeParam : typeParams) { - typeParamStrings.add(typeParam.getName().toString()); - } + 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) { @@ -239,7 +226,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } } - + // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { @@ -249,7 +236,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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) { @@ -257,37 +244,32 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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 fieldNodes = new ArrayList(); - for (BuilderFieldData bfd : builderFields) { - fieldNodes.addAll(bfd.createdFields); - } + for (BuilderFieldData bfd : builderFields) fieldNodes.addAll(bfd.createdFields); + // 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 (toStringMethod != null) injectMethod(builderType, toStringMethod); + // If clean methods are requested, add them now. - if (addCleaning) { - injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); - } + 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) { @@ -296,48 +278,45 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), builderImplType, List.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)); - + 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); - } + 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 typeParams, List 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 allTypeParams = new ListBuffer(); allTypeParams.addAll(copyTypeParams(source, typeParams)); @@ -356,7 +335,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList()); allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.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. @@ -367,18 +346,18 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList()); } - + JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.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 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. @@ -400,7 +379,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), List.nil()); return injectType(tdParent, builder); } @@ -422,22 +401,20 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { */ private void generateBuilderBasedConstructor(JavacNode typeNode, List typeParams, java.util.List builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { JavacTreeMaker maker = typeNode.getTreeMaker(); - + AccessLevel level = AccessLevel.PROTECTED; - + ListBuffer nullChecks = new ListBuffer(); ListBuffer statements = new ListBuffer(); - + Name builderVariableName = typeNode.toName("b"); for (BuilderFieldData bfd : builderFields) { List nonNulls = findAnnotations(bfd.originalFieldNode, NON_NULL_PATTERN); if (!nonNulls.isEmpty()) { JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, source); - if (nullCheck != null) { - nullChecks.append(nullCheck); - } + 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"); @@ -446,7 +423,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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. @@ -458,9 +435,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { statements.append(assign); } } - + JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.nil()); - + // Create a constructor that has just the builder as parameter. ListBuffer params = new ListBuffer(); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); @@ -475,7 +452,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), @@ -483,29 +460,27 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { List.of(maker.Ident(builderVariableName))); statements.prepend(maker.Exec(callToSuperConstructor)); } - + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName(""), null, List.nil(), params.toList(), List.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 typeParams) { JavacTreeMaker maker = type.getTreeMaker(); - + ListBuffer typeArgs = new ListBuffer(); - for (JCTypeParameter typeParam : typeParams) { - typeArgs.append(maker.Ident(typeParam.name)); - } - + for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name)); + JCExpression call = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.nil(), null); JCStatement statement = maker.Return(call); - + JCBlock body = maker.Block(0, List.of(statement)); int modifiers = Flags.PUBLIC; modifiers |= Flags.STATIC; - + // Add any type params of the annotated class to the return type. ListBuffer typeParameterNames = new ListBuffer(); typeParameterNames.addAll(typeParameterNames(maker, typeParams)); @@ -514,10 +489,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), List.nil(), body, null); } - + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); @@ -528,24 +503,24 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), List.nil(), List.nil(), null, null); } - + private JCMethodDecl generateSelfMethod(JavacNode builderImplType) { JavacTreeMaker maker = builderImplType.getTreeMaker(); - + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.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.of(statement)); - + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), body, null); } - + private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); @@ -556,53 +531,51 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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.nil(), List.nil(), List.nil(), null, null); } private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List thrownExceptions) { JavacTreeMaker maker = type.getTreeMaker(); - + JCExpression call; ListBuffer statements = new ListBuffer(); - + // Use a constructor that only has this builder as parameter. List builderArg = List.of(maker.Ident(type.toName("this"))); call = maker.NewClass(null, List.nil(), returnType, builderArg, null); statements.append(maker.Return(call)); - + JCBlock body = maker.Block(0, statements.toList()); - + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.nil()); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); - + return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); } - + private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer statements = new ListBuffer(); - + 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.nil(), List.nil(), List.nil(), body, null); } - + private void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { int len = builderFields.size(); java.util.List existing = new ArrayList(); 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) { @@ -611,12 +584,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { 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) { @@ -633,52 +602,47 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } } - + 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. - Supplier returnType = new Supplier() { @Override public JCExpression get() { + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { return maker.Ident(builderType.toName(builderGenericName)); }}; - Supplier returnStatement = new Supplier() { @Override public JCReturn get() { + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { return maker.Return(maker.Apply(List.nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.nil())); }}; + if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(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, returnTypeMaker.make(), returnStatementMaker.make()); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnType, returnStatement); + 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, JCReturn returnStatement) { + + 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; - } + 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.nil(), List.nil()); - + injectMethod(builderType, newMethod); } - + 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 ann = createAnnotation(ObtainVia.class, child); bfd.obtainVia = ann.getInstance(); bfd.obtainViaNode = child; @@ -686,7 +650,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return; } } - + /** * Returns the explicitly requested singular annotation on this node (field * or parameter), or null if there's no {@code @Singular} annotation on it. @@ -695,9 +659,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { */ 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 ann = createAnnotation(Singular.class, child); deleteAnnotationIfNeccessary(child, Singular.class); @@ -715,58 +677,48 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { } } Name singularName = node.toName(explicitSingular); - + JCExpression type = null; - if (node.get() instanceof JCVariableDecl) { - type = ((JCVariableDecl) node.get()).vartype; - } - + if (node.get() instanceof JCVariableDecl) type = ((JCVariableDecl) node.get()).vartype; + String name = null; List 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 typeParamStrings) { - if (!typeParamStrings.contains(classGenericName)) { - return classGenericName; - } + if (!typeParamStrings.contains(classGenericName)) return classGenericName; int counter = 2; - while (typeParamStrings.contains(classGenericName + counter)) { - counter++; - } + 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; - } + if (child.getKind() != Kind.TYPE) continue; JCClassDecl td = (JCClassDecl) child.get(); - if (td.name.contentEquals(name)) { - return child; - } + if (td.name.contentEquals(name)) return child; } return null; } - + private ListBuffer getTypeParamExpressions(List typeParams, JavacTreeMaker maker) { ListBuffer typeParamsForBuilderParameter = new ListBuffer(); for (JCTree typeParam : typeParams) { diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 67862355..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 @@ -29,7 +29,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; @@ -54,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 singularizers = new HashMap(); private final TypeLibrary singularizableTypes = new TypeLibrary(); @@ -197,32 +204,32 @@ public class JavacSingularsRecipes { public abstract java.util.List generateFields(SingularData data, JavacNode builderType, JCTree source); /** - * Generates the singular, plural, and clear methods for the given - * {@link SingularData}.
- * Uses the given builderType as return type if - * chain == true, void otherwise. If you need more - * control over the return type and value, use - * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, JCExpression, JCStatement)}. + * 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(); - // TODO: Make these lambdas when switching to a source level >= 1.8. - Supplier returnType = new Supplier() { @Override public JCExpression get() { + + ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { return chain ? - cloneSelfType(builderType) : - maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); + cloneSelfType(builderType) : + maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); }}; - Supplier returnStatement = new Supplier() { @Override public JCStatement get() { + + StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; }}; - generateMethods(data, deprecate, builderType, source, fluent, returnType, returnStatement); + + generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); } + /** - * Generates the singular, plural, and clear methods for the given - * {@link SingularData}.
- * Uses the given returnType and returnStatement for the generated methods. + * 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, Supplier returnType, Supplier returnStatement); + 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 statements, Name targetVariableName, String builderVariable); diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index ecaf5c87..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 @@ -25,7 +25,6 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; -import java.util.function.Supplier; import lombok.core.GuavaTypeMap; import lombok.core.LombokImmutableList; @@ -33,8 +32,10 @@ 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.tree.JCTree; @@ -70,11 +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, Supplier returnType, Supplier returnStatement) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) { JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), 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/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index bd0f740e..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 @@ -25,13 +25,14 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; -import java.util.function.Supplier; 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.tree.JCTree; @@ -76,16 +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, Supplier returnType, Supplier returnStatement) { + @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, returnType, returnStatement); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), 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/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 1b4dd960..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 @@ -25,15 +25,16 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Arrays; -import java.util.function.Supplier; import lombok.core.LombokImmutableList; 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; @@ -101,16 +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, Supplier returnType, Supplier returnStatement) { + @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, returnType, returnStatement); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker); return; } JavacTreeMaker maker = builderType.getTreeMaker(); - generateSingularMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generatePluralMethod(deprecate, maker, returnType.get(), returnStatement.get(), data, builderType, source, fluent); - generateClearMethod(deprecate, maker, returnType.get(), returnStatement.get(), 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/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html index 4801d373..0249e19a 100644 --- a/website/templates/features/experimental/SuperBuilder.html +++ b/website/templates/features/experimental/SuperBuilder.html @@ -3,7 +3,7 @@ <@f.scaffold title="@SuperBuilder" logline="Bob now knows his ancestors: Builders with fields from superclasses, too. "> <@f.history>

- @SuperBuilder was introduced as experimental feature in lombok v1.18.1. + @SuperBuilder was introduced as experimental feature in lombok v1.18.2.

@@ -19,10 +19,7 @@

@SuperBuilder can generate so-called 'singular' methods for collection parameters/fields. For details, see the @Singular documentation in @Builder.

- As lombok has no access to the fields of superclasses when generating the builder code, the methods for setting those superclass fields can only be in the builder of the superclass. - Therefore, a @SuperBuilder must extend the @SuperBuilder of the superclass in order to include those methods. - Furthermore, the generated builder code heavily relies on generics to avoid class casting when using the builder. - @SuperBuilder generates a private constructor on the class that takes a builder instances as a parameter. This constructor sets the fields of the new instance to the values from the builder. + @SuperBuilder generates a private constructor on the class that takes a builder instance as a parameter. This constructor sets the fields of the new instance to the values from the builder.

@SuperBuilder is not compatible with @Builder.

@@ -41,8 +38,6 @@

- <@f.snippets name="Builder" /> - <@f.confKeys>
lombok.superBuilder.flagUsage = [warning | error] (default: not set) @@ -66,6 +61,8 @@ The sorted collections (java.util: SortedSet, NavigableSet, SortedMap, NavigableMap and guava: ImmutableSortedSet, ImmutableSortedMap) require that the type argument of the collection has natural order (implements java.util.Comparable). There is no way to pass an explicit Comparator to use in the builder.

An ArrayList is used to store added elements as call methods of a @Singular marked field, if the target collection is from the java.util package, even if the collection is a set or map. Because lombok ensures that generated collections are compacted, a new backing instance of a set or map must be constructed anyway, and storing the data as an ArrayList during the build process is more efficient that storing it as a map or set. This behaviour is not externally visible, an implementation detail of the current implementation of the java.util recipes for @Singular. +

+ The generated builder code heavily relies on generics to avoid class casting when using the builder.

diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html index 309303a0..988a7154 100644 --- a/website/templates/features/experimental/index.html +++ b/website/templates/features/experimental/index.html @@ -64,7 +64,7 @@ <@main.feature title="@SuperBuilder" href="SuperBuilder"> - Bob now knows his ancestors: Builders with fields from superclasses, too. + Bob now knows his ancestors: Builders with fields from superclasses, too. -- cgit