diff --git a/AUTHORS b/AUTHORS index 0aaf9290..5e70538f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,6 +3,7 @@ Lombok contributors in alphabetical order: Christian Sterzl DaveLaw Dawid Rusin +Enrique da Costa Cambio Jappe van der Hel Luan Nico Maarten Mulders @@ -12,6 +13,7 @@ Reinier Zwitserloot Robbert Jan Grootjans Roel Spilker Sander Koning +Szymon Pacanowski Taiki Sugawara Yun Zhi Lin diff --git a/doc/changelog.markdown b/doc/changelog.markdown index da1cefce..d5850457 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,9 @@ Lombok Changelog ---------------- ### v1.16.7 "Edgy Guinea Pig" +* FEATURE: `@Builder` updates: It now generates `clearFieldName()` methods if `@Singular` is used. [Issue #967](https://github.com/rzwitserloot/lombok/issues/967). +* FEATURE: `@Builder` updates: The annotation can now be put on instance methods. [Issue #63](https://github.com/rzwitserloot/lombok/issues/63). +* FEATURE: `@Builder` updates: `@Singular` now supports guava's ImmutableTable [Issue #937](https://github.com/rzwitserloot/lombok/issues/937). * FEATURE: A `lombok.config` key can now be used to make your fields `final` and/or `private`... __everywhere__. We'll be monitoring the performance impact of this for a while. We'll touch every source file if you turn these on, and even if you don't, we have to call into the lombok config system for every file. * BUGFIX: `@Value` and `@FieldDefaults` no longer make uninitialized static fields final. [Issue #928](https://github.com/rzwitserloot/lombok/issues/928). * BUGFIX: When using delombok, a source file with only `@NonNull` annotations on parameters as lombok feature would not get properly delomboked. [Issue #950](https://github.com/rzwitserloot/lombok/issues/950). diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index 0639c4cb..6a92028c 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -31,21 +31,21 @@ import java.lang.annotation.Target; * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class * that contains a member which is annotated with {@code @Builder}. *

- * If a member is annotated, it must be either a constructor or a static method. If a class is annotated, + * If a member is annotated, it must be either a constructor or a method. If a class is annotated, * then a private constructor is generated with all fields as arguments * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead. *

* The effect of {@code @Builder} is that an inner class is generated named TBuilder, - * with a private constructor. Instances of TBuilder are made with the static + * with a private constructor. Instances of TBuilder are made with the * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). *

* The TBuilder class contains 1 method for each parameter of the annotated - * constructor / static method (each field, when annotating a class), which returns the builder itself. + * constructor / method (each field, when annotating a class), which returns the builder itself. * The builder also has a build() method which returns a completed instance of the original type, * created by passing all parameters as set via the various other methods in the builder to the constructor - * or static method that was annotated with {@code @Builder}. The return type of this method will be the same - * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the + * or method that was annotated with {@code @Builder}. The return type of this method will be the same + * as the relevant class, unless a method has been annotated, in which case it'll be equal to the * return type of that method. *

* Complete documentation is found at the project lombok features page for @Builder. @@ -107,10 +107,10 @@ import java.lang.annotation.Target; @Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(SOURCE) public @interface Builder { - /** Name of the static method that creates a new builder instance. Default: {@code builder}. */ + /** Name of the method that creates a new builder instance. Default: {@code builder}. */ String builderMethodName() default "builder"; - /** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ + /** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ String buildMethodName() default "build"; /** @@ -118,7 +118,7 @@ public @interface Builder { * * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. *

- * Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}. + * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. */ String builderClassName() default ""; diff --git a/src/core/lombok/core/GuavaTypeMap.java b/src/core/lombok/core/GuavaTypeMap.java index 900d2b72..282d5d81 100644 --- a/src/core/lombok/core/GuavaTypeMap.java +++ b/src/core/lombok/core/GuavaTypeMap.java @@ -45,6 +45,7 @@ public final class GuavaTypeMap { m.put("com.google.common.collect.ImmutableSortedMap", "ImmutableSortedMap"); m.put("com.google.common.collect.ImmutableList", "ImmutableList"); m.put("com.google.common.collect.ImmutableCollection", "ImmutableList"); + m.put("com.google.common.collect.ImmutableTable", "ImmutableTable"); TYPE_TO_GUAVA_TYPE = Collections.unmodifiableMap(m); } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index ef06e249..be14653a 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -49,6 +49,7 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; @@ -163,6 +164,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null; boolean addCleaning = false; + boolean isStatic = true; if (parent.get() instanceof TypeDeclaration) { tdParent = parent; @@ -212,11 +214,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { } else if (parent.get() instanceof MethodDeclaration) { MethodDeclaration md = (MethodDeclaration) parent.get(); tdParent = parent.up(); - if (!md.isStatic()) { - annotationNode.addError("@Builder is only supported on types, constructors, and static methods."); - return; - } - + isStatic = md.isStatic(); + if (toBuilder) { final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; char[] token; @@ -322,7 +321,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { builderClassName = new String(token) + "Builder"; } } else { - annotationNode.addError("@Builder is only supported on types, constructors, and static methods."); + annotationNode.addError("@Builder is only supported on types, constructors, and methods."); return; } @@ -342,8 +341,16 @@ public class HandleBuilder extends EclipseAnnotationHandler { EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast); + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast); } else { + TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); + if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { + annotationNode.addError("Existing Builder must be a static inner class."); + return; + } else if (!isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) { + annotationNode.addError("Existing Builder must be a non-static inner class."); + return; + } sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); /* generate errors for @Singular BFDs that have one already defined node. */ { for (BuilderFieldData bfd : builderFields) { @@ -398,7 +405,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); + MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); if (md != null) injectMethod(builderType, md); } @@ -417,7 +424,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast); + MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast); if (md != null) injectMethod(tdParent, md); } @@ -507,7 +514,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { return decl; } - public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { + public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); @@ -552,7 +559,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { } else { MessageSend invoke = new MessageSend(); invoke.selector = staticName; - invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0); + if (isStatic) + invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0); + else + invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0); TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters; if (tps != null) { TypeReference[] trs = new TypeReference[tps.length]; @@ -573,13 +583,14 @@ public class HandleBuilder extends EclipseAnnotationHandler { return out; } - public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { + public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, 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.modifiers = ClassFileConstants.AccPublic; + if (isStatic) out.modifiers |= ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); out.typeParameters = copyTypeParams(typeParams, source); @@ -661,11 +672,12 @@ public class HandleBuilder extends EclipseAnnotationHandler { return null; } - public EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) { + public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) { 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; + builder.modifiers |= ClassFileConstants.AccPublic; + if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic; builder.typeParameters = copyTypeParams(typeParams, source); builder.name = builderClassName.toCharArray(); builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java index 95fd8935..5956c01b 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java @@ -32,14 +32,27 @@ public class EclipseGuavaMapSingularizer extends EclipseGuavaSingularizer { // TODO cgcc.ImmutableClassToInstanceMap // TODO cgcc.ImmutableRangeMap + private static final LombokImmutableList SUFFIXES = + LombokImmutableList.of("key", "value"); + private static final LombokImmutableList SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableMap", + "com.google.common.collect.ImmutableBiMap", + "com.google.common.collect.ImmutableSortedMap" + ); + @Override public LombokImmutableList getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableMap", - "com.google.common.collect.ImmutableBiMap", - "com.google.common.collect.ImmutableSortedMap"); + return SUPPORTED_TYPES; } - @Override protected boolean isMap() { - return true; + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; + } + + @Override protected String getAddAllTypeName() { + return "java.util.Map"; } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java index bc2893bf..326a9179 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java @@ -28,18 +28,29 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; @ProviderFor(EclipseSingularizer.class) public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer { - // TODO com.google.common.collect.ImmutableTable // TODO com.google.common.collect.ImmutableRangeSet // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset + private static final LombokImmutableList SUFFIXES = LombokImmutableList.of(""); + private static final LombokImmutableList SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableCollection", + "com.google.common.collect.ImmutableList", + "com.google.common.collect.ImmutableSet", + "com.google.common.collect.ImmutableSortedSet" + ); + @Override public LombokImmutableList getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableCollection", - "com.google.common.collect.ImmutableList", - "com.google.common.collect.ImmutableSet", - "com.google.common.collect.ImmutableSortedSet"); + return SUPPORTED_TYPES; } - - @Override protected boolean isMap() { - return false; + + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "add"; + } + + @Override protected String getAddAllTypeName() { + return "java.lang.Iterable"; } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index fa121328..242bde1f 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; import lombok.core.GuavaTypeMap; +import lombok.core.LombokImmutableList; import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; @@ -56,14 +57,9 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; abstract class EclipseGuavaSingularizer extends EclipseSingularizer { - protected static final char[][] JAVA_UTIL_MAP = { - {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'} - }; - protected String getSimpleTargetTypeName(SingularData data) { return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn()); } @@ -74,8 +70,6 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { return "builder".toCharArray(); } - protected abstract boolean isMap(); - protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) { char[][] tokenizedName = new char[addBuilder ? 6 : 5][]; tokenizedName[0] = new char[] {'c', 'o', 'm'}; @@ -88,9 +82,10 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { } @Override public List generateFields(SingularData data, EclipseNode builderType) { - char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), true); + String simpleTypeName = getSimpleTargetTypeName(data); + char[][] tokenizedName = makeGuavaTypeName(simpleTypeName, true); TypeReference type = new QualifiedTypeReference(tokenizedName, NULL_POSS); - type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs()); + type = addTypeArgs(getTypeArgumentsCount(), false, builderType, type, data.getTypeArgs()); FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1); buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -109,12 +104,34 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + + returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); + returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; + generateClearMethod(returnType, returnStatement, data, builderType); + } + + void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + md.modifiers = ClassFileConstants.AccPublic; + + FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); + thisDotField.receiver = new ThisReference(0, 0); + Assignment a = new Assignment(thisDotField, new NullLiteral(0, 0), 0); + md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray(); + md.statements = returnStatement != null ? new Statement[] {a, returnStatement} : new Statement[] {a}; + md.returnType = returnType; + injectMethod(builderType, md); } void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { - boolean mapMode = isMap(); - char[] keyName = !mapMode ? data.getSingularName() : (new String(data.getSingularName()) + "$key").toCharArray(); - char[] valueName = !mapMode ? null : (new String(data.getSingularName()) + "$value").toCharArray(); + LombokImmutableList suffixes = getArgumentSuffixes(); + char[][] names = new char[suffixes.size()][]; + for (int i = 0; i < suffixes.size(); i++) { + String s = suffixes.get(i); + char[] n = data.getSingularName(); + names[i] = s.isEmpty() ? n : s.toCharArray(); + } MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -126,40 +143,28 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); thisDotField.receiver = new ThisReference(0, 0); MessageSend thisDotFieldDotAdd = new MessageSend(); - if (mapMode) { - thisDotFieldDotAdd.arguments = new Expression[] { - new SingleNameReference(keyName, 0L), - new SingleNameReference(valueName, 0L)}; - } else { - thisDotFieldDotAdd.arguments = new Expression[] {new SingleNameReference(keyName, 0L)}; + thisDotFieldDotAdd.arguments = new Expression[suffixes.size()]; + for (int i = 0; i < suffixes.size(); i++) { + thisDotFieldDotAdd.arguments[i] = new SingleNameReference(names[i], 0L); } thisDotFieldDotAdd.receiver = thisDotField; - thisDotFieldDotAdd.selector = (mapMode ? "put" : "add").toCharArray(); + thisDotFieldDotAdd.selector = getAddMethodName().toCharArray(); statements.add(thisDotFieldDotAdd); if (returnStatement != null) statements.add(returnStatement); md.statements = statements.toArray(new Statement[statements.size()]); - - if (mapMode) { - TypeReference keyType = cloneParamType(0, data.getTypeArgs(), builderType); - Argument keyParam = new Argument(keyName, 0, keyType, 0); - TypeReference valueType = cloneParamType(1, data.getTypeArgs(), builderType); - Argument valueParam = new Argument(valueName, 0, valueType, 0); - md.arguments = new Argument[] {keyParam, valueParam}; - } else { - TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType); - Argument param = new Argument(keyName, 0, paramType, 0); - md.arguments = new Argument[] {param}; + md.arguments = new Argument[suffixes.size()]; + for (int i = 0; i < suffixes.size(); i++) { + TypeReference tr = cloneParamType(i, data.getTypeArgs(), builderType); + md.arguments[i] = new Argument(names[i], 0, tr, 0); } md.returnType = returnType; - md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(mapMode ? "put" : "add", new String(data.getSingularName())).toCharArray(); + md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray(); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { - boolean mapMode = isMap(); - MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -172,40 +177,36 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { MessageSend thisDotFieldDotAddAll = new MessageSend(); thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)}; thisDotFieldDotAddAll.receiver = thisDotField; - thisDotFieldDotAddAll.selector = (mapMode ? "putAll" : "addAll").toCharArray(); + thisDotFieldDotAddAll.selector = (getAddMethodName() + "All").toCharArray(); statements.add(thisDotFieldDotAddAll); if (returnStatement != null) statements.add(returnStatement); md.statements = statements.toArray(new Statement[statements.size()]); TypeReference paramType; - if (mapMode) { - paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS); - paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs()); - } else { - paramType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_ITERABLE, NULL_POSS); - paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs()); - } + paramType = new QualifiedTypeReference(fromQualifiedName(getAddAllTypeName()), NULL_POSS); + paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs()); Argument param = new Argument(data.getPluralName(), 0, paramType, 0); md.arguments = new Argument[] {param}; md.returnType = returnType; - md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", new String(data.getPluralName())).toCharArray(); + md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray(); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List statements, char[] targetVariableName) { - boolean mapMode = isMap(); TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS); - varType = addTypeArgs(mapMode ? 2 : 1, false, builderType, varType, data.getTypeArgs()); + String simpleTypeName = getSimpleTargetTypeName(data); + int agrumentsCount = getTypeArgumentsCount(); + varType = addTypeArgs(agrumentsCount, false, builderType, varType, data.getTypeArgs()); MessageSend emptyInvoke; { //ImmutableX.of() emptyInvoke = new MessageSend(); emptyInvoke.selector = new char[] {'o', 'f'}; - emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(getSimpleTargetTypeName(data), false), NULL_POSS, 0, 0); - emptyInvoke.typeArguments = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs()); + emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(simpleTypeName, false), NULL_POSS, 0, 0); + emptyInvoke.typeArguments = createTypeArgs(agrumentsCount, false, builderType, data.getTypeArgs()); } MessageSend invokeBuild; { @@ -244,4 +245,12 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { createBuilderInvoke.selector = getBuilderMethodName(data); return new IfStatement(cond, new Assignment(thisDotField2, createBuilderInvoke, 0), 0, 0); } + + protected abstract LombokImmutableList getArgumentSuffixes(); + protected abstract String getAddMethodName(); + protected abstract String getAddAllTypeName(); + + protected int getTypeArgumentsCount() { + return getArgumentSuffixes().size(); + } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java new file mode 100644 index 00000000..4d25811b --- /dev/null +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.handlers.singulars; + +import org.mangosdk.spi.ProviderFor; + +import lombok.core.LombokImmutableList; +import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; + +@ProviderFor(EclipseSingularizer.class) +public class EclipseGuavaTableSingularizer extends EclipseGuavaSingularizer { + private static final LombokImmutableList SUFFIXES = + LombokImmutableList.of("rowKey", "columnKey", "value"); + private static final LombokImmutableList SUPPORTED_TYPES = + LombokImmutableList.of("com.google.common.collect.ImmutableTable"); + + @Override public LombokImmutableList getSupportedTypes() { + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; + } + + @Override protected String getAddAllTypeName() { + return "com.google.common.collect.Table"; + } +} diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 4b59f7a8..2d8083d3 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -34,11 +34,15 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; 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; @@ -96,6 +100,29 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + + returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); + returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; + generateClearMethod(returnType, returnStatement, data, builderType); + } + + private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + md.modifiers = ClassFileConstants.AccPublic; + + FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L); + thisDotField.receiver = new ThisReference(0, 0); + FieldReference thisDotField2 = new FieldReference(data.getPluralName(), 0L); + thisDotField2.receiver = new ThisReference(0, 0); + md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray(); + MessageSend clearMsg = new MessageSend(); + clearMsg.receiver = thisDotField2; + clearMsg.selector = "clear".toCharArray(); + Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsg, 0, 0); + md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; + md.returnType = returnType; + injectMethod(builderType, md); } void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 1c6b1ff3..ef9e2a76 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -32,13 +32,17 @@ import java.util.List; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; 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; @@ -141,6 +145,40 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + + returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); + returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; + generateClearMethod(returnType, returnStatement, data, builderType); + } + + private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + md.modifiers = ClassFileConstants.AccPublic; + + String pN = new String(data.getPluralName()); + char[] keyFieldName = (pN + "$key").toCharArray(); + char[] valueFieldName = (pN + "$value").toCharArray(); + + FieldReference thisDotField = new FieldReference(keyFieldName, 0L); + thisDotField.receiver = new ThisReference(0, 0); + FieldReference thisDotField2 = new FieldReference(keyFieldName, 0L); + thisDotField2.receiver = new ThisReference(0, 0); + FieldReference thisDotField3 = new FieldReference(valueFieldName, 0L); + thisDotField3.receiver = new ThisReference(0, 0); + md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray(); + MessageSend clearMsg1 = new MessageSend(); + clearMsg1.receiver = thisDotField2; + clearMsg1.selector = "clear".toCharArray(); + MessageSend clearMsg2 = new MessageSend(); + clearMsg2.receiver = thisDotField3; + clearMsg2.selector = "clear".toCharArray(); + Block clearMsgs = new Block(2); + clearMsgs.statements = new Statement[] {clearMsg1, clearMsg2}; + Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsgs, 0, 0); + md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; + md.returnType = returnType; + injectMethod(builderType, md); } private void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java index 68250ffd..4d5e0f67 100644 --- a/src/core/lombok/experimental/Builder.java +++ b/src/core/lombok/experimental/Builder.java @@ -31,21 +31,21 @@ import java.lang.annotation.Target; * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class * that contains a member which is annotated with {@code @Builder}. *

- * If a member is annotated, it must be either a constructor or a static method. If a class is annotated, + * If a member is annotated, it must be either a constructor or a method. If a class is annotated, * then a private constructor is generated with all fields as arguments * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead. *

* The effect of {@code @Builder} is that an inner class is generated named TBuilder, - * with a private constructor. Instances of TBuilder are made with the static + * with a private constructor. Instances of TBuilder are made with the * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). *

* The TBuilder class contains 1 method for each parameter of the annotated - * constructor / static method (each field, when annotating a class), which returns the builder itself. + * constructor / method (each field, when annotating a class), which returns the builder itself. * The builder also has a build() method which returns a completed instance of the original type, * created by passing all parameters as set via the various other methods in the builder to the constructor - * or static method that was annotated with {@code @Builder}. The return type of this method will be the same - * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the + * or method that was annotated with {@code @Builder}. The return type of this method will be the same + * as the relevant class, unless a method has been annotated, in which case it'll be equal to the * return type of that method. *

* Complete documentation is found at the project lombok features page for @Builder. @@ -110,15 +110,15 @@ import java.lang.annotation.Target; @Retention(SOURCE) @Deprecated public @interface Builder { - /** Name of the static method that creates a new builder instance. Default: {@code builder}. */ + /** Name of the method that creates a new builder instance. Default: {@code builder}. */ String builderMethodName() default "builder"; - /** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ + /** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ String buildMethodName() default "build"; /** Name of the builder class. * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. - * Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}. + * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. */ String builderClassName() default ""; diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index f0139b47..efe40da3 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -24,6 +24,8 @@ package lombok.javac.handlers; import java.lang.annotation.Annotation; import java.util.ArrayList; +import javax.lang.model.element.Modifier; + import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; @@ -124,11 +126,12 @@ public class HandleBuilder extends JavacAnnotationHandler { JCExpression returnType; List typeParams = List.nil(); List thrownExceptions = List.nil(); - Name nameOfStaticBuilderMethod; + Name nameOfBuilderMethod; JavacNode tdParent; JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null; boolean addCleaning = false; + boolean isStatic = true; if (parent.get() instanceof JCClassDecl) { tdParent = parent; @@ -157,7 +160,7 @@ public class HandleBuilder extends JavacAnnotationHandler { returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); - nameOfStaticBuilderMethod = null; + nameOfBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("")) { JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); @@ -171,21 +174,18 @@ public class HandleBuilder extends JavacAnnotationHandler { returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = jmd.thrown; - nameOfStaticBuilderMethod = null; + nameOfBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null) { tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); - if ((jmd.mods.flags & Flags.STATIC) == 0) { - annotationNode.addError("@Builder is only supported on types, constructors, and static methods."); - return; - } + isStatic = (jmd.mods.flags & Flags.STATIC) != 0; JCExpression fullReturnType = jmd.restype; returnType = fullReturnType; typeParams = jmd.typarams; thrownExceptions = jmd.thrown; - nameOfStaticBuilderMethod = jmd.name; + nameOfBuilderMethod = jmd.name; if (returnType instanceof JCTypeApply) { returnType = ((JCTypeApply) returnType).clazz; } @@ -278,7 +278,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } } } else { - annotationNode.addError("@Builder is only supported on types, constructors, and static methods."); + annotationNode.addError("@Builder is only supported on types, constructors, and methods."); return; } @@ -298,8 +298,16 @@ public class HandleBuilder extends JavacAnnotationHandler { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast); + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast); } else { + JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); + if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { + annotationNode.addError("Existing Builder must be a static inner class."); + return; + } else if (!isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { + annotationNode.addError("Existing Builder must be a non-static inner class."); + return; + } sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); /* generate errors for @Singular BFDs that have one already defined node. */ { for (BuilderFieldData bfd : builderFields) { @@ -351,7 +359,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); + JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); if (md != null) injectMethod(builderType, md); } @@ -367,7 +375,7 @@ public class HandleBuilder extends JavacAnnotationHandler { if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams); + JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams); recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); if (md != null) injectMethod(tdParent, md); } @@ -488,7 +496,7 @@ public class HandleBuilder extends JavacAnnotationHandler { */ } - private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning) { + private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List builderFields, JavacNode type, List thrownExceptions, JCTree source, boolean addCleaning) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -516,16 +524,19 @@ public class HandleBuilder extends JavacAnnotationHandler { statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); } - if (staticName == null) { + if (builderName == null) { call = maker.NewClass(null, List.nil(), returnType, args.toList(), null); statements.append(maker.Return(call)); } else { + ListBuffer typeParams = new ListBuffer(); for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) { typeParams.append(maker.Ident(tp.name)); } - - JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName); + JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name); + if (!isStatic) + callee = maker.Select(callee, type.up().toName("this")); + JCExpression fn = maker.Select(callee, builderName); call = maker.Apply(typeParams.toList(), fn, args.toList()); if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) { statements.append(maker.Exec(call)); @@ -536,10 +547,10 @@ public class HandleBuilder extends JavacAnnotationHandler { JCBlock body = maker.Block(0, statements.toList()); - return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.nil(), List.nil(), thrownExceptions, body, null); + return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.nil(), List.nil(), thrownExceptions, body, null); } - public JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List typeParams) { + public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode type, List typeParams) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer typeArgs = new ListBuffer(); @@ -551,7 +562,9 @@ public class HandleBuilder extends JavacAnnotationHandler { JCStatement statement = maker.Return(call); JCBlock body = maker.Block(0, List.of(statement)); - return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.nil(), List.nil(), body, null); + int modifiers = Flags.PUBLIC; + if (isStatic) modifiers |= Flags.STATIC; + return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.nil(), List.nil(), body, null); } public void generateBuilderFields(JavacNode builderType, java.util.List builderFields, JCTree source) { @@ -615,9 +628,11 @@ public class HandleBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC); + int modifiers = Flags.PUBLIC; + if (isStatic) modifiers |= Flags.STATIC; + JCModifiers mods = maker.Modifiers(modifiers); JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.nil(), List.nil()); return injectType(tdParent, builder); } diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java index 0700e2e5..e0621cf7 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java @@ -32,14 +32,27 @@ public class JavacGuavaMapSingularizer extends JavacGuavaSingularizer { // TODO cgcc.ImmutableClassToInstanceMap // TODO cgcc.ImmutableRangeMap + private static final LombokImmutableList SUFFIXES = + LombokImmutableList.of("key", "value"); + private static final LombokImmutableList SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableMap", + "com.google.common.collect.ImmutableBiMap", + "com.google.common.collect.ImmutableSortedMap" + ); + @Override public LombokImmutableList getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableMap", - "com.google.common.collect.ImmutableBiMap", - "com.google.common.collect.ImmutableSortedMap"); + return SUPPORTED_TYPES; } - @Override protected boolean isMap() { - return true; + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; + } + + @Override protected String getAddAllTypeName() { + return "java.util.Map"; } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java index 2e404ca8..5c7fcab5 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java @@ -1,16 +1,16 @@ /* * Copyright (C) 2015 The Project Lombok Authors. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,25 +21,36 @@ */ package lombok.javac.handlers.singulars; +import org.mangosdk.spi.ProviderFor; + import lombok.core.LombokImmutableList; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; -import org.mangosdk.spi.ProviderFor; - @ProviderFor(JavacSingularizer.class) public class JavacGuavaSetListSingularizer extends JavacGuavaSingularizer { - // TODO com.google.common.collect.ImmutableTable // TODO com.google.common.collect.ImmutableRangeSet // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset + private static final LombokImmutableList SUFFIXES = LombokImmutableList.of(""); + private static final LombokImmutableList SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableCollection", + "com.google.common.collect.ImmutableList", + "com.google.common.collect.ImmutableSet", + "com.google.common.collect.ImmutableSortedSet" + ); + @Override public LombokImmutableList getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableCollection", - "com.google.common.collect.ImmutableList", - "com.google.common.collect.ImmutableSet", - "com.google.common.collect.ImmutableSortedSet"); + return SUPPORTED_TYPES; } - @Override protected boolean isMap() { - return false; + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "add"; + } + + @Override protected String getAddAllTypeName() { + return "java.lang.Iterable"; } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 41e379f6..4de39d98 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -1,16 +1,16 @@ /* * Copyright (C) 2015 The Project Lombok Authors. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,6 +27,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collections; import lombok.core.GuavaTypeMap; +import lombok.core.LombokImmutableList; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -58,12 +59,11 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return "builder"; } - protected abstract boolean isMap(); - @Override public java.util.List generateFields(SingularData data, JavacNode builderType, JCTree source) { JavacTreeMaker maker = builderType.getTreeMaker(); - JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "Builder"); - type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs(), source); + String simpleTypeName = getSimpleTargetTypeName(data); + JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", simpleTypeName, "Builder"); + type = addTypeArgs(getTypeArgumentsCount(), false, builderType, type, data.getTypeArgs(), source); JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null); return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); @@ -74,75 +74,94 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); - + returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + + returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); + returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + } + + private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + List typeParams = List.nil(); + List thrown = List.nil(); + List params = List.nil(); + + JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()); + JCStatement clearField = maker.Exec(maker.Assign(thisDotField, maker.Literal(CTC_BOT, null))); + List statements = returnStatement != null ? List.of(clearField, returnStatement) : List.of(clearField); + + JCBlock body = maker.Block(0, statements); + Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); + JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null); + injectMethod(builderType, method); } void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List typeParams = List.nil(); List thrown = List.nil(); - boolean mapMode = isMap(); - Name keyName = !mapMode ? data.getSingularName() : builderType.toName(data.getSingularName() + "$key"); - Name valueName = !mapMode ? null : builderType.toName(data.getSingularName() + "$value"); + LombokImmutableList suffixes = getArgumentSuffixes(); + Name[] names = new Name[suffixes.size()]; + for (int i = 0; i < suffixes.size(); i++) { + String s = suffixes.get(i); + Name n = data.getSingularName(); + names[i] = s.isEmpty() ? n : builderType.toName(s); + } JCModifiers mods = maker.Modifiers(Flags.PUBLIC); ListBuffer statements = new ListBuffer(); - statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source)); - JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "put" : "add"); - List invokeAddExpr; - if (mapMode) { - invokeAddExpr = List.of(maker.Ident(keyName), maker.Ident(valueName)); - } else { - invokeAddExpr = List.of(maker.Ident(keyName)); + statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); + JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName()); + ListBuffer invokeAddExprBuilder = new ListBuffer(); + for (int i = 0; i < suffixes.size(); i++) { + invokeAddExprBuilder.append(maker.Ident(names[i])); } + List invokeAddExpr = invokeAddExprBuilder.toList(); JCExpression invokeAdd = maker.Apply(List.nil(), thisDotFieldDotAdd, invokeAddExpr); statements.append(maker.Exec(invokeAdd)); if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); Name methodName = data.getSingularName(); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); - if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "put" : "add", methodName.toString())); - List params; - if (mapMode) { - JCExpression keyType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source); - JCExpression valueType = cloneParamType(1, maker, data.getTypeArgs(), builderType, source); - JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, keyType, null); - JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, valueType, null); - params = List.of(paramKey, paramValue); - } else { - JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source); - params = List.of(maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null)); + if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), methodName.toString())); + ListBuffer params = new ListBuffer(); + for (int i = 0; i < suffixes.size(); i++) { + JCExpression pt = cloneParamType(i, maker, data.getTypeArgs(), builderType, source); + JCVariableDecl p = maker.VarDef(maker.Modifiers(paramFlags), names[i], pt, null); + params.append(p); } - JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null); + + JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params.toList(), thrown, body, null); injectMethod(builderType, method); } protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List typeParams = List.nil(); List thrown = List.nil(); - boolean mapMode = isMap(); JCModifiers mods = maker.Modifiers(Flags.PUBLIC); ListBuffer statements = new ListBuffer(); - statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source)); - JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "putAll" : "addAll"); + statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); + JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All"); JCExpression invokeAddAll = maker.Apply(List.nil(), thisDotFieldDotAddAll, List.of(maker.Ident(data.getPluralName()))); statements.append(maker.Exec(invokeAddAll)); if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); Name methodName = data.getPluralName(); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); - if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", methodName.toString())); + if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", methodName.toString())); JCExpression paramType; - if (mapMode) { - paramType = chainDots(builderType, "java", "util", "Map"); + String aaTypeName = getAddAllTypeName(); + if (aaTypeName.startsWith("java.lang.") && aaTypeName.indexOf('.', 11) == -1) { + paramType = genJavaLangTypeRef(builderType, aaTypeName.substring(10)); } else { - paramType = genJavaLangTypeRef(builderType, "Iterable"); + paramType = chainDotsString(builderType, aaTypeName); } - paramType = addTypeArgs(mapMode ? 2 : 1, true, builderType, paramType, data.getTypeArgs(), source); + paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source); JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null); JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, List.of(param), thrown, body, null); injectMethod(builderType, method); @@ -151,15 +170,15 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { JavacTreeMaker maker = builderType.getTreeMaker(); List jceBlank = List.nil(); - boolean mapMode = isMap(); JCExpression varType = chainDotsString(builderType, data.getTargetFqn()); - varType = addTypeArgs(mapMode ? 2 : 1, false, builderType, varType, data.getTypeArgs(), source); + int agrumentsCount = getTypeArgumentsCount(); + varType = addTypeArgs(agrumentsCount, false, builderType, varType, data.getTypeArgs(), source); JCExpression empty; { //ImmutableX.of() JCExpression emptyMethod = chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "of"); - List invokeTypeArgs = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs(), source); + List invokeTypeArgs = createTypeArgs(agrumentsCount, false, builderType, data.getTypeArgs(), source); empty = maker.Apply(invokeTypeArgs, emptyMethod, jceBlank); } @@ -179,7 +198,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { statements.append(jcs); } - protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) { + protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { List jceBlank = List.nil(); JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()); @@ -191,4 +210,12 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return maker.If(cond, thenPart, null); } + + protected abstract LombokImmutableList getArgumentSuffixes(); + protected abstract String getAddMethodName(); + protected abstract String getAddAllTypeName(); + + protected int getTypeArgumentsCount() { + return getArgumentSuffixes().size(); + } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java new file mode 100644 index 00000000..080266b8 --- /dev/null +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers.singulars; + +import org.mangosdk.spi.ProviderFor; + +import lombok.core.LombokImmutableList; +import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; + +@ProviderFor(JavacSingularizer.class) +public class JavacGuavaTableSingularizer extends JavacGuavaSingularizer { + private static final LombokImmutableList SUFFIXES = + LombokImmutableList.of("rowKey", "columnKey", "value"); + private static final LombokImmutableList SUPPORTED_TYPES = + LombokImmutableList.of("com.google.common.collect.ImmutableTable"); + + @Override public LombokImmutableList getSupportedTypes() { + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; + } + + @Override protected String getAddAllTypeName() { + return "com.google.common.collect.Table"; + } +} diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 8574ddbf..e167c7e2 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -91,6 +91,30 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + + returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); + returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; + generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + } + + private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + List typeParams = List.nil(); + List thrown = List.nil(); + List params = List.nil(); + List jceBlank = List.nil(); + + JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()); + JCExpression thisDotFieldDotClear = maker.Select(maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), builderType.toName("clear")); + JCStatement clearCall = maker.Exec(maker.Apply(jceBlank, thisDotFieldDotClear, jceBlank)); + JCExpression cond = maker.Binary(CTC_NOT_EQUAL, thisDotField, maker.Literal(CTC_BOT, null)); + JCStatement ifSetCallClear = maker.If(cond, clearCall, null); + List statements = returnStatement != null ? List.of(ifSetCallClear, returnStatement) : List.of(ifSetCallClear); + + JCBlock body = maker.Block(0, statements); + Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); + JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null); + injectMethod(builderType, method); } void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 0830c9c9..1acae7e3 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -115,6 +115,33 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + + returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID)); + returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + } + + private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + List typeParams = List.nil(); + List thrown = List.nil(); + List params = List.nil(); + List jceBlank = List.nil(); + + JCExpression thisDotKeyField = chainDots(builderType, "this", data.getPluralName() + "$key"); + JCExpression thisDotKeyFieldDotClear = chainDots(builderType, "this", data.getPluralName() + "$key", "clear"); + JCExpression thisDotValueFieldDotClear = chainDots(builderType, "this", data.getPluralName() + "$value", "clear"); + JCStatement clearKeyCall = maker.Exec(maker.Apply(jceBlank, thisDotKeyFieldDotClear, jceBlank)); + JCStatement clearValueCall = maker.Exec(maker.Apply(jceBlank, thisDotValueFieldDotClear, jceBlank)); + JCExpression cond = maker.Binary(CTC_NOT_EQUAL, thisDotKeyField, maker.Literal(CTC_BOT, null)); + JCBlock clearCalls = maker.Block(0, List.of(clearKeyCall, clearValueCall)); + JCStatement ifSetCallClear = maker.If(cond, clearCalls, null); + List statements = returnStatement != null ? List.of(ifSetCallClear, returnStatement) : List.of(ifSetCallClear); + + JCBlock body = maker.Block(0, statements); + Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); + JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null); + injectMethod(builderType, method); } private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { diff --git a/test/transform/resource/after-delombok/BuilderInstanceMethod.java b/test/transform/resource/after-delombok/BuilderInstanceMethod.java new file mode 100644 index 00000000..61e237d0 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderInstanceMethod.java @@ -0,0 +1,66 @@ +import java.util.List; +class BuilderInstanceMethod { + public String create(int show, final int yes, List also, int $andMe) { + return "" + show + yes + also + $andMe; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public class StringBuilder { + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + private int show; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + private int yes; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + private List also; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + private int $andMe; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + StringBuilder() { + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public StringBuilder show(final int show) { + this.show = show; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public StringBuilder yes(final int yes) { + this.yes = yes; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public StringBuilder also(final List also) { + this.also = also; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public StringBuilder $andMe(final int $andMe) { + this.$andMe = $andMe; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public String build() { + return BuilderInstanceMethod.this.create(show, yes, also, $andMe); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public java.lang.String toString() { + return "BuilderInstanceMethod.StringBuilder(show=" + this.show + ", yes=" + this.yes + ", also=" + this.also + ", $andMe=" + this.$andMe + ")"; + } + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public StringBuilder builder() { + return new StringBuilder(); + } +} diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java index f8e0579d..79ffbc8b 100644 --- a/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java +++ b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java @@ -2,19 +2,22 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.ImmutableTable; class BuilderSingularGuavaListsSets { private ImmutableList cards; private ImmutableCollection frogs; @SuppressWarnings("all") private ImmutableSet rawSet; private ImmutableSortedSet passes; + private ImmutableTable users; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - BuilderSingularGuavaListsSets(final ImmutableList cards, final ImmutableCollection frogs, final ImmutableSet rawSet, final ImmutableSortedSet passes) { + BuilderSingularGuavaListsSets(final ImmutableList cards, final ImmutableCollection frogs, final ImmutableSet rawSet, final ImmutableSortedSet passes, final ImmutableTable users) { this.cards = cards; this.frogs = frogs; this.rawSet = rawSet; this.passes = passes; + this.users = users; } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") @@ -33,6 +36,9 @@ class BuilderSingularGuavaListsSets { private com.google.common.collect.ImmutableSortedSet.Builder passes; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + private com.google.common.collect.ImmutableTable.Builder users; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder() { } @java.lang.SuppressWarnings("all") @@ -51,6 +57,12 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder clearCards() { + this.cards = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder frog(final Number frog) { if (this.frogs == null) this.frogs = com.google.common.collect.ImmutableList.builder(); this.frogs.add(frog); @@ -65,6 +77,12 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder clearFrogs() { + this.frogs = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder rawSet(final java.lang.Object rawSet) { if (this.rawSet == null) this.rawSet = com.google.common.collect.ImmutableSet.builder(); this.rawSet.add(rawSet); @@ -79,6 +97,12 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder clearRawSet() { + this.rawSet = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder pass(final String pass) { if (this.passes == null) this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder(); this.passes.add(pass); @@ -93,18 +117,45 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder clearPasses() { + this.passes = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder user(final Number rowKey, final Number columnKey, final String value) { + if (this.users == null) this.users = com.google.common.collect.ImmutableTable.builder(); + this.users.put(rowKey, columnKey, value); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder users(final com.google.common.collect.Table users) { + if (this.users == null) this.users = com.google.common.collect.ImmutableTable.builder(); + this.users.putAll(users); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder clearUsers() { + this.users = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSets build() { com.google.common.collect.ImmutableList cards = this.cards == null ? com.google.common.collect.ImmutableList.of() : this.cards.build(); com.google.common.collect.ImmutableCollection frogs = this.frogs == null ? com.google.common.collect.ImmutableList.of() : this.frogs.build(); com.google.common.collect.ImmutableSet rawSet = this.rawSet == null ? com.google.common.collect.ImmutableSet.of() : this.rawSet.build(); com.google.common.collect.ImmutableSortedSet passes = this.passes == null ? com.google.common.collect.ImmutableSortedSet.of() : this.passes.build(); - return new BuilderSingularGuavaListsSets(cards, frogs, rawSet, passes); + com.google.common.collect.ImmutableTable users = this.users == null ? com.google.common.collect.ImmutableTable.of() : this.users.build(); + return new BuilderSingularGuavaListsSets(cards, frogs, rawSet, passes, users); } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public java.lang.String toString() { - return "BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards + ", frogs=" + this.frogs + ", rawSet=" + this.rawSet + ", passes=" + this.passes + ")"; + return "BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards + ", frogs=" + this.frogs + ", rawSet=" + this.rawSet + ", passes=" + this.passes + ", users=" + this.users + ")"; } } @java.lang.SuppressWarnings("all") diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java index 0cb0001b..1ad8fa83 100644 --- a/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java +++ b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java @@ -31,9 +31,9 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder battleaxe(final K battleaxe$key, final V battleaxe$value) { + public BuilderSingularGuavaMapsBuilder battleaxe(final K key, final V value) { if (this.battleaxes == null) this.battleaxes = com.google.common.collect.ImmutableMap.builder(); - this.battleaxes.put(battleaxe$key, battleaxe$value); + this.battleaxes.put(key, value); return this; } @java.lang.SuppressWarnings("all") @@ -45,9 +45,15 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder vertex(final Integer vertex$key, final V vertex$value) { + public BuilderSingularGuavaMapsBuilder clearBattleaxes() { + this.battleaxes = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder vertex(final Integer key, final V value) { if (this.vertices == null) this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder(); - this.vertices.put(vertex$key, vertex$value); + this.vertices.put(key, value); return this; } @java.lang.SuppressWarnings("all") @@ -59,9 +65,15 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder rawMap(final java.lang.Object rawMap$key, final java.lang.Object rawMap$value) { + public BuilderSingularGuavaMapsBuilder clearVertices() { + this.vertices = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder rawMap(final java.lang.Object key, final java.lang.Object value) { if (this.rawMap == null) this.rawMap = com.google.common.collect.ImmutableBiMap.builder(); - this.rawMap.put(rawMap$key, rawMap$value); + this.rawMap.put(key, value); return this; } @java.lang.SuppressWarnings("all") @@ -73,6 +85,12 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder clearRawMap() { + this.rawMap = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaMaps build() { com.google.common.collect.ImmutableMap battleaxes = this.battleaxes == null ? com.google.common.collect.ImmutableMap.of() : this.battleaxes.build(); com.google.common.collect.ImmutableSortedMap vertices = this.vertices == null ? com.google.common.collect.ImmutableSortedMap.of() : this.vertices.build(); diff --git a/test/transform/resource/after-delombok/BuilderSingularLists.java b/test/transform/resource/after-delombok/BuilderSingularLists.java index f58934d4..9b409404 100644 --- a/test/transform/resource/after-delombok/BuilderSingularLists.java +++ b/test/transform/resource/after-delombok/BuilderSingularLists.java @@ -44,6 +44,12 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder clearChildren() { + if (this.children != null) this.children.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularListsBuilder scarf(final Number scarf) { if (this.scarves == null) this.scarves = new java.util.ArrayList(); this.scarves.add(scarf); @@ -58,6 +64,12 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder clearScarves() { + if (this.scarves != null) this.scarves.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularListsBuilder rawList(final java.lang.Object rawList) { if (this.rawList == null) this.rawList = new java.util.ArrayList(); this.rawList.add(rawList); @@ -72,6 +84,12 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder clearRawList() { + if (this.rawList != null) this.rawList.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularLists build() { java.util.List children; switch (this.children == null ? 0 : this.children.size()) { diff --git a/test/transform/resource/after-delombok/BuilderSingularMaps.java b/test/transform/resource/after-delombok/BuilderSingularMaps.java index 212ece26..257a2ec2 100644 --- a/test/transform/resource/after-delombok/BuilderSingularMaps.java +++ b/test/transform/resource/after-delombok/BuilderSingularMaps.java @@ -57,6 +57,14 @@ class BuilderSingularMaps { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder clearWomen() { + if (this.women$key != null) { + this.women$key.clear(); + this.women$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder man(K manKey, Number manValue) { if (this.men$key == null) { this.men$key = new java.util.ArrayList(); @@ -79,6 +87,14 @@ class BuilderSingularMaps { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder clearMen() { + if (this.men$key != null) { + this.men$key.clear(); + this.men$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder rawMap(Object rawMapKey, Object rawMapValue) { if (this.rawMap$key == null) { this.rawMap$key = new java.util.ArrayList(); @@ -101,6 +117,14 @@ class BuilderSingularMaps { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder clearRawMap() { + if (this.rawMap$key != null) { + this.rawMap$key.clear(); + this.rawMap$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder stringMap(String stringMapKey, V stringMapValue) { if (this.stringMap$key == null) { this.stringMap$key = new java.util.ArrayList(); @@ -123,6 +147,14 @@ class BuilderSingularMaps { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder clearStringMap() { + if (this.stringMap$key != null) { + this.stringMap$key.clear(); + this.stringMap$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMaps build() { java.util.Map women; switch (this.women$key == null ? 0 : this.women$key.size()) { diff --git a/test/transform/resource/after-delombok/BuilderSingularNoAuto.java b/test/transform/resource/after-delombok/BuilderSingularNoAuto.java index 0be33a84..d5cd8f41 100644 --- a/test/transform/resource/after-delombok/BuilderSingularNoAuto.java +++ b/test/transform/resource/after-delombok/BuilderSingularNoAuto.java @@ -42,6 +42,12 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularNoAutoBuilder clearThings() { + if (this.things != null) this.things.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularNoAutoBuilder widget(final String widget) { if (this.widgets == null) this.widgets = new java.util.ArrayList(); this.widgets.add(widget); @@ -56,6 +62,12 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularNoAutoBuilder clearWidgets() { + if (this.widgets != null) this.widgets.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularNoAutoBuilder items(final String items) { if (this.items == null) this.items = new java.util.ArrayList(); this.items.add(items); @@ -70,6 +82,12 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularNoAutoBuilder clearItems() { + if (this.items != null) this.items.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularNoAuto build() { java.util.List things; switch (this.things == null ? 0 : this.things.size()) { diff --git a/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java b/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java index 0dd40c9f..7ffa647b 100644 --- a/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java +++ b/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java @@ -44,9 +44,15 @@ class BuilderSingularRedirectToGuava { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularRedirectToGuavaBuilder thing(final Integer thing$key, final Number thing$value) { + public BuilderSingularRedirectToGuavaBuilder clearDangerMice() { + this.dangerMice = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularRedirectToGuavaBuilder thing(final Integer key, final Number value) { if (this.things == null) this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder(); - this.things.put(thing$key, thing$value); + this.things.put(key, value); return this; } @java.lang.SuppressWarnings("all") @@ -58,6 +64,12 @@ class BuilderSingularRedirectToGuava { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularRedirectToGuavaBuilder clearThings() { + this.things = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularRedirectToGuavaBuilder doohickey(final Class doohickey) { if (this.doohickeys == null) this.doohickeys = com.google.common.collect.ImmutableList.builder(); this.doohickeys.add(doohickey); @@ -72,6 +84,12 @@ class BuilderSingularRedirectToGuava { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularRedirectToGuavaBuilder clearDoohickeys() { + this.doohickeys = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularRedirectToGuava build() { java.util.Set dangerMice = this.dangerMice == null ? com.google.common.collect.ImmutableSet.of() : this.dangerMice.build(); java.util.NavigableMap things = this.things == null ? com.google.common.collect.ImmutableSortedMap.of() : this.things.build(); diff --git a/test/transform/resource/after-delombok/BuilderSingularSets.java b/test/transform/resource/after-delombok/BuilderSingularSets.java index 70372b99..c794726a 100644 --- a/test/transform/resource/after-delombok/BuilderSingularSets.java +++ b/test/transform/resource/after-delombok/BuilderSingularSets.java @@ -49,6 +49,12 @@ class BuilderSingularSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder clearDangerMice() { + if (this.dangerMice != null) this.dangerMice.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder octopus(final Number octopus) { if (this.octopodes == null) this.octopodes = new java.util.ArrayList(); this.octopodes.add(octopus); @@ -63,6 +69,12 @@ class BuilderSingularSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder clearOctopodes() { + if (this.octopodes != null) this.octopodes.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder rawSet(final java.lang.Object rawSet) { if (this.rawSet == null) this.rawSet = new java.util.ArrayList(); this.rawSet.add(rawSet); @@ -77,6 +89,12 @@ class BuilderSingularSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder clearRawSet() { + if (this.rawSet != null) this.rawSet.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder stringSet(final String stringSet) { if (this.stringSet == null) this.stringSet = new java.util.ArrayList(); this.stringSet.add(stringSet); @@ -91,6 +109,12 @@ class BuilderSingularSets { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder clearStringSet() { + if (this.stringSet != null) this.stringSet.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSets build() { java.util.Set dangerMice; switch (this.dangerMice == null ? 0 : this.dangerMice.size()) { diff --git a/test/transform/resource/after-delombok/BuilderWithToBuilder.java b/test/transform/resource/after-delombok/BuilderWithToBuilder.java index eb61a6db..7dfb046a 100644 --- a/test/transform/resource/after-delombok/BuilderWithToBuilder.java +++ b/test/transform/resource/after-delombok/BuilderWithToBuilder.java @@ -68,6 +68,12 @@ class BuilderWithToBuilder { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderWithToBuilderBuilder clearBars() { + if (this.bars != null) this.bars.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderWithToBuilder build() { java.util.List bars; switch (this.bars == null ? 0 : this.bars.size()) { diff --git a/test/transform/resource/after-ecj/BuilderInstanceMethod.java b/test/transform/resource/after-ecj/BuilderInstanceMethod.java new file mode 100644 index 00000000..ff7d0aab --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderInstanceMethod.java @@ -0,0 +1,43 @@ +import java.util.List; +class BuilderInstanceMethod { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") class StringBuilder { + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int show; + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int yes; + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") List also; + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") int $andMe; + @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder show(final int show) { + this.show = show; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder yes(final int yes) { + this.yes = yes; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder also(final List also) { + this.also = also; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder $andMe(final int $andMe) { + this.$andMe = $andMe; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String build() { + return BuilderInstanceMethod.this.create(show, yes, also, $andMe); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") java.lang.String toString() { + return (((((((("BuilderInstanceMethod.StringBuilder(show=" + this.show) + ", yes=") + this.yes) + ", also=") + this.also) + ", $andMe=") + this.$andMe) + ")"); + } + } + BuilderInstanceMethod() { + super(); + } + public @lombok.Builder String create(int show, final int yes, List also, int $andMe) { + return (((("" + show) + yes) + also) + $andMe); + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") StringBuilder builder() { + return new StringBuilder(); + } +} diff --git a/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java b/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java index e7aa7835..5cf9f4ac 100644 --- a/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java +++ b/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.ImmutableTable; import lombok.Singular; @lombok.Builder class BuilderSingularGuavaListsSets { public static @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") class BuilderSingularGuavaListsSetsBuilder { @@ -9,6 +10,7 @@ import lombok.Singular; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableList.Builder frogs; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableSet.Builder rawSet; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableSortedSet.Builder passes; + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableTable.Builder users; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder() { super(); } @@ -24,6 +26,10 @@ import lombok.Singular; this.cards.addAll(cards); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder clearCards() { + this.cards = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder frog(Number frog) { if ((this.frogs == null)) this.frogs = com.google.common.collect.ImmutableList.builder(); @@ -36,6 +42,10 @@ import lombok.Singular; this.frogs.addAll(frogs); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder clearFrogs() { + this.frogs = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder rawSet(java.lang.Object rawSet) { if ((this.rawSet == null)) this.rawSet = com.google.common.collect.ImmutableSet.builder(); @@ -48,6 +58,10 @@ import lombok.Singular; this.rawSet.addAll(rawSet); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder clearRawSet() { + this.rawSet = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder pass(String pass) { if ((this.passes == null)) this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder(); @@ -60,27 +74,50 @@ import lombok.Singular; this.passes.addAll(passes); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder clearPasses() { + this.passes = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder user(Number rowKey, Number columnKey, String value) { + if ((this.users == null)) + this.users = com.google.common.collect.ImmutableTable.builder(); + this.users.put(rowKey, columnKey, value); + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder users(com.google.common.collect.Table users) { + if ((this.users == null)) + this.users = com.google.common.collect.ImmutableTable.builder(); + this.users.putAll(users); + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder clearUsers() { + this.users = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets build() { com.google.common.collect.ImmutableList cards = ((this.cards == null) ? com.google.common.collect.ImmutableList.of() : this.cards.build()); com.google.common.collect.ImmutableCollection frogs = ((this.frogs == null) ? com.google.common.collect.ImmutableList.of() : this.frogs.build()); com.google.common.collect.ImmutableSet rawSet = ((this.rawSet == null) ? com.google.common.collect.ImmutableSet.of() : this.rawSet.build()); com.google.common.collect.ImmutableSortedSet passes = ((this.passes == null) ? com.google.common.collect.ImmutableSortedSet.of() : this.passes.build()); - return new BuilderSingularGuavaListsSets(cards, frogs, rawSet, passes); + com.google.common.collect.ImmutableTable users = ((this.users == null) ? com.google.common.collect.ImmutableTable.of() : this.users.build()); + return new BuilderSingularGuavaListsSets(cards, frogs, rawSet, passes, users); } public @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") java.lang.String toString() { - return (((((((("BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards) + ", frogs=") + this.frogs) + ", rawSet=") + this.rawSet) + ", passes=") + this.passes) + ")"); + return (((((((((("BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards) + ", frogs=") + this.frogs) + ", rawSet=") + this.rawSet) + ", passes=") + this.passes) + ", users=") + this.users) + ")"); } } private @Singular ImmutableList cards; private @Singular ImmutableCollection frogs; private @SuppressWarnings("all") @Singular("rawSet") ImmutableSet rawSet; private @Singular ImmutableSortedSet passes; - @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets(final ImmutableList cards, final ImmutableCollection frogs, final ImmutableSet rawSet, final ImmutableSortedSet passes) { + private @Singular ImmutableTable users; + @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets(final ImmutableList cards, final ImmutableCollection frogs, final ImmutableSet rawSet, final ImmutableSortedSet passes, final ImmutableTable users) { super(); this.cards = cards; this.frogs = frogs; this.rawSet = rawSet; this.passes = passes; + this.users = users; } public static @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder builder() { return new BuilderSingularGuavaListsSetsBuilder(); diff --git a/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java b/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java index d4dfc18a..378ec309 100644 --- a/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java +++ b/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java @@ -10,10 +10,10 @@ import lombok.Singular; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder() { super(); } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder battleaxe(K battleaxe$key, V battleaxe$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder battleaxe(K key, V value) { if ((this.battleaxes == null)) this.battleaxes = com.google.common.collect.ImmutableMap.builder(); - this.battleaxes.put(battleaxe$key, battleaxe$value); + this.battleaxes.put(key, value); return this; } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder battleaxes(java.util.Map battleaxes) { @@ -22,10 +22,14 @@ import lombok.Singular; this.battleaxes.putAll(battleaxes); return this; } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder vertex(Integer vertex$key, V vertex$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder clearBattleaxes() { + this.battleaxes = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder vertex(Integer key, V value) { if ((this.vertices == null)) this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder(); - this.vertices.put(vertex$key, vertex$value); + this.vertices.put(key, value); return this; } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder vertices(java.util.Map vertices) { @@ -34,10 +38,14 @@ import lombok.Singular; this.vertices.putAll(vertices); return this; } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder rawMap(java.lang.Object rawMap$key, java.lang.Object rawMap$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder clearVertices() { + this.vertices = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder rawMap(java.lang.Object key, java.lang.Object value) { if ((this.rawMap == null)) this.rawMap = com.google.common.collect.ImmutableBiMap.builder(); - this.rawMap.put(rawMap$key, rawMap$value); + this.rawMap.put(key, value); return this; } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder rawMap(java.util.Map rawMap) { @@ -46,6 +54,10 @@ import lombok.Singular; this.rawMap.putAll(rawMap); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder clearRawMap() { + this.rawMap = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMaps build() { com.google.common.collect.ImmutableMap battleaxes = ((this.battleaxes == null) ? com.google.common.collect.ImmutableMap.of() : this.battleaxes.build()); com.google.common.collect.ImmutableSortedMap vertices = ((this.vertices == null) ? com.google.common.collect.ImmutableSortedMap.of() : this.vertices.build()); diff --git a/test/transform/resource/after-ecj/BuilderSingularLists.java b/test/transform/resource/after-ecj/BuilderSingularLists.java index c66fcf1b..e1036262 100644 --- a/test/transform/resource/after-ecj/BuilderSingularLists.java +++ b/test/transform/resource/after-ecj/BuilderSingularLists.java @@ -22,6 +22,11 @@ import lombok.Singular; this.children.addAll(children); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder clearChildren() { + if ((this.children != null)) + this.children.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder scarf(Number scarf) { if ((this.scarves == null)) this.scarves = new java.util.ArrayList(); @@ -34,6 +39,11 @@ import lombok.Singular; this.scarves.addAll(scarves); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder clearScarves() { + if ((this.scarves != null)) + this.scarves.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder rawList(java.lang.Object rawList) { if ((this.rawList == null)) this.rawList = new java.util.ArrayList(); @@ -46,6 +56,11 @@ import lombok.Singular; this.rawList.addAll(rawList); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder clearRawList() { + if ((this.rawList != null)) + this.rawList.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularLists build() { java.util.List children; switch (((this.children == null) ? 0 : this.children.size())) { diff --git a/test/transform/resource/after-ecj/BuilderSingularMaps.java b/test/transform/resource/after-ecj/BuilderSingularMaps.java index 8a2e14eb..38ac0ed7 100644 --- a/test/transform/resource/after-ecj/BuilderSingularMaps.java +++ b/test/transform/resource/after-ecj/BuilderSingularMaps.java @@ -37,6 +37,14 @@ import lombok.Singular; } return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder clearWomen() { + if ((this.women$key != null)) + { + this.women$key.clear(); + this.women$value.clear(); + } + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder man(K manKey, Number manValue) { if ((this.men$key == null)) { @@ -60,6 +68,14 @@ import lombok.Singular; } return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder clearMen() { + if ((this.men$key != null)) + { + this.men$key.clear(); + this.men$value.clear(); + } + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder rawMap(java.lang.Object rawMapKey, java.lang.Object rawMapValue) { if ((this.rawMap$key == null)) { @@ -83,6 +99,14 @@ import lombok.Singular; } return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder clearRawMap() { + if ((this.rawMap$key != null)) + { + this.rawMap$key.clear(); + this.rawMap$value.clear(); + } + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder stringMap(String stringMapKey, V stringMapValue) { if ((this.stringMap$key == null)) { @@ -106,6 +130,14 @@ import lombok.Singular; } return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMapsBuilder clearStringMap() { + if ((this.stringMap$key != null)) + { + this.stringMap$key.clear(); + this.stringMap$value.clear(); + } + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularMaps build() { java.util.Map women; switch (((this.women$key == null) ? 0 : this.women$key.size())) { diff --git a/test/transform/resource/after-ecj/BuilderSingularNoAuto.java b/test/transform/resource/after-ecj/BuilderSingularNoAuto.java index d5b06f1e..1b79538c 100644 --- a/test/transform/resource/after-ecj/BuilderSingularNoAuto.java +++ b/test/transform/resource/after-ecj/BuilderSingularNoAuto.java @@ -20,6 +20,11 @@ import lombok.Singular; this.things.addAll(things); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAutoBuilder clearThings() { + if ((this.things != null)) + this.things.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAutoBuilder widget(String widget) { if ((this.widgets == null)) this.widgets = new java.util.ArrayList(); @@ -32,6 +37,11 @@ import lombok.Singular; this.widgets.addAll(widgets); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAutoBuilder clearWidgets() { + if ((this.widgets != null)) + this.widgets.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAutoBuilder items(String items) { if ((this.items == null)) this.items = new java.util.ArrayList(); @@ -44,6 +54,11 @@ import lombok.Singular; this.items.addAll(items); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAutoBuilder clearItems() { + if ((this.items != null)) + this.items.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularNoAuto build() { java.util.List things; switch (((this.things == null) ? 0 : this.things.size())) { diff --git a/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java b/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java index 6e18d8ee..84335f46 100644 --- a/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java +++ b/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java @@ -22,10 +22,14 @@ import lombok.Singular; this.dangerMice.addAll(dangerMice); return this; } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder thing(Integer thing$key, Number thing$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder clearDangerMice() { + this.dangerMice = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder thing(Integer key, Number value) { if ((this.things == null)) this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder(); - this.things.put(thing$key, thing$value); + this.things.put(key, value); return this; } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder things(java.util.Map things) { @@ -34,6 +38,10 @@ import lombok.Singular; this.things.putAll(things); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder clearThings() { + this.things = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder doohickey(Class doohickey) { if ((this.doohickeys == null)) this.doohickeys = com.google.common.collect.ImmutableList.builder(); @@ -46,6 +54,10 @@ import lombok.Singular; this.doohickeys.addAll(doohickeys); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuavaBuilder clearDoohickeys() { + this.doohickeys = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularRedirectToGuava build() { java.util.Set dangerMice = ((this.dangerMice == null) ? com.google.common.collect.ImmutableSet.of() : this.dangerMice.build()); java.util.NavigableMap things = ((this.things == null) ? com.google.common.collect.ImmutableSortedMap.of() : this.things.build()); diff --git a/test/transform/resource/after-ecj/BuilderSingularSets.java b/test/transform/resource/after-ecj/BuilderSingularSets.java index 819de534..118eb16f 100644 --- a/test/transform/resource/after-ecj/BuilderSingularSets.java +++ b/test/transform/resource/after-ecj/BuilderSingularSets.java @@ -22,6 +22,11 @@ import lombok.Singular; this.dangerMice.addAll(dangerMice); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder clearDangerMice() { + if ((this.dangerMice != null)) + this.dangerMice.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder octopus(Number octopus) { if ((this.octopodes == null)) this.octopodes = new java.util.ArrayList(); @@ -34,6 +39,11 @@ import lombok.Singular; this.octopodes.addAll(octopodes); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder clearOctopodes() { + if ((this.octopodes != null)) + this.octopodes.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder rawSet(java.lang.Object rawSet) { if ((this.rawSet == null)) this.rawSet = new java.util.ArrayList(); @@ -46,6 +56,11 @@ import lombok.Singular; this.rawSet.addAll(rawSet); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder clearRawSet() { + if ((this.rawSet != null)) + this.rawSet.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder stringSet(String stringSet) { if ((this.stringSet == null)) this.stringSet = new java.util.ArrayList(); @@ -58,6 +73,11 @@ import lombok.Singular; this.stringSet.addAll(stringSet); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder clearStringSet() { + if ((this.stringSet != null)) + this.stringSet.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSets build() { java.util.Set dangerMice; switch (((this.dangerMice == null) ? 0 : this.dangerMice.size())) { diff --git a/test/transform/resource/after-ecj/BuilderWithToBuilder.java b/test/transform/resource/after-ecj/BuilderWithToBuilder.java index 423865ff..0d296cb6 100644 --- a/test/transform/resource/after-ecj/BuilderWithToBuilder.java +++ b/test/transform/resource/after-ecj/BuilderWithToBuilder.java @@ -33,6 +33,11 @@ import lombok.Builder; this.bars.addAll(bars); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderWithToBuilderBuilder clearBars() { + if ((this.bars != null)) + this.bars.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderWithToBuilder build() { java.util.List bars; switch (((this.bars == null) ? 0 : this.bars.size())) { diff --git a/test/transform/resource/before/BuilderInstanceMethod.java b/test/transform/resource/before/BuilderInstanceMethod.java new file mode 100644 index 00000000..666664a2 --- /dev/null +++ b/test/transform/resource/before/BuilderInstanceMethod.java @@ -0,0 +1,8 @@ +import java.util.List; + +class BuilderInstanceMethod { + @lombok.Builder + public String create(int show, final int yes, List also, int $andMe) { + return "" + show + yes + also + $andMe; + } +} diff --git a/test/transform/resource/before/BuilderSingularGuavaListsSets.java b/test/transform/resource/before/BuilderSingularGuavaListsSets.java index 995c00e8..73e8018c 100644 --- a/test/transform/resource/before/BuilderSingularGuavaListsSets.java +++ b/test/transform/resource/before/BuilderSingularGuavaListsSets.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.ImmutableTable; import lombok.Singular; @@ -11,4 +12,5 @@ class BuilderSingularGuavaListsSets { @Singular private ImmutableCollection frogs; @SuppressWarnings("all") @Singular("rawSet") private ImmutableSet rawSet; @Singular private ImmutableSortedSet passes; + @Singular private ImmutableTable users; } diff --git a/usage_examples/BuilderExample_post.jpage b/usage_examples/BuilderExample_post.jpage index 8a1d1e69..54b064d7 100644 --- a/usage_examples/BuilderExample_post.jpage +++ b/usage_examples/BuilderExample_post.jpage @@ -51,6 +51,14 @@ public class BuilderExample { return this; } + public BuilderExampleBuilder clearOccupations() { + if (this.occupations != null) { + this.occupations.clear(); + } + + return this; + } + public BuilderExample build() { // complicated switch statement to produce a compact properly sized immutable set omitted. // go to https://projectlombok.org/features/Singular-snippet.html to see it. diff --git a/website/features/Builder.html b/website/features/Builder.html index 6cf46600..88083a27 100644 --- a/website/features/Builder.html +++ b/website/features/Builder.html @@ -18,7 +18,8 @@ @Builder was introduced as experimental feature in lombok v0.12.0.

@Builder gained @Singular support and was promoted to the main lombok package since lombok v1.16.0. -

+

+ @Builder with @Singular adds a clear method since lombok v1.16.8.

Overview

@@ -28,16 +29,16 @@ @Builder lets you automatically produce the code required to have your class be instantiable with code such as:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();

- @Builder can be placed on a class, or on a constructor, or on a static method. While the "on a class" and "on a constructor" - mode are the most common use-case, @Builder is most easily explained with the "static method" use-case. + @Builder can be placed on a class, or on a constructor, or on a method. While the "on a class" and "on a constructor" + mode are the most common use-case, @Builder is most easily explained with the "method" use-case.

- A static method annotated with @Builder (from now on called the target) causes the following 7 things to be generated:

    -
  • An inner static class named FooBuilder, with the same type arguments as the static method (called the builder).
  • + A method annotated with @Builder (from now on called the target) causes the following 7 things to be generated:
      +
    • An inner class named FooBuilder, with the same type arguments as the method (called the builder).
    • In the builder: One private non-static non-final field for each parameter of the target.
    • In the builder: A package private no-args empty constructor.
    • In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example.
    • -
    • In the builder: A build() method which calls the static method, passing in each field. It returns the same type that the +
    • In the builder: A build() method which calls the method, passing in each field. It returns the same type that the target returns.
    • In the builder: A sensible toString() implementation.
    • In the class containing the target: A static builder() method, which creates a new instance of the builder.
    • @@ -51,7 +52,7 @@ element to the list. For example: Person.builder().job("Mythbusters").job("Unchained Reaction").build(); would result in the List<String> jobs field to have 2 strings in it. To get this behaviour, the field/parameter needs to be annotated with @Singular. The feature has its own documentation.

      - Now that the "static method" mode is clear, putting a @Builder annotation on a constructor functions similarly; effectively, + Now that the "method" mode is clear, putting a @Builder annotation on a constructor functions similarly; effectively, constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their type parameters are the same as the type parameters of the class itself.

      @@ -63,8 +64,8 @@

      The name of the builder class is FoobarBuilder, where Foobar is the simplified, title-cased form of the return type of the target - that is, the name of your type for @Builder on constructors and types, and the name of the return type for @Builder - on static methods. For example, if @Builder is applied to a class named com.yoyodyne.FancyList<T>, then the builder name will be - FancyListBuilder<T>. If @Builder is applied to a static method that returns void, the builder will be named + on methods. For example, if @Builder is applied to a class named com.yoyodyne.FancyList<T>, then the builder name will be + FancyListBuilder<T>. If @Builder is applied to a method that returns void, the builder will be named VoidBuilder.

      The configurable aspects of builder are:

        @@ -80,13 +81,13 @@

        @Singular

        - By annotating one of the parameters (if annotating a static method or constructor with @Builder) or fields (if annotating a class with @Builder) with the + By annotating one of the parameters (if annotating a method or constructor with @Builder) or fields (if annotating a class with @Builder) with the @Singular annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one - which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. These 'singular' builders + which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. A 'clear' method is also generated. These 'singular' builders are very complicated in order to guarantee the following properties:

        • When invoking build(), the produced collection will be immutable.
        • -
        • Calling one of the 'adder' methods after invoking build() does not modify any already generated objects, and, if build() is later called again, another collection with all the elements added since the creation of the builder is generated.
        • +
        • Calling one of the 'adder' methods, or the 'clear' method, after invoking build() does not modify any already generated objects, and, if build() is later called again, another collection with all the elements added since the creation of the builder is generated.
        • The produced collection will be compacted to the smallest feasible format while remaining efficient.

        @@ -101,6 +102,7 @@

      • ImmutableCollection and ImmutableList (backed by the builder feature of ImmutableList).
      • ImmutableSet and ImmutableSortedSet (backed by the builder feature of those types).
      • ImmutableMap, ImmutableBiMap, and ImmutableSortedMap (backed by the builder feature of those types).
      • +
      • ImmutableTable (backed by the builder feature of ImmutableTable).