diff options
| author | Reinier Zwitserloot <r.zwitserloot@projectlombok.org> | 2020-01-18 03:10:07 +0100 |
|---|---|---|
| committer | Reinier Zwitserloot <r.zwitserloot@projectlombok.org> | 2020-01-28 16:21:39 +0100 |
| commit | 91a40b83125808d3684ce07c5cb4a2927d0b979c (patch) | |
| tree | ee1105aa16d2db522e26ea16cf11579fee81d893 | |
| parent | 6cc74e42295b6138629c6b32dd56a99ee8c2c646 (diff) | |
| download | lombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.tar.gz lombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.tar.bz2 lombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.zip | |
[singular][issue #2221] the plural builder method now nullchecks its argument with configurable results.
79 files changed, 835 insertions, 108 deletions
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<Boolean> SINGULAR_AUTO = new ConfigurationKey<Boolean>("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<Boolean> SINGULAR_AUTO = new ConfigurationKey<Boolean>("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 <em>add</em> each element in it to the {@code @Singular} marked target. What should happen if {@code null} is passed instead of a collection instance?<ul> + * <li>If the chosen configuration is {@code NullPointerException} (the default), or {@code IllegalArgumentException}, that exception type is a thrown, with as message <code><em>field-name</em> must not be null</code>.</li> + * <li>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}).</li> + * <li>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}).</li> + * <li>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.</li> + * </ul> + */ + public static final ConfigurationKey<NullCollectionBehavior> SINGULAR_NULL_COLLECTIONS = new ConfigurationKey<NullCollectionBehavior>("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}.<ul> + * <li>If the chosen configuration is {@code NullPointerException} (the default), or {@code IllegalArgumentException}, that exception type is a thrown, with as message <code><em>field-name</em> is marked non-null but is null</code>.</li> + * <li>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.</li> + * <li>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}).</li> + * <li>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}).</li> + * </ul> */ public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("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<String> getMethod() { + return NullCheckExceptionType.ILLEGAL_ARGUMENT_EXCEPTION.getMethod(); + } + }, + NULL_POINTER_EXCEPTION { + @Override public String getExceptionType() { + return NullCheckExceptionType.NULL_POINTER_EXCEPTION.getExceptionType(); + } + + @Override public LombokImmutableList<String> getMethod() { + return NullCheckExceptionType.NULL_POINTER_EXCEPTION.getMethod(); + } + }, + JDK { + @Override public String getExceptionType() { + return NullCheckExceptionType.JDK.getExceptionType(); + } + + @Override public LombokImmutableList<String> getMethod() { + return NullCheckExceptionType.JDK.getMethod(); + } + }, + GUAVA { + @Override public String getExceptionType() { + return NullCheckExceptionType.GUAVA.getExceptionType(); + } + + @Override public LombokImmutableList<String> getMethod() { + return NullCheckExceptionType.GUAVA.getMethod(); + } + }, + IGNORE { + @Override public String getExceptionType() { + return null; + } + + @Override public LombokImmutableList<String> getMethod() { + return null; + } + }; + + + public String toExceptionMessage(String fieldName) { + return fieldName + " cannot be null"; + } + + public abstract String getExceptionType(); + + public abstract LombokImmutableList<String> 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.NullCollection |
