diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-11-17 02:15:31 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-11-17 02:15:31 +0100 |
commit | 0bd688d7e624d95fef921f8b463209d70b92779a (patch) | |
tree | 19c7dc3d6e996ee96cfe4c02fbe6d651945885a7 | |
parent | 0e34eec7668ee060fe65133243009dbb27e7c251 (diff) | |
parent | 5b6829b86379321be378490b895827cd6bca13f1 (diff) | |
download | lombok-0bd688d7e624d95fef921f8b463209d70b92779a.tar.gz lombok-0bd688d7e624d95fef921f8b463209d70b92779a.tar.bz2 lombok-0bd688d7e624d95fef921f8b463209d70b92779a.zip |
Merge branch 'builderUpdate'
41 files changed, 1005 insertions, 208 deletions
@@ -3,6 +3,7 @@ Lombok contributors in alphabetical order: Christian Sterzl <christian.sterzl@gmail.com> DaveLaw <project.lombok@apconsult.de> Dawid Rusin <dawidrusin90@gmail.com> +Enrique da Costa Cambio <enrique.dacostacambio@gmail.com> Jappe van der Hel <jappe.vanderhel@gmail.com> Luan Nico <luannico27@gmail.com> Maarten Mulders <mthmulders@users.noreply.github.com> @@ -12,6 +13,7 @@ Reinier Zwitserloot <reinier@zwitserloot.com> Robbert Jan Grootjans <grootjans@gmail.com> Roel Spilker <r.spilker@gmail.com> Sander Koning <askoning@gmail.com> +Szymon Pacanowski <spacanowski@gmail.com> Taiki Sugawara <buzz.taiki@gmail.com> Yun Zhi Lin <yun@yunspace.com> 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}. * <p> - * 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. * <p> * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>, - * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static + * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). * <p> * The <code><strong>T</strong>Builder</code> 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 <code>build()</code> 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. * <p> * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>. @@ -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}. * <p> - * 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<Builder> { 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<Builder> { } 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<Builder> { 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<Builder> { 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<Builder> { } 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<Builder> { } 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<Builder> { return decl; } - public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { + public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> 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<Statement> statements = new ArrayList<Statement>(); @@ -552,7 +559,10 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } 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<Builder> { 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<Builder> { 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<String> SUFFIXES = + LombokImmutableList.of("key", "value"); + private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableMap", + "com.google.common.collect.ImmutableBiMap", + "com.google.common.collect.ImmutableSortedMap" + ); + @Override public LombokImmutableList<String> getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableMap", - "com.google.common.collect.ImmutableBiMap", - "com.google.common.collect.ImmutableSortedMap"); + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList<String> getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; } - @Override protected boolean isMap() { - return true; + @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<String> SUFFIXES = LombokImmutableList.of(""); + private static final LombokImmutableList<String> 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<String> 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<String> 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<EclipseNode> 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<String> 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<Statement> 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<String> 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<String> SUFFIXES = + LombokImmutableList.of("rowKey", "columnKey", "value"); + private static final LombokImmutableList<String> SUPPORTED_TYPES = + LombokImmutableList.of("com.google.common.collect.ImmutableTable"); + + @Override public LombokImmutableList<String> getSupportedTypes() { + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList<String> 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}. * <p> - * 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. * <p> * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>, - * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static + * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). * <p> * The <code><strong>T</strong>Builder</code> 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 <code>build()</code> 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. * <p> * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>. @@ -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<Builder> { JCExpression returnType; List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> 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<Builder> { 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("<init>")) { JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); @@ -171,21 +174,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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<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; } @@ -298,8 +298,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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<Builder> { } 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<Builder> { 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<Builder> { */ } - private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) { + private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -516,16 +524,19 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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.<JCExpression>nil(), returnType, args.toList(), null); statements.append(maker.Return(call)); } else { + ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>(); 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<Builder> { JCBlock body = maker.Block(0, statements.toList()); - return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); + return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); } - public JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) { + public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); @@ -551,7 +562,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCStatement statement = maker.Return(call); JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); - return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>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.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) { @@ -615,9 +628,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return null; } - public JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(boolean isStatic, JavacNode tdParent, String builderClassName, List<JCTypeParameter> 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.<JCExpression>nil(), List.<JCTree>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<String> SUFFIXES = + LombokImmutableList.of("key", "value"); + private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of( + "com.google.common.collect.ImmutableMap", + "com.google.common.collect.ImmutableBiMap", + "com.google.common.collect.ImmutableSortedMap" + ); + @Override public LombokImmutableList<String> getSupportedTypes() { - return LombokImmutableList.of( - "com.google.common.collect.ImmutableMap", - "com.google.common.collect.ImmutableBiMap", - "com.google.common.collect.ImmutableSortedMap"); + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList<String> getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "put"; } - @Override protected boolean isMap() { - return true; + @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<String> SUFFIXES = LombokImmutableList.of(""); + private static final LombokImmutableList<String> 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<String> 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 LombokImmutableList<String> getArgumentSuffixes() { + return SUFFIXES; + } + + @Override protected String getAddMethodName() { + return "add"; } - @Override protected boolean isMap() { - return false; + @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<JavacNode> 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<JCTypeParameter> typeParams = List.nil(); + List<JCExpression> thrown = List.nil(); + List<JCVariableDecl> 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<JCStatement> 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<JCTypeParameter> typeParams = List.nil(); List<JCExpression> 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<String> 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<JCStatement> statements = new ListBuffer<JCStatement>(); - statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source)); - JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "put" : "add"); - List<JCExpression> invokeAddExpr; - if (mapMode) { - invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName), maker.Ident(valueName)); - } else { - invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName)); + statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); + JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName()); + ListBuffer<JCExpression> invokeAddExprBuilder = new ListBuffer<JCExpression>(); + for (int i = 0; i < suffixes.size(); i++) { + invokeAddExprBuilder.append(maker.Ident(names[i])); } + List<JCExpression> invokeAddExpr = invokeAddExprBuilder.toList(); JCExpression invokeAdd = maker.Apply(List.<JCExpression>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<JCVariableDecl> 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<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); + 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<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - boolean mapMode = isMap(); JCModifiers mods = maker.Modifiers(Flags.PUBLIC); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - 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.<JCExpression>nil(), thisDotFieldDotAddAll, List.<JCExpression>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<JCStatement> statements, Name targetVariableName) { JavacTreeMaker maker = builderType.getTreeMaker(); List<JCExpression> 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<JCExpression> invokeTypeArgs = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs(), source); + List<JCExpression> 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<JCExpression> 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<String> 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<String> SUFFIXES = + LombokImmutableList.of("rowKey", "columnKey", "value"); + private static final LombokImmutableList<String> SUPPORTED_TYPES = + LombokImmutableList.of("com.google.common.collect.ImmutableTable"); + + @Override public LombokImmutableList<String> getSupportedTypes() { + return SUPPORTED_TYPES; + } + + @Override protected LombokImmutableList<String> 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<JCTypeParameter> typeParams = List.nil(); + List<JCExpression> thrown = List.nil(); + List<JCVariableDecl> params = List.nil(); + List<JCExpression> 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<JCStatement> 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<JCTypeParameter> typeParams = List.nil(); + List<JCExpression> thrown = List.nil(); + List<JCVariableDecl> params = List.nil(); + List<JCExpression> 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<JCStatement> 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<T> { + public String create(int show, final int yes, List<T> 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<T> 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<T> 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<T> { private ImmutableList<T> cards; private ImmutableCollection<? extends Number> frogs; @SuppressWarnings("all") private ImmutableSet rawSet; private ImmutableSortedSet<String> passes; + private ImmutableTable<? extends Number, ? extends Number, String> users; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes) { + BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes, final ImmutableTable<? extends Number, ? extends Number, String> 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<T> { private com.google.common.collect.ImmutableSortedSet.Builder<String> passes; @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + private com.google.common.collect.ImmutableTable.Builder<Number, Number, String> users; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder() { } @java.lang.SuppressWarnings("all") @@ -51,6 +57,12 @@ class BuilderSingularGuavaListsSets<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder<T> clearCards() { + this.cards = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder<T> 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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder<T> clearFrogs() { + this.frogs = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder<T> 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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder<T> clearRawSet() { + this.rawSet = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSetsBuilder<T> 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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder<T> clearPasses() { + this.passes = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaListsSetsBuilder<T> 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<T> users(final com.google.common.collect.Table<? extends Number, ? extends Number, ? extends String> 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<T> clearUsers() { + this.users = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaListsSets<T> build() { com.google.common.collect.ImmutableList<T> cards = this.cards == null ? com.google.common.collect.ImmutableList.<T>of() : this.cards.build(); com.google.common.collect.ImmutableCollection<Number> frogs = this.frogs == null ? com.google.common.collect.ImmutableList.<Number>of() : this.frogs.build(); com.google.common.collect.ImmutableSet<java.lang.Object> rawSet = this.rawSet == null ? com.google.common.collect.ImmutableSet.<java.lang.Object>of() : this.rawSet.build(); com.google.common.collect.ImmutableSortedSet<String> passes = this.passes == null ? com.google.common.collect.ImmutableSortedSet.<String>of() : this.passes.build(); - return new BuilderSingularGuavaListsSets<T>(cards, frogs, rawSet, passes); + com.google.common.collect.ImmutableTable<Number, Number, String> users = this.users == null ? com.google.common.collect.ImmutableTable.<Number, Number, String>of() : this.users.build(); + return new BuilderSingularGuavaListsSets<T>(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<K, V> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder<K, V> battleaxe(final K battleaxe$key, final V battleaxe$value) { + public BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder<K, V> vertex(final Integer vertex$key, final V vertex$value) { + public BuilderSingularGuavaMapsBuilder<K, V> clearBattleaxes() { + this.battleaxes = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") - public BuilderSingularGuavaMapsBuilder<K, V> rawMap(final java.lang.Object rawMap$key, final java.lang.Object rawMap$value) { + public BuilderSingularGuavaMapsBuilder<K, V> clearVertices() { + this.vertices = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularGuavaMapsBuilder<K, V> clearRawMap() { + this.rawMap = null; + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularGuavaMaps<K, V> build() { com.google.common.collect.ImmutableMap<K, V> battleaxes = this.battleaxes == null ? com.google.common.collect.ImmutableMap.<K, V>of() : this.battleaxes.build(); com.google.common.collect.ImmutableSortedMap<Integer, V> vertices = this.vertices == null ? com.google.common.collect.ImmutableSortedMap.<Integer, V>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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder<T> clearChildren() { + if (this.children != null) this.children.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularListsBuilder<T> scarf(final Number scarf) { if (this.scarves == null) this.scarves = new java.util.ArrayList<Number>(); this.scarves.add(scarf); @@ -58,6 +64,12 @@ class BuilderSingularLists<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder<T> clearScarves() { + if (this.scarves != null) this.scarves.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularListsBuilder<T> rawList(final java.lang.Object rawList) { if (this.rawList == null) this.rawList = new java.util.ArrayList<java.lang.Object>(); this.rawList.add(rawList); @@ -72,6 +84,12 @@ class BuilderSingularLists<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularListsBuilder<T> clearRawList() { + if (this.rawList != null) this.rawList.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularLists<T> build() { java.util.List<T> 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<K, V> { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder<K, V> clearWomen() { + if (this.women$key != null) { + this.women$key.clear(); + this.women$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder<K, V> man(K manKey, Number manValue) { if (this.men$key == null) { this.men$key = new java.util.ArrayList<K>(); @@ -79,6 +87,14 @@ class BuilderSingularMaps<K, V> { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder<K, V> clearMen() { + if (this.men$key != null) { + this.men$key.clear(); + this.men$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder<K, V> rawMap(Object rawMapKey, Object rawMapValue) { if (this.rawMap$key == null) { this.rawMap$key = new java.util.ArrayList<Object>(); @@ -101,6 +117,14 @@ class BuilderSingularMaps<K, V> { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder<K, V> clearRawMap() { + if (this.rawMap$key != null) { + this.rawMap$key.clear(); + this.rawMap$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMapsBuilder<K, V> stringMap(String stringMapKey, V stringMapValue) { if (this.stringMap$key == null) { this.stringMap$key = new java.util.ArrayList<String>(); @@ -123,6 +147,14 @@ class BuilderSingularMaps<K, V> { return this; } @SuppressWarnings("all") + public BuilderSingularMapsBuilder<K, V> clearStringMap() { + if (this.stringMap$key != null) { + this.stringMap$key.clear(); + this.stringMap$value.clear(); + } + return this; + } + @SuppressWarnings("all") public BuilderSingularMaps<K, V> build() { java.util.Map<K, V> 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<String>(); 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<String>(); 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<String> 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<String> dangerMice = this.dangerMice == null ? com.google.common.collect.ImmutableSet.<String>of() : this.dangerMice.build(); java.util.NavigableMap<Integer, Number> things = this.things == null ? com.google.common.collect.ImmutableSortedMap.<Integer, Number>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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder<T> clearDangerMice() { + if (this.dangerMice != null) this.dangerMice.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder<T> octopus(final Number octopus) { if (this.octopodes == null) this.octopodes = new java.util.ArrayList<Number>(); this.octopodes.add(octopus); @@ -63,6 +69,12 @@ class BuilderSingularSets<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder<T> clearOctopodes() { + if (this.octopodes != null) this.octopodes.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder<T> rawSet(final java.lang.Object rawSet) { if (this.rawSet == null) this.rawSet = new java.util.ArrayList<java.lang.Object>(); this.rawSet.add(rawSet); @@ -77,6 +89,12 @@ class BuilderSingularSets<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder<T> clearRawSet() { + if (this.rawSet != null) this.rawSet.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSetsBuilder<T> stringSet(final String stringSet) { if (this.stringSet == null) this.stringSet = new java.util.ArrayList<String>(); this.stringSet.add(stringSet); @@ -91,6 +109,12 @@ class BuilderSingularSets<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderSingularSetsBuilder<T> clearStringSet() { + if (this.stringSet != null) this.stringSet.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderSingularSets<T> build() { java.util.Set<T> 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<T> { } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") + public BuilderWithToBuilderBuilder<T> clearBars() { + if (this.bars != null) this.bars.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") public BuilderWithToBuilder<T> build() { java.util.List<T> 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<T> { + 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<T> 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<T> 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<T> 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<T> { public static @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") class BuilderSingularGuavaListsSetsBuilder<T> { @@ -9,6 +10,7 @@ import lombok.Singular; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableList.Builder<Number> frogs; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableSet.Builder<java.lang.Object> rawSet; private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableSortedSet.Builder<String> passes; + private @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") com.google.common.collect.ImmutableTable.Builder<Number, Number, String> 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<T> clearCards() { + this.cards = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder<T> 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<T> clearFrogs() { + this.frogs = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder<T> 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<T> clearRawSet() { + this.rawSet = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder<T> 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<T> clearPasses() { + this.passes = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSetsBuilder<T> 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<T> users(com.google.common.collect.Table<? extends Number, ? extends Number, ? extends String> 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<T> clearUsers() { + this.users = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets<T> build() { com.google.common.collect.ImmutableList<T> cards = ((this.cards == null) ? com.google.common.collect.ImmutableList.<T>of() : this.cards.build()); com.google.common.collect.ImmutableCollection<Number> frogs = ((this.frogs == null) ? com.google.common.collect.ImmutableList.<Number>of() : this.frogs.build()); com.google.common.collect.ImmutableSet<java.lang.Object> rawSet = ((this.rawSet == null) ? com.google.common.collect.ImmutableSet.<java.lang.Object>of() : this.rawSet.build()); com.google.common.collect.ImmutableSortedSet<String> passes = ((this.passes == null) ? com.google.common.collect.ImmutableSortedSet.<String>of() : this.passes.build()); - return new BuilderSingularGuavaListsSets<T>(cards, frogs, rawSet, passes); + com.google.common.collect.ImmutableTable<Number, Number, String> users = ((this.users == null) ? com.google.common.collect.ImmutableTable.<Number, Number, String>of() : this.users.build()); + return new BuilderSingularGuavaListsSets<T>(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<T> cards; private @Singular ImmutableCollection<? extends Number> frogs; private @SuppressWarnings("all") @Singular("rawSet") ImmutableSet rawSet; private @Singular ImmutableSortedSet<String> passes; - @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes) { + private @Singular ImmutableTable<? extends Number, ? extends Number, String> users; + @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes, final ImmutableTable<? extends Number, ? extends Number, String> 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") <T>BuilderSingularGuavaListsSetsBuilder<T> builder() { return new BuilderSingularGuavaListsSetsBuilder<T>(); 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<K, V> battleaxe(K battleaxe$key, V battleaxe$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> battleaxes(java.util.Map<? extends K, ? extends V> battleaxes) { @@ -22,10 +22,14 @@ import lombok.Singular; this.battleaxes.putAll(battleaxes); return this; } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> vertex(Integer vertex$key, V vertex$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> clearBattleaxes() { + this.battleaxes = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> vertices(java.util.Map<? extends Integer, ? extends V> vertices) { @@ -34,10 +38,14 @@ import lombok.Singular; this.vertices.putAll(vertices); return this; } - public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> rawMap(java.lang.Object rawMap$key, java.lang.Object rawMap$value) { + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> clearVertices() { + this.vertices = null; + return this; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMapsBuilder<K, V> 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<K, V> 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<K, V> clearRawMap() { + this.rawMap = null; + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularGuavaMaps<K, V> build() { com.google.common.collect.ImmutableMap<K, V> battleaxes = ((this.battleaxes == null) ? com.google.common.collect.ImmutableMap.<K, V>of() : this.battleaxes.build()); com.google.common.collect.ImmutableSortedMap<Integer, V> vertices = ((this.vertices == null) ? com.google.common.collect.ImmutableSortedMap.<Integer, V>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<T> clearChildren() { + if ((this.children != null)) + this.children.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder<T> scarf(Number scarf) { if ((this.scarves == null)) this.scarves = new java.util.ArrayList<Number>(); @@ -34,6 +39,11 @@ import lombok.Singular; this.scarves.addAll(scarves); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder<T> clearScarves() { + if ((this.scarves != null)) + this.scarves.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder<T> rawList(java.lang.Object rawList) { if ((this.rawList == null)) this.rawList = new java.util.ArrayList<java.lang.Object>(); @@ -46,6 +56,11 @@ import lombok.Singular; this.rawList.addAll(rawList); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularListsBuilder<T> clearRawList() { + if ((this.rawList != null)) + this.rawList.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularLists<T> build() { java.util.List<T> 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<K, V> 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<K, V> 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<K, V> 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<K, V> 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<K, V> 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<K, V> 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<K, V> 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<K, V> build() { java.util.Map<K, V> 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<String>(); @@ -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<String>(); @@ -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<String> 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<? extends Integer, ? extends Number> 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<String> dangerMice = ((this.dangerMice == null) ? com.google.common.collect.ImmutableSet.<String>of() : this.dangerMice.build()); java.util.NavigableMap<Integer, Number> things = ((this.things == null) ? com.google.common.collect.ImmutableSortedMap.<Integer, Number>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<T> clearDangerMice() { + if ((this.dangerMice != null)) + this.dangerMice.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> octopus(Number octopus) { if ((this.octopodes == null)) this.octopodes = new java.util.ArrayList<Number>(); @@ -34,6 +39,11 @@ import lombok.Singular; this.octopodes.addAll(octopodes); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> clearOctopodes() { + if ((this.octopodes != null)) + this.octopodes.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> rawSet(java.lang.Object rawSet) { if ((this.rawSet == null)) this.rawSet = new java.util.ArrayList<java.lang.Object>(); @@ -46,6 +56,11 @@ import lombok.Singular; this.rawSet.addAll(rawSet); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> clearRawSet() { + if ((this.rawSet != null)) + this.rawSet.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> stringSet(String stringSet) { if ((this.stringSet == null)) this.stringSet = new java.util.ArrayList<String>(); @@ -58,6 +73,11 @@ import lombok.Singular; this.stringSet.addAll(stringSet); return this; } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSetsBuilder<T> clearStringSet() { + if ((this.stringSet != null)) + this.stringSet.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderSingularSets<T> build() { java.util.Set<T> 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<T> clearBars() { + if ((this.bars != null)) + this.bars.clear(); + return this; + } public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") BuilderWithToBuilder<T> build() { java.util.List<T> 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<T> { + @lombok.Builder + public String create(int show, final int yes, List<T> 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<T> { @Singular private ImmutableCollection<? extends Number> frogs; @SuppressWarnings("all") @Singular("rawSet") private ImmutableSet rawSet; @Singular private ImmutableSortedSet<String> passes; + @Singular private ImmutableTable<? extends Number, ? extends Number, String> 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 @@ <code>@Builder</code> was introduced as experimental feature in lombok v0.12.0. </p><p> <code>@Builder</code> gained <code>@Singular</code> support and was promoted to the main <code>lombok</code> package since lombok v1.16.0. - </p> + </p><p> + <code>@Builder</code> with <code>@Singular</code> adds a clear method since lombok v1.16.8. </div> <div class="overview"> <h3>Overview</h3> @@ -28,16 +29,16 @@ <code>@Builder</code> lets you automatically produce the code required to have your class be instantiable with code such as:<br /> <code>Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();</code> </p><p> - <code>@Builder</code> 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, <code>@Builder</code> is most easily explained with the "static method" use-case. + <code>@Builder</code> 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, <code>@Builder</code> is most easily explained with the "method" use-case. </p><p> - A static method annotated with <code>@Builder</code> (from now on called the <em>target</em>) causes the following 7 things to be generated:<ul> - <li>An inner static class named <code><em>Foo</em>Builder</code>, with the same type arguments as the static method (called the <em>builder</em>).</li> + A method annotated with <code>@Builder</code> (from now on called the <em>target</em>) causes the following 7 things to be generated:<ul> + <li>An inner class named <code><em>Foo</em>Builder</code>, with the same type arguments as the method (called the <em>builder</em>).</li> <li>In the <em>builder</em>: One private non-static non-final field for each parameter of the <em>target</em>.</li> <li>In the <em>builder</em>: A package private no-args empty constructor.</li> <li>In the <em>builder</em>: A 'setter'-like method for each parameter of the <em>target</em>: 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.</li> - <li>In the <em>builder</em>: A <code>build()</code> method which calls the static method, passing in each field. It returns the same type that the + <li>In the <em>builder</em>: A <code>build()</code> method which calls the method, passing in each field. It returns the same type that the <em>target</em> returns.</li> <li>In the <em>builder</em>: A sensible <code>toString()</code> implementation.</li> <li>In the class containing the <em>target</em>: A static <code>builder()</code> method, which creates a new instance of the <em>builder</em>.</li> @@ -51,7 +52,7 @@ element to the list. For example: <code>Person.builder().job("Mythbusters").job("Unchained Reaction").build();</code> would result in the <code>List<String> jobs</code> field to have 2 strings in it. To get this behaviour, the field/parameter needs to be annotated with <code>@Singular</code>. The feature has <a href="#singular">its own documentation</a>. </p><p> - Now that the "static method" mode is clear, putting a <code>@Builder</code> annotation on a constructor functions similarly; effectively, + Now that the "method" mode is clear, putting a <code>@Builder</code> 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. </p><p> @@ -63,8 +64,8 @@ </p><p> The name of the builder class is <code><em>Foobar</em>Builder</code>, where <em>Foobar</em> is the simplified, title-cased form of the return type of the <em>target</em> - that is, the name of your type for <code>@Builder</code> on constructors and types, and the name of the return type for <code>@Builder</code> - on static methods. For example, if <code>@Builder</code> is applied to a class named <code>com.yoyodyne.FancyList<T></code>, then the builder name will be - <code>FancyListBuilder<T></code>. If <code>@Builder</code> is applied to a static method that returns <code>void</code>, the builder will be named + on methods. For example, if <code>@Builder</code> is applied to a class named <code>com.yoyodyne.FancyList<T></code>, then the builder name will be + <code>FancyListBuilder<T></code>. If <code>@Builder</code> is applied to a method that returns <code>void</code>, the builder will be named <code>VoidBuilder</code>. </p><p> The configurable aspects of builder are:<ul> @@ -80,13 +81,13 @@ <div class="overview"> <h3><a name="singular">@Singular</a></h3> <p> - By annotating one of the parameters (if annotating a static method or constructor with <code>@Builder</code>) or fields (if annotating a class with <code>@Builder</code>) with the + By annotating one of the parameters (if annotating a method or constructor with <code>@Builder</code>) or fields (if annotating a class with <code>@Builder</code>) with the <code>@Singular</code> 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: <ul> <li>When invoking <code>build()</code>, the produced collection will be immutable.</li> - <li>Calling one of the 'adder' methods after invoking <code>build()</code> does not modify any already generated objects, and, if <code>build()</code> is later called again, another collection with all the elements added since the creation of the builder is generated.</li> + <li>Calling one of the 'adder' methods, or the 'clear' method, after invoking <code>build()</code> does not modify any already generated objects, and, if <code>build()</code> is later called again, another collection with all the elements added since the creation of the builder is generated.</li> <li>The produced collection will be compacted to the smallest feasible format while remaining efficient.</li> </ul> </p><p> @@ -101,6 +102,7 @@ <li><code>ImmutableCollection</code> and <code>ImmutableList</code> (backed by the builder feature of <code>ImmutableList</code>).</li> <li><code>ImmutableSet</code> and <code>ImmutableSortedSet</code> (backed by the builder feature of those types).</li> <li><code>ImmutableMap</code>, <code>ImmutableBiMap</code>, and <code>ImmutableSortedMap</code> (backed by the builder feature of those types).</li> + <li><code>ImmutableTable</code> (backed by the builder feature of <code>ImmutableTable</code>).</li> </ul></li> </ul> </p><p> |