aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinier Zwitserloot <r.zwitserloot@projectlombok.org>2020-01-18 03:10:07 +0100
committerReinier Zwitserloot <r.zwitserloot@projectlombok.org>2020-01-28 16:21:39 +0100
commit91a40b83125808d3684ce07c5cb4a2927d0b979c (patch)
treeee1105aa16d2db522e26ea16cf11579fee81d893
parent6cc74e42295b6138629c6b32dd56a99ee8c2c646 (diff)
downloadlombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.tar.gz
lombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.tar.bz2
lombok-91a40b83125808d3684ce07c5cb4a2927d0b979c.zip
[singular][issue #2221] the plural builder method now nullchecks its argument with configurable results.
-rw-r--r--src/core/lombok/ConfigurationKeys.java29
-rw-r--r--src/core/lombok/Singular.java64
-rw-r--r--src/core/lombok/core/configuration/NullCheckExceptionType.java4
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java7
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java59
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleBuilder.java18
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleSuperBuilder.java7
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java5
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java4
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java5
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java19
-rw-r--r--src/core/lombok/javac/handlers/HandleSuperBuilder.java7
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java68
-rw-r--r--test/core/src/lombok/DirectoryRunner.java2
-rw-r--r--test/transform/resource/after-delombok/BuilderDefaultsWarnings.java1
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularAnnotatedTypes.java2
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularAnnotatedTypesWithSetterPrefix.java2
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java5
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java3
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularLists.java3
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularMaps.java4
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularMapsWithSetterPrefix.java64
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularNoAuto.java3
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularNoAutoWithSetterPrefix.java3
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularNullBehavior1.java160
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java3
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularSets.java4
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularSetsWithSetterPrefix.java4
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java1
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularToBuilderWithNullWithSetterPrefix.java1
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularWildcardListsWithToBuilder.java2
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularWithPrefixes.java1
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularWithPrefixesWithSetterPrefix.java1
-rw-r--r--test/transform/resource/after-delombok/BuilderWithDeprecated.java2
-rw-r--r--test/transform/resource/after-delombok/BuilderWithToBuilder.java1
-rw-r--r--test/transform/resource/after-delombok/CheckerFrameworkBuilder.java1
-rw-r--r--test/transform/resource/after-delombok/CheckerFrameworkSuperBuilder.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderBasic.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderSingularAnnotatedTypes.java2
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithCustomBuilderMethod.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithGenerics.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithGenerics2.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithPrefixes.java1
-rw-r--r--test/transform/resource/after-ecj/BuilderDefaultsWarnings.java2
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularAnnotatedTypes.java4
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularAnnotatedTypesWithSetterPrefix.java4
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java10
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java6
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularLists.java6
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularMaps.java8
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularMapsWithSetterPrefix.java78
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularNoAuto.java6
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularNoAutoWithSetterPrefix.java6
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularNullBehavior1.java154
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java6
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularSets.java8
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularSetsWithSetterPrefix.java8
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java2
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularToBuilderWithNullWithSetterPrefix.java2
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularWildcardListsWithToBuilder.java4
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularWithPrefixes.java2
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularWithPrefixesWithSetterPrefix.java2
-rw-r--r--test/transform/resource/after-ecj/BuilderWithDeprecated.java4
-rw-r--r--test/transform/resource/after-ecj/BuilderWithToBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/CheckerFrameworkBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/CheckerFrameworkSuperBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/I2335_BuilderMultipleObtainVia.java4
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderBasic.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderSingularAnnotatedTypes.java4
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithCustomBuilderMethod.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithGenerics.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithGenerics2.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java2
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithPrefixes.java2
-rw-r--r--test/transform/resource/before/BuilderSingularMapsWithSetterPrefix.java1
-rw-r--r--test/transform/resource/before/BuilderSingularNullBehavior1.java13
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