From 91a40b83125808d3684ce07c5cb4a2927d0b979c Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Sat, 18 Jan 2020 03:10:07 +0100 Subject: [singular][issue #2221] the plural builder method now nullchecks its argument with configurable results. --- src/core/lombok/ConfigurationKeys.java | 29 +++- src/core/lombok/Singular.java | 64 +++++++++ .../core/configuration/NullCheckExceptionType.java | 4 +- src/core/lombok/core/handlers/HandlerUtil.java | 7 +- .../eclipse/handlers/EclipseSingularsRecipes.java | 59 +++++++- .../lombok/eclipse/handlers/HandleBuilder.java | 18 ++- .../eclipse/handlers/HandleSuperBuilder.java | 7 +- .../singulars/EclipseGuavaSingularizer.java | 5 +- .../EclipseJavaUtilListSetSingularizer.java | 4 +- .../singulars/EclipseJavaUtilMapSingularizer.java | 5 +- src/core/lombok/javac/handlers/HandleBuilder.java | 19 ++- .../lombok/javac/handlers/HandleSuperBuilder.java | 7 +- .../javac/handlers/JavacSingularsRecipes.java | 68 ++++++--- test/core/src/lombok/DirectoryRunner.java | 2 +- .../after-delombok/BuilderDefaultsWarnings.java | 1 + .../BuilderSingularAnnotatedTypes.java | 2 + ...lderSingularAnnotatedTypesWithSetterPrefix.java | 2 + .../BuilderSingularGuavaListsSets.java | 5 + .../after-delombok/BuilderSingularGuavaMaps.java | 3 + .../after-delombok/BuilderSingularLists.java | 3 + .../after-delombok/BuilderSingularMaps.java | 4 + .../BuilderSingularMapsWithSetterPrefix.java | 64 +++++---- .../after-delombok/BuilderSingularNoAuto.java | 3 + .../BuilderSingularNoAutoWithSetterPrefix.java | 3 + .../BuilderSingularNullBehavior1.java | 160 +++++++++++++++++++++ .../BuilderSingularRedirectToGuava.java | 3 + .../after-delombok/BuilderSingularSets.java | 4 + .../BuilderSingularSetsWithSetterPrefix.java | 4 + .../BuilderSingularToBuilderWithNull.java | 1 + ...rSingularToBuilderWithNullWithSetterPrefix.java | 1 + .../BuilderSingularWildcardListsWithToBuilder.java | 2 + .../BuilderSingularWithPrefixes.java | 1 + ...uilderSingularWithPrefixesWithSetterPrefix.java | 1 + .../after-delombok/BuilderWithDeprecated.java | 2 + .../after-delombok/BuilderWithToBuilder.java | 1 + .../after-delombok/CheckerFrameworkBuilder.java | 1 + .../CheckerFrameworkSuperBuilder.java | 1 + .../resource/after-delombok/SuperBuilderBasic.java | 1 + .../after-delombok/SuperBuilderBasicToBuilder.java | 1 + .../SuperBuilderSingularAnnotatedTypes.java | 2 + .../SuperBuilderWithCustomBuilderMethod.java | 1 + .../after-delombok/SuperBuilderWithGenerics.java | 1 + .../after-delombok/SuperBuilderWithGenerics2.java | 1 + .../SuperBuilderWithGenericsAndToBuilder.java | 1 + .../after-delombok/SuperBuilderWithPrefixes.java | 1 + .../after-ecj/BuilderDefaultsWarnings.java | 2 + .../after-ecj/BuilderSingularAnnotatedTypes.java | 4 + ...lderSingularAnnotatedTypesWithSetterPrefix.java | 4 + .../after-ecj/BuilderSingularGuavaListsSets.java | 10 ++ .../after-ecj/BuilderSingularGuavaMaps.java | 6 + .../resource/after-ecj/BuilderSingularLists.java | 6 + .../resource/after-ecj/BuilderSingularMaps.java | 8 ++ .../BuilderSingularMapsWithSetterPrefix.java | 78 +++++----- .../resource/after-ecj/BuilderSingularNoAuto.java | 6 + .../BuilderSingularNoAutoWithSetterPrefix.java | 6 + .../after-ecj/BuilderSingularNullBehavior1.java | 154 ++++++++++++++++++++ .../after-ecj/BuilderSingularRedirectToGuava.java | 6 + .../resource/after-ecj/BuilderSingularSets.java | 8 ++ .../BuilderSingularSetsWithSetterPrefix.java | 8 ++ .../BuilderSingularToBuilderWithNull.java | 2 + ...rSingularToBuilderWithNullWithSetterPrefix.java | 2 + .../BuilderSingularWildcardListsWithToBuilder.java | 4 + .../after-ecj/BuilderSingularWithPrefixes.java | 2 + ...uilderSingularWithPrefixesWithSetterPrefix.java | 2 + .../resource/after-ecj/BuilderWithDeprecated.java | 4 + .../resource/after-ecj/BuilderWithToBuilder.java | 2 + .../after-ecj/CheckerFrameworkBuilder.java | 2 + .../after-ecj/CheckerFrameworkSuperBuilder.java | 2 + .../after-ecj/I2335_BuilderMultipleObtainVia.java | 4 +- .../resource/after-ecj/SuperBuilderBasic.java | 2 + .../after-ecj/SuperBuilderBasicToBuilder.java | 2 + .../SuperBuilderSingularAnnotatedTypes.java | 4 + .../SuperBuilderWithCustomBuilderMethod.java | 2 + .../after-ecj/SuperBuilderWithGenerics.java | 2 + .../after-ecj/SuperBuilderWithGenerics2.java | 2 + .../SuperBuilderWithGenericsAndToBuilder.java | 2 + .../after-ecj/SuperBuilderWithPrefixes.java | 2 + .../BuilderSingularMapsWithSetterPrefix.java | 1 + .../before/BuilderSingularNullBehavior1.java | 13 ++ 79 files changed, 835 insertions(+), 108 deletions(-) create mode 100644 test/transform/resource/after-delombok/BuilderSingularNullBehavior1.java create mode 100644 test/transform/resource/after-ecj/BuilderSingularNullBehavior1.java create mode 100644 test/transform/resource/before/BuilderSingularNullBehavior1.java diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index ef433d8d..691346cd 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 The Project Lombok Authors. + * Copyright (C) 2013-2020 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 @@ -23,12 +23,13 @@ package lombok; import java.util.List; +import lombok.Singular.NullCollectionBehavior; import lombok.core.configuration.CallSuperType; import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.configuration.ConfigurationKey; -import lombok.core.configuration.LogDeclaration; import lombok.core.configuration.FlagUsageType; import lombok.core.configuration.IdentifierName; +import lombok.core.configuration.LogDeclaration; import lombok.core.configuration.NullCheckExceptionType; import lombok.core.configuration.TypeName; @@ -287,7 +288,19 @@ public class ConfigurationKeys { * * By default or if explicitly set to {@code true}, lombok will attempt to automatically singularize the name of your variable/parameter when using {@code @Singular}; the name is assumed to be written in english, and a plural. If explicitly to {@code false}, you must always specify the singular form; this is especially useful if your identifiers are in a foreign language. */ - public static final ConfigurationKey SINGULAR_AUTO = new ConfigurationKey("lombok.singular.auto", "If true (default): Automatically singularize the assumed-to-be-plural name of your variable/parameter when using {@code @Singular}.") {}; + public static final ConfigurationKey SINGULAR_AUTO = new ConfigurationKey("lombok.singular.auto", "If true (default): Automatically singularize the assumed-to-be-plural name of your variable/parameter when using @Singular.") {}; + + /** + * lombok configuration: {@code lombok.singular.nullCollections} = one of: [{@code NullPointerException}, {@code IllegalArgumentException}, {@code JDK}, {@code Guava}, or {@code ignore}]. + * + * Lombok generates a 'plural form' method, which takes in a collection and will add each element in it to the {@code @Singular} marked target. What should happen if {@code null} is passed instead of a collection instance?
    + *
  • If the chosen configuration is {@code NullPointerException} (the default), or {@code IllegalArgumentException}, that exception type is a thrown, with as message field-name must not be null.
  • + *
  • If the chosen configuration is {@code JDK}, a call to {@code java.util.Objects.requireNonNull} is generated with the fieldname passed along (which throws {@code NullPointerException}).
  • + *
  • If the chosen configuration is {@code Guava}, a call to {@code com.google.common.base.Preconditions.checkNotNull} is generated with the fieldname passed along (which throws {@code NullPointerException}).
  • + *
  • If the chosen configuration is {@code Ignore}, then no exception occurs, and the call does nothing; it acts as if an empty collection was passed.
  • + *
+ */ + public static final ConfigurationKey SINGULAR_NULL_COLLECTIONS = new ConfigurationKey("lombok.singular.nullCollections", "Lombok generates a method to add all elements in a collection when using @Singular. What should happen if a null ref is passed? default: throw NullPointerException.") {}; // ##### Standalones ##### @@ -312,10 +325,14 @@ public class ConfigurationKeys { // ----- NonNull ----- /** - * lombok configuration: {@code lombok.nonNull.exceptionType} = one of: [{@code IllegalArgumentException}, {@code NullPointerException}, or {@code Assertion}]. + * lombok configuration: {@code lombok.nonNull.exceptionType} = one of: [{@code IllegalArgumentException}, {@code NullPointerException}, {@code JDK}, {@code Guava}, or {@code Assertion}]. * - * Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}. If the chosen configuration is {@code Assertion}, an assertion is generated instead, - * which would mean your code throws an {@code AssertionError} if assertions are enabled, and does nothing if assertions are not enabled. + * Sets the behavior of the generated nullcheck if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}.
    + *
  • If the chosen configuration is {@code NullPointerException} (the default), or {@code IllegalArgumentException}, that exception type is a thrown, with as message field-name is marked non-null but is null.
  • + *
  • If the chosen configuration is {@code Assert}, then an {@code assert} statement is generated. This means an {@code AssertionError} will be thrown if assertions are on (VM started with {@code -ea} parameter), and nothing happens if not.
  • + *
  • If the chosen configuration is {@code JDK}, a call to {@code java.util.Objects.requireNonNull} is generated with the fieldname passed along (which throws {@code NullPointerException}).
  • + *
  • If the chosen configuration is {@code Guava}, a call to {@code com.google.common.base.Preconditions.checkNotNull} is generated with the fieldname passed along (which throws {@code NullPointerException}).
  • + *
*/ public static final ConfigurationKey NON_NULL_EXCEPTION_TYPE = new ConfigurationKey("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null (Default: NullPointerException).") {}; diff --git a/src/core/lombok/Singular.java b/src/core/lombok/Singular.java index 67edab96..f8cf6853 100644 --- a/src/core/lombok/Singular.java +++ b/src/core/lombok/Singular.java @@ -27,6 +27,10 @@ import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import lombok.core.LombokImmutableList; +import lombok.core.configuration.ExampleValueString; +import lombok.core.configuration.NullCheckExceptionType; + /** * The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections. */ @@ -35,4 +39,64 @@ import java.lang.annotation.Target; public @interface Singular { /** @return The singular name of this field. If it's a normal english plural, lombok will figure it out automatically. Otherwise, this parameter is mandatory. */ String value() default ""; + + NullCollectionBehavior nullBehavior() default NullCollectionBehavior.NULL_POINTER_EXCEPTION; + + @ExampleValueString("[NullPointerException | IllegalArgumentException | JDK | Guava | Ignore]") + public enum NullCollectionBehavior { + ILLEGAL_ARGUMENT_EXCEPTION { + @Override public String getExceptionType() { + return NullCheckExceptionType.ILLEGAL_ARGUMENT_EXCEPTION.getExceptionType(); + } + + @Override public LombokImmutableList getMethod() { + return NullCheckExceptionType.ILLEGAL_ARGUMENT_EXCEPTION.getMethod(); + } + }, + NULL_POINTER_EXCEPTION { + @Override public String getExceptionType() { + return NullCheckExceptionType.NULL_POINTER_EXCEPTION.getExceptionType(); + } + + @Override public LombokImmutableList getMethod() { + return NullCheckExceptionType.NULL_POINTER_EXCEPTION.getMethod(); + } + }, + JDK { + @Override public String getExceptionType() { + return NullCheckExceptionType.JDK.getExceptionType(); + } + + @Override public LombokImmutableList getMethod() { + return NullCheckExceptionType.JDK.getMethod(); + } + }, + GUAVA { + @Override public String getExceptionType() { + return NullCheckExceptionType.GUAVA.getExceptionType(); + } + + @Override public LombokImmutableList getMethod() { + return NullCheckExceptionType.GUAVA.getMethod(); + } + }, + IGNORE { + @Override public String getExceptionType() { + return null; + } + + @Override public LombokImmutableList getMethod() { + return null; + } + }; + + + public String toExceptionMessage(String fieldName) { + return fieldName + " cannot be null"; + } + + public abstract String getExceptionType(); + + public abstract LombokImmutableList getMethod(); + } } diff --git a/src/core/lombok/core/configuration/NullCheckExceptionType.java b/src/core/lombok/core/configuration/NullCheckExceptionType.java index 3c9e325d..4632916c 100644 --- a/src/core/lombok/core/configuration/NullCheckExceptionType.java +++ b/src/core/lombok/core/configuration/NullCheckExceptionType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2019 The Project Lombok Authors. + * Copyright (C) 2014-2020 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 @@ -23,7 +23,7 @@ package lombok.core.configuration; import lombok.core.LombokImmutableList; -@ExampleValueString("[NullPointerException | IllegalArgumentException | Assertion | JDK | GUAVA]") +@ExampleValueString("[NullPointerException | IllegalArgumentException | Assertion | JDK | Guava]") public enum NullCheckExceptionType { ILLEGAL_ARGUMENT_EXCEPTION { @Override public String getExceptionType() { diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 38aada23..883b1a29 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2018 The Project Lombok Authors. + * Copyright (C) 2013-2020 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 @@ -451,11 +451,6 @@ public class HandlerUtil { return null; } - /* NB: 'notnull' is not part of the pattern because there are lots of @NotNull annotations out there that are crappily named and actually mean - something else, such as 'this field must not be null _when saved to the db_ but its perfectly okay to start out as such, and a no-args - constructor and the implied starts-out-as-null state that goes with it is in fact mandatory' which happens with javax.validation.constraints.NotNull. - Various problems with spring have also been reported. See issue #287, issue #271, and issue #43. */ - public static final String DEFAULT_EXCEPTION_FOR_NON_NULL = "java.lang.NullPointerException"; /** diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 9d79d75c..5fe4b958 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -33,25 +33,31 @@ import java.util.Map; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; 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.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; 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.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.Reference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; @@ -60,10 +66,12 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import lombok.AccessLevel; +import lombok.Singular.NullCollectionBehavior; import lombok.core.LombokImmutableList; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseNode; public class EclipseSingularsRecipes { @@ -126,13 +134,14 @@ public class EclipseSingularsRecipes { private final List typeArgs; private final String targetFqn; private final EclipseSingularizer singularizer; + private final NullCollectionBehavior nullCollectionBehavior; private final ASTNode source; - public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source) { - this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, source, new char[0]); + public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, NullCollectionBehavior nullCollectionBehavior) { + this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, source, nullCollectionBehavior, new char[0]); } - public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, char[] setterPrefix) { + public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, NullCollectionBehavior nullCollectionBehavior, char[] setterPrefix) { this.annotation = annotation; this.singularName = singularName; this.pluralName = pluralName; @@ -140,6 +149,7 @@ public class EclipseSingularsRecipes { this.targetFqn = targetFqn; this.singularizer = singularizer; this.source = source; + this.nullCollectionBehavior = nullCollectionBehavior; this.setterPrefix = setterPrefix; } @@ -187,6 +197,10 @@ public class EclipseSingularsRecipes { return singularizer; } + public NullCollectionBehavior getNullCollectionBehavior() { + return nullCollectionBehavior; + } + public String getTargetSimpleType() { int idx = targetFqn.lastIndexOf("."); return idx == -1 ? targetFqn : targetFqn.substring(idx + 1); @@ -423,6 +437,45 @@ public class EclipseSingularsRecipes { } } + protected void nullBehaviorize(SingularData data, List statements) { + NullCollectionBehavior behavior = data.getNullCollectionBehavior(); + + if (behavior == NullCollectionBehavior.IGNORE) { + Expression isNotNull = new EqualExpression(new SingleNameReference(data.getPluralName(), 0L), new NullLiteral(0, 0), OperatorIds.NOT_EQUAL); + Block b = new Block(0); + b.statements = statements.toArray(new Statement[statements.size()]); + statements.clear(); + statements.add(new IfStatement(isNotNull, b, 0, 0)); + return; + } + + String exceptionTypeStr = behavior.getExceptionType(); + StringLiteral message = new StringLiteral(behavior.toExceptionMessage(new String(data.getPluralName())).toCharArray(), 0, 0, 0); + if (exceptionTypeStr != null) { + Expression isNull = new EqualExpression(new SingleNameReference(data.getPluralName(), 0L), new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + int partCount = 1; + for (int i = 0; i < exceptionTypeStr.length(); i++) if (exceptionTypeStr.charAt(i) == '.') partCount++; + long[] ps = new long[partCount]; + Arrays.fill(ps, 0L); + AllocationExpression alloc = new AllocationExpression(); + alloc.type = new QualifiedTypeReference(Eclipse.fromQualifiedName(exceptionTypeStr), ps); + alloc.arguments = new Expression[] {message}; + Statement t = new ThrowStatement(alloc, 0, 0); + statements.add(0, new IfStatement(isNull, t, 0, 0)); + return; + } + + MessageSend invoke = new MessageSend(); + LombokImmutableList method = behavior.getMethod(); + char[][] utilityTypeName = new char[method.size() - 1][]; + for (int i = 0; i < method.size() - 1; i++) utilityTypeName[i] = method.get(i).toCharArray(); + + invoke.receiver = new QualifiedNameReference(utilityTypeName, new long[method.size() - 1], 0, 0); + invoke.selector = method.get(method.size() - 1).toCharArray(); + invoke.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L), message}; + statements.add(0, invoke); + } + protected abstract char[][] getEmptyMakerReceiver(String targetFqn); protected abstract char[] getEmptyMakerSelector(String targetFqn); } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index bb031ebc..578fa2a3 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -79,6 +79,7 @@ import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.Builder; import lombok.Builder.ObtainVia; +import lombok.Singular.NullCollectionBehavior; import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; @@ -992,7 +993,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (!annotationTypeMatches(Singular.class, child)) continue; char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); - String explicitSingular = ann.getInstance().value(); + Singular singularInstance = ann.getInstance(); + String explicitSingular = singularInstance.value(); if (explicitSingular.isEmpty()) { if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); @@ -1034,9 +1036,21 @@ public class HandleBuilder extends EclipseAnnotationHandler { return null; } - return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, setterPrefix.toCharArray()); + NullCollectionBehavior behavior = getNullBehaviorFor(ann, singularInstance, node); + return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, behavior, setterPrefix.toCharArray()); } return null; } + + static NullCollectionBehavior getNullBehaviorFor(AnnotationValues ann, Singular singularInstance, EclipseNode node) { + NullCollectionBehavior behavior; + if (ann.isExplicit("nullBehavior")) { + behavior = singularInstance.nullBehavior(); + } else { + behavior = node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_NULL_COLLECTIONS); + } + if (behavior == null) return NullCollectionBehavior.NULL_POINTER_EXCEPTION; + return behavior; + } } diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3a2e59f7..4b450a07 100755 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -79,6 +79,7 @@ import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.Builder; import lombok.Builder.ObtainVia; +import lombok.Singular.NullCollectionBehavior; import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; @@ -1000,7 +1001,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); - String explicitSingular = ann.getInstance().value(); + Singular singularInstance = ann.getInstance(); + String explicitSingular = singularInstance.value(); if (explicitSingular.isEmpty()) { if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); @@ -1042,7 +1044,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler { return null; } - return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source); + NullCollectionBehavior behavior = HandleBuilder.getNullBehaviorFor(ann, singularInstance, node); + return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, behavior); } return null; diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 8f80d228..b067ad80 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2020 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 @@ -195,6 +195,9 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { thisDotFieldDotAddAll.receiver = thisDotField; thisDotFieldDotAddAll.selector = (getAddMethodName() + "All").toCharArray(); statements.add(thisDotFieldDotAddAll); + + nullBehaviorize(data, statements); + if (returnStatement != null) statements.add(returnStatement); md.statements = statements.toArray(new Statement[0]); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index e3a99008..ba447397 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2020 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 @@ -173,6 +173,8 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula thisDotFieldDotAddAll.receiver = thisDotField; thisDotFieldDotAddAll.selector = "addAll".toCharArray(); statements.add(thisDotFieldDotAddAll); + + nullBehaviorize(data, statements); if (returnStatement != null) statements.add(returnStatement); md.statements = statements.toArray(new Statement[0]); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index b0223c50..e91c6616 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2020 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 @@ -305,6 +305,9 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer forEachContent.statements = new Statement[] {addKey, addValue}; forEach.action = forEachContent; statements.add(forEach); + + nullBehaviorize(data, statements); + if (returnStatement != null) statements.add(returnStatement); md.statements = statements.toArray(new Statement[0]); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 5fc3957b..75f3de2c 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -61,6 +61,7 @@ import lombok.Builder; import lombok.Builder.ObtainVia; import lombok.ConfigurationKeys; import lombok.Singular; +import lombok.Singular.NullCollectionBehavior; import lombok.ToString; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -869,8 +870,9 @@ public class HandleBuilder extends JavacAnnotationHandler { if (!annotationTypeMatches(Singular.class, child)) continue; Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); + Singular singularInstance = ann.getInstance(); deleteAnnotationIfNeccessary(child, Singular.class); - String explicitSingular = ann.getInstance().value(); + String explicitSingular = singularInstance.value(); if (explicitSingular.isEmpty()) { if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); @@ -906,9 +908,22 @@ public class HandleBuilder extends JavacAnnotationHandler { return null; } - return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, setterPrefix); + NullCollectionBehavior behavior = getNullBehaviorFor(ann, singularInstance, node); + + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, behavior, setterPrefix); } return null; } + + static NullCollectionBehavior getNullBehaviorFor(AnnotationValues ann, Singular singularInstance, JavacNode node) { + NullCollectionBehavior behavior; + if (ann.isExplicit("nullBehavior")) { + behavior = singularInstance.nullBehavior(); + } else { + behavior = node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_NULL_COLLECTIONS); + } + if (behavior == null) return NullCollectionBehavior.NULL_POINTER_EXCEPTION; + return behavior; + } } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index b3002946..692ee60b 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -59,6 +59,7 @@ import com.sun.tools.javac.util.Name; import lombok.AccessLevel; import lombok.Builder; import lombok.Builder.ObtainVia; +import lombok.Singular.NullCollectionBehavior; import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; @@ -939,8 +940,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (!annotationTypeMatches(Singular.class, child)) continue; Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; AnnotationValues ann = createAnnotation(Singular.class, child); + Singular singularInstance = ann.getInstance(); deleteAnnotationIfNeccessary(child, Singular.class); - String explicitSingular = ann.getInstance().value(); + String explicitSingular = singularInstance.value(); if (explicitSingular.isEmpty()) { if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) { node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled."); @@ -974,7 +976,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer); + NullCollectionBehavior behavior = HandleBuilder.getNullBehaviorFor(ann, singularInstance, node); + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, behavior); } return null; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index c1e6edd8..a5d4a295 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -24,33 +24,24 @@ package lombok.javac.handlers; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import com.sun.tools.javac.tree.JCTree.JCBlock; -import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import lombok.AccessLevel; -import lombok.ConfigurationKeys; -import lombok.core.LombokImmutableList; -import lombok.core.SpiLoadUtil; -import lombok.core.TypeLibrary; -import lombok.core.configuration.CheckerFrameworkVersion; -import lombok.core.handlers.HandlerUtil; -import lombok.javac.JavacNode; -import lombok.javac.JavacTreeMaker; - import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.util.Context; @@ -58,6 +49,17 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.Singular.NullCollectionBehavior; +import lombok.core.LombokImmutableList; +import lombok.core.SpiLoadUtil; +import lombok.core.TypeLibrary; +import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.core.handlers.HandlerUtil; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; + public class JavacSingularsRecipes { public interface ExpressionMaker { JCExpression make(); @@ -120,12 +122,13 @@ public class JavacSingularsRecipes { private final String targetFqn; private final JavacSingularizer singularizer; private final String setterPrefix; + private final NullCollectionBehavior nullCollectionBehavior; - public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer) { - this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, ""); + public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer, NullCollectionBehavior nullCollectionBehavior) { + this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, nullCollectionBehavior, ""); } - public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer, String setterPrefix) { + public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer, NullCollectionBehavior nullCollectionBehavior, String setterPrefix) { this.annotation = annotation; this.singularName = singularName; this.pluralName = pluralName; @@ -133,6 +136,7 @@ public class JavacSingularsRecipes { this.targetFqn = targetFqn; this.singularizer = singularizer; this.setterPrefix = setterPrefix; + this.nullCollectionBehavior = nullCollectionBehavior; } public JavacNode getAnnotation() { @@ -163,6 +167,10 @@ public class JavacSingularsRecipes { return singularizer; } + public NullCollectionBehavior getNullCollectionBehavior() { + return nullCollectionBehavior; + } + public String getTargetSimpleType() { int idx = targetFqn.lastIndexOf("."); return idx == -1 ? targetFqn : targetFqn.substring(idx + 1); @@ -320,6 +328,7 @@ public class JavacSingularsRecipes { private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { ListBuffer statements = generatePluralMethodStatements(maker, data, builderType, source); + Name name = data.getPluralName(); String setterPrefix = data.getSetterPrefix(); if (setterPrefix.isEmpty() && !fluent) setterPrefix = getAddMethodName() + "All"; @@ -329,13 +338,40 @@ public class JavacSingularsRecipes { long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null); statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); + + NullCollectionBehavior behavior = data.getNullCollectionBehavior(); + + if (behavior == NullCollectionBehavior.IGNORE) { + JCExpression incomingIsNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(data.getPluralName()), maker.Literal(CTC_BOT, null)); + JCStatement onNotNull = maker.Block(0, statements.toList()); + statements = new ListBuffer(); + statements.add(maker.If(incomingIsNotNull, onNotNull, null)); + } else { + JCLiteral message = maker.Literal(behavior.toExceptionMessage(data.getPluralName().toString())); + if (behavior.getExceptionType() != null) { + JCExpression incomingIsNull = maker.Binary(CTC_EQUAL, maker.Ident(data.getPluralName()), maker.Literal(CTC_BOT, null)); + JCExpression exType = genTypeRef(builderType, behavior.getExceptionType()); + JCExpression exception = maker.NewClass(null, List.nil(), exType, List.of(message), null); + JCStatement onNull = maker.Throw(exception); + statements.prepend(maker.If(incomingIsNull, onNull, null)); + } else { + LombokImmutableList method = behavior.getMethod(); + JCExpression invoke = maker.Apply(List.nil(), chainDots(builderType, method), List.of(maker.Ident(data.getPluralName()), message)); + statements.prepend(maker.Exec(invoke)); + } + } + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), access); } protected ListBuffer generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + ListBuffer statements = new ListBuffer(); + JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All"); JCExpression invokeAdd = maker.Apply(List.nil(), thisDotFieldDotAdd, List.of(maker.Ident(data.getPluralName()))); - return new ListBuffer().append(maker.Exec(invokeAdd)); + statements.append(maker.Exec(invokeAdd)); + + return statements; } protected abstract JCExpression getPluralMethodParamType(JavacNode builderType); diff --git a/test/core/src/lombok/DirectoryRunner.java b/test/core/src/lombok/DirectoryRunner.java index 116b9d94..b8b1da43 100644 --- a/test/core/src/lombok/DirectoryRunner.java +++ b/test/core/src/lombok/DirectoryRunner.java @@ -77,7 +77,7 @@ public class DirectoryRunner extends Runner { return true; } - public abstract boolean expectChanges(); + public abstract boolean expectChanges(); } private static final FileFilter JAVA_FILE_FILTER = new FileFilter() { diff --git a/test/transform/resource/after-delombok/BuilderDefaultsWarnings.java b/test/transform/resource/after-delombok/BuilderDefaultsWarnings.java index 4df98158..04cd3bbb 100644 --- a/test/transform/resource/after-delombok/BuilderDefaultsWarnings.java +++ b/test/transform/resource/after-delombok/BuilderDefaultsWarnings.java @@ -38,6 +38,7 @@ public class BuilderDefaultsWarnings { } @java.lang.SuppressWarnings("all") public BuilderDefaultsWarnings.BuilderDefaultsWarningsBuilder items(final java.util.Collection items) { + if (items == null) throw new java.lang.NullPointerException("items cannot be null"); if (this.items == null) this.items = new java.util.ArrayList(); this.items.addAll(items); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypes.java b/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypes.java index 6bd3390c..4cc247ef 100644 --- a/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypes.java +++ b/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypes.java @@ -36,6 +36,7 @@ class BuilderSingularAnnotatedTypes { } @java.lang.SuppressWarnings("all") public BuilderSingularAnnotatedTypes.BuilderSingularAnnotatedTypesBuilder foos(final java.util.Collection foos) { + if (foos == null) throw new java.lang.NullPointerException("foos cannot be null"); if (this.foos == null) this.foos = new java.util.ArrayList<@MyAnnotation @NonNull String>(); this.foos.addAll(foos); return this; @@ -63,6 +64,7 @@ class BuilderSingularAnnotatedTypes { } @java.lang.SuppressWarnings("all") public BuilderSingularAnnotatedTypes.BuilderSingularAnnotatedTypesBuilder bars(final java.util.Map bars) { + if (bars == null) throw new java.lang.NullPointerException("bars cannot be null"); if (this.bars$key == null) { this.bars$key = new java.util.ArrayList<@MyAnnotation @NonNull String>(); this.bars$value = new java.util.ArrayList<@MyAnnotation @NonNull Integer>(); diff --git a/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypesWithSetterPrefix.java b/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypesWithSetterPrefix.java index d1047567..90cc5570 100644 --- a/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypesWithSetterPrefix.java +++ b/test/transform/resource/after-delombok/BuilderSingularAnnotatedTypesWithSetterPrefix.java @@ -36,6 +36,7 @@ class BuilderSingularAnnotatedTypesWithSetterPrefix { } @java.lang.SuppressWarnings("all") public BuilderSingularAnnotatedTypesWithSetterPrefix.BuilderSingularAnnotatedTypesWithSetterPrefixBuilder withFoos(final java.util.Collection foos) { + if (foos == null) throw new java.lang.NullPointerException("foos cannot be null"); if (this.foos == null) this.foos = new java.util.ArrayList<@MyAnnotation @NonNull String>(); this.foos.addAll(foos); return this; @@ -63,6 +64,7 @@ class BuilderSingularAnnotatedTypesWithSetterPrefix { } @java.lang.SuppressWarnings("all") public BuilderSingularAnnotatedTypesWithSetterPrefix.BuilderSingularAnnotatedTypesWithSetterPrefixBuilder withBars(final java.util.Map bars) { + if (bars == null) throw new java.lang.NullPointerException("bars cannot be null"); if (this.bars$key == null) { this.bars$key = new java.util.ArrayList<@MyAnnotation @NonNull String>(); this.bars$value = new java.util.ArrayList<@MyAnnotation @NonNull Integer>(); diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java index b6fb5732..650ab2fd 100644 --- a/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java +++ b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java @@ -41,6 +41,7 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder cards(final java.lang.Iterable cards) { + if (cards == null) throw new java.lang.NullPointerException("cards cannot be null"); if (this.cards == null) this.cards = com.google.common.collect.ImmutableList.builder(); this.cards.addAll(cards); return this; @@ -58,6 +59,7 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder frogs(final java.lang.Iterable frogs) { + if (frogs == null) throw new java.lang.NullPointerException("frogs cannot be null"); if (this.frogs == null) this.frogs = com.google.common.collect.ImmutableList.builder(); this.frogs.addAll(frogs); return this; @@ -75,6 +77,7 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder rawSet(final java.lang.Iterable rawSet) { + if (rawSet == null) throw new java.lang.NullPointerException("rawSet cannot be null"); if (this.rawSet == null) this.rawSet = com.google.common.collect.ImmutableSet.builder(); this.rawSet.addAll(rawSet); return this; @@ -92,6 +95,7 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder passes(final java.lang.Iterable passes) { + if (passes == null) throw new java.lang.NullPointerException("passes cannot be null"); if (this.passes == null) this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder(); this.passes.addAll(passes); return this; @@ -109,6 +113,7 @@ class BuilderSingularGuavaListsSets { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder users(final com.google.common.collect.Table users) { + if (users == null) throw new java.lang.NullPointerException("users cannot be null"); if (this.users == null) this.users = com.google.common.collect.ImmutableTable.builder(); this.users.putAll(users); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java index a6043475..6af1bf8d 100644 --- a/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java +++ b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java @@ -31,6 +31,7 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaMaps.BuilderSingularGuavaMapsBuilder battleaxes(final java.util.Map battleaxes) { + if (battleaxes == null) throw new java.lang.NullPointerException("battleaxes cannot be null"); if (this.battleaxes == null) this.battleaxes = com.google.common.collect.ImmutableMap.builder(); this.battleaxes.putAll(battleaxes); return this; @@ -48,6 +49,7 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaMaps.BuilderSingularGuavaMapsBuilder vertices(final java.util.Map vertices) { + if (vertices == null) throw new java.lang.NullPointerException("vertices cannot be null"); if (this.vertices == null) this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder(); this.vertices.putAll(vertices); return this; @@ -65,6 +67,7 @@ class BuilderSingularGuavaMaps { } @java.lang.SuppressWarnings("all") public BuilderSingularGuavaMaps.BuilderSingularGuavaMapsBuilder rawMap(final java.util.Map rawMap) { + if (rawMap == null) throw new java.lang.NullPointerException("rawMap cannot be null"); if (this.rawMap == null) this.rawMap = com.google.common.collect.ImmutableBiMap.builder(); this.rawMap.putAll(rawMap); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularLists.java b/test/transform/resource/after-delombok/BuilderSingularLists.java index 971a2851..43eb7702 100644 --- a/test/transform/resource/after-delombok/BuilderSingularLists.java +++ b/test/transform/resource/after-delombok/BuilderSingularLists.java @@ -30,6 +30,7 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") public BuilderSingularLists.BuilderSingularListsBuilder children(final java.util.Collection children) { + if (children == null) throw new java.lang.NullPointerException("children cannot be null"); if (this.children == null) this.children = new java.util.ArrayList(); this.children.addAll(children); return this; @@ -47,6 +48,7 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") public BuilderSingularLists.BuilderSingularListsBuilder scarves(final java.util.Collection scarves) { + if (scarves == null) throw new java.lang.NullPointerException("scarves cannot be null"); if (this.scarves == null) this.scarves = new java.util.ArrayList(); this.scarves.addAll(scarves); return this; @@ -64,6 +66,7 @@ class BuilderSingularLists { } @java.lang.SuppressWarnings("all") public BuilderSingularLists.BuilderSingularListsBuilder rawList(final java.util.Collection rawList) { + if (rawList == null) throw new java.lang.NullPointerException("rawList cannot be null"); if (this.rawList == null) this.rawList = new java.util.ArrayList(); this.rawList.addAll(rawList); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularMaps.java b/test/transform/resource/after-delombok/BuilderSingularMaps.java index 240a6e3b..d10dd090 100644 --- a/test/transform/resource/after-delombok/BuilderSingularMaps.java +++ b/test/transform/resource/after-delombok/BuilderSingularMaps.java @@ -46,6 +46,7 @@ class BuilderSingularMaps { } @SuppressWarnings("all") public BuilderSingularMaps.BuilderSingularMapsBuilder women(java.util.Map women) { + if (women == null) throw new NullPointerException("women cannot be null"); if (this.women$key == null) { this.women$key = new java.util.ArrayList(); this.women$value = new java.util.ArrayList(); @@ -76,6 +77,7 @@ class BuilderSingularMaps { } @SuppressWarnings("all") public BuilderSingularMaps.BuilderSingularMapsBuilder men(java.util.Map men) { + if (men == null) throw new NullPointerException("men cannot be null"); if (this.men$key == null) { this.men$key = new java.util.ArrayList(); this.men$value = new java.util.ArrayList(); @@ -106,6 +108,7 @@ class BuilderSingularMaps { } @SuppressWarnings("all") public BuilderSingularMaps.BuilderSingularMapsBuilder rawMap(java.util.Map rawMap) { + if (rawMap == null) throw new NullPointerException("rawMap cannot be null"); if (this.rawMap$key == null) { this.rawMap$key = new java.util.ArrayList(); this.rawMap$value = new java.util.ArrayList(); @@ -136,6 +139,7 @@ class BuilderSingularMaps { } @SuppressWarnings("all") public BuilderSingularMaps.BuilderSingularMapsBuilder stringMap(java.util.Map stringMap) { + if (stringMap == null) throw new NullPointerException("stringMap cannot be null"); if (this.stringMap$key == null) { this.stringMap$key = new java.util.ArrayList(); this.stringMap$value = new java.util.ArrayList(); diff --git a/test/transform/resource/after-delombok/BuilderSingularMapsWithSetterPrefix.java b/test/transform/resource/after-delombok/BuilderSingularMapsWithSetterPrefix.java index bfc2e750..baa8f311 100644 --- a/test/transform/resource/after-delombok/BuilderSingularMapsWithSetterPrefix.java +++ b/test/transform/resource/after-delombok/BuilderSingularMapsWithSetterPrefix.java @@ -46,13 +46,15 @@ class BuilderSingularMapsWithSetterPrefix { } @SuppressWarnings("all") public BuilderSingularMapsWithSetterPrefix.BuilderSingularMapsWithSetterPrefixBuilder withWomen(java.util.Map women) { - if (this.women$key == null) { - this.women$key = new java.util.ArrayList(); - this.women$value = new java.util.ArrayList(); - } - for (java.util.Map.Entry $lombokEntry : women.entrySet()) { - this.women$key.add($lombokEntry.getKey()); - this.women$value.add($lombokEntry.getValue()); + if (women != null) { + if (this.women$key == null) { + this.women$key = new java.util.ArrayList(); + this.women$value = new java.util.ArrayList(); + } + for (java.util.Map.Entry $lombokEntry : women.entrySet()) { + this.women$key.add($lombokEntry.getKey()); + this.women$value.add($lombokEntry.getValue()); + } } return this; } @@ -76,13 +78,15 @@ class BuilderSingularMapsWithSetterPrefix { } @SuppressWarnings("all") public BuilderSingularMapsWithSetterPrefix.BuilderSingularMapsWithSetterPrefixBuilder withMen(java.util.Map men) { - if (this.men$key == null) { - this.men$key = new java.util.ArrayList(); - this.men$value = new java.util.ArrayList(); - } - for (java.util.Map.Entry $lombokEntry : men.entrySet()) { - this.men$key.add($lombokEntry.getKey()); - this.men$value.add($lombokEntry.getValue()); + if (men != null) { + if (this.men$key == null) { + this.men$key = new java.util.ArrayList(); + this.men$value = new java.util.ArrayList(); + } + for (java.util.Map.Entry $lombokEntry : men.entrySet()) { + this.men$key.add($lombokEntry.getKey()); + this.men$value.add($lombokEntry.getValue()); + } } return this; } @@ -106,13 +110,15 @@ class BuilderSingularMapsWithSetterPrefix { } @SuppressWarnings("all") public BuilderSingularMapsWithSetterPrefix.BuilderSingularMapsWithSetterPrefixBuilder withRawMap(java.util.Map rawMap) { - if (this.rawMap$key == null) { - this.rawMap$key = new java.util.ArrayList(); - this.rawMap$value = new java.util.ArrayList(); - } - for (java.util.Map.Entry $lombokEntry : rawMap.entrySet()) { - this.rawMap$key.add($lombokEntry.getKey()); - this.rawMap$value.add($lombokEntry.getValue()); + if (rawMap != null) { + if (this.rawMap$key == null) { + this.rawMap$key = new java.util.ArrayList(); + this.rawMap$value = new java.util.ArrayList(); + } + for (java.util.Map.Entry $lombokEntry : rawMap.entrySet()) { + this.rawMap$key.add($lombokEntry.getKey()); + this.rawMap$value.add($lombokEntry.getValue()); + } } return this; } @@ -136,13 +142,15 @@ class BuilderSingularMapsWithSetterPrefix { } @SuppressWarnings("all") public BuilderSingularMapsWithSetterPrefix.BuilderSingularMapsWithSetterPrefixBuilder withStringMap(java.util.Map stringMap) { - if (this.stringMap$key == null) { - this.stringMap$key = new java.util.ArrayList(); - this.stringMap$value = new java.util.ArrayList(); - } - for (java.util.Map.Entry $lombokEntry : stringMap.entrySet()) { - this.stringMap$key.add($lombokEntry.getKey()); - this.stringMap$value.add($lombokEntry.getValue()); + if (stringMap != null) { + if (this.stringMap$key == null) { + this.stringMap$key = new java.util.ArrayList(); + this.stringMap$value = new java.util.ArrayList(); + } + for (java.util.Map.Entry $lombokEntry : stringMap.entrySet()) { + this.stringMap$key.add($lombokEntry.getKey()); + this.stringMap$value.add($lombokEntry.getValue()); + } } return this; } diff --git a/test/transform/resource/after-delombok/BuilderSingularNoAuto.java b/test/transform/resource/after-delombok/BuilderSingularNoAuto.java index a38effc0..6b2fd5b2 100644 --- a/test/transform/resource/after-delombok/BuilderSingularNoAuto.java +++ b/test/transform/resource/after-delombok/BuilderSingularNoAuto.java @@ -28,6 +28,7 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAuto.BuilderSingularNoAutoBuilder things(final java.util.Collection things) { + if (things == null) throw new java.lang.NullPointerException("things cannot be null"); if (this.things == null) this.things = new java.util.ArrayList(); this.things.addAll(things); return this; @@ -45,6 +46,7 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAuto.BuilderSingularNoAutoBuilder widgets(final java.util.Collection widgets) { + if (widgets == null) throw new java.lang.NullPointerException("widgets cannot be null"); if (this.widgets == null) this.widgets = new java.util.ArrayList(); this.widgets.addAll(widgets); return this; @@ -62,6 +64,7 @@ class BuilderSingularNoAuto { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAuto.BuilderSingularNoAutoBuilder items(final java.util.Collection items) { + if (items == null) throw new java.lang.NullPointerException("items cannot be null"); if (this.items == null) this.items = new java.util.ArrayList(); this.items.addAll(items); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularNoAutoWithSetterPrefix.java b/test/transform/resource/after-delombok/BuilderSingularNoAutoWithSetterPrefix.java index 57bef625..a023b82a 100644 --- a/test/transform/resource/after-delombok/BuilderSingularNoAutoWithSetterPrefix.java +++ b/test/transform/resource/after-delombok/BuilderSingularNoAutoWithSetterPrefix.java @@ -28,6 +28,7 @@ class BuilderSingularNoAutoWithSetterPrefix { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAutoWithSetterPrefix.BuilderSingularNoAutoWithSetterPrefixBuilder withThings(final java.util.Collection things) { + if (things == null) throw new java.lang.NullPointerException("things cannot be null"); if (this.things == null) this.things = new java.util.ArrayList(); this.things.addAll(things); return this; @@ -45,6 +46,7 @@ class BuilderSingularNoAutoWithSetterPrefix { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAutoWithSetterPrefix.BuilderSingularNoAutoWithSetterPrefixBuilder withWidgets(final java.util.Collection widgets) { + if (widgets == null) throw new java.lang.NullPointerException("widgets cannot be null"); if (this.widgets == null) this.widgets = new java.util.ArrayList(); this.widgets.addAll(widgets); return this; @@ -62,6 +64,7 @@ class BuilderSingularNoAutoWithSetterPrefix { } @java.lang.SuppressWarnings("all") public BuilderSingularNoAutoWithSetterPrefix.BuilderSingularNoAutoWithSetterPrefixBuilder withItems(final java.util.Collection items) { + if (items == null) throw new java.lang.NullPointerException("items cannot be null"); if (this.items == null) this.items = new java.util.ArrayList(); this.items.addAll(items); return this; diff --git a/test/transform/resource/after-delombok/BuilderSingularNullBehavior1.java b/test/transform/resource/after-delombok/BuilderSingularNullBehavior1.java new file mode 100644 index 00000000..7b4e0a29 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderSingularNullBehavior1.java @@ -0,0 +1,160 @@ +import java.util.List; +import java.util.Collection; +import lombok.Singular.NullCollectionBehavior; +class BuilderSingularNullBehavior1 { + private List names; + private List locations; + private List whatevers; + private List doohickeys; + @java.lang.SuppressWarnings("all") + BuilderSingularNullBehavior1(final List names, final List locations, final List whatevers, final List doohickeys) { + this.names = names; + this.locations = locations; + this.whatevers = whatevers; + this.doohickeys = doohickeys; + } + @java.lang.SuppressWarnings("all") + public static class BuilderSingularNullBehavior1Builder { + @java.lang.SuppressWarnings("all") + private java.util.ArrayList names; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList locations; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList whatevers; + @java.lang.SuppressWarnings("all") + private java.util.ArrayList doohickeys; + @java.lang.SuppressWarnings("all") + BuilderSingularNullBehavior1Builder() { + } + @java.lang.SuppressWarnings("all") + public BuilderSingularNullBehavior1.BuilderSingularNullBehavior1Builder name(final String name) { + if (this.names == null) this.names = new java.util.ArrayList(); + this.names.add(name); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularNullBehavior1.BuilderSingularNullBehavior1Builder names(final java.util.Collection names) { + if (names == null) throw new java.lang.NullPointerException("name