aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
13 files changed, 252 insertions, 44 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.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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, char[] setterPrefix) {
+ public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> 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<Statement> 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<String> 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<Builder> {
if (!annotationTypeMatches(Singular.class, child)) continue;
char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
AnnotationValues<Singular> 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<Builder> {
return null;
}
- return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, setterPrefix.toCharArray());
+ NullCollectionBehavior behavior = getNullBehaviorFor(ann, singularInstance, node);
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, behavior, setterPrefix.toCharArray());
}
return null;
}
+
+ static NullCollectionBehavior getNullBehaviorFor(AnnotationValues<Singular> 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<SuperBuilder> {
char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
AnnotationValues<Singular> 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<SuperBuilder> {
return null;
}
- return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
+ NullCollectionBehavior behavior = HandleBuilder.getNullBehaviorFor(ann, singularInstance, node);
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>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<Builder> {
if (!annotationTypeMatches(Singular.class, child)) continue;
Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
AnnotationValues<Singular> 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<Builder> {
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<Singular> 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<SuperBuilder> {
if (!annotationTypeMatches(Singular.class, child)) continue;
Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
AnnotationValues<Singular> 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<SuperBuilder> {
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<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) {
- this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, "");
+ public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer, NullCollectionBehavior nullCollectionBehavior) {
+ this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, nullCollectionBehavior, "");
}
- public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer, String setterPrefix) {
+ public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> 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<JCStatement> 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<JCStatement>();
+ 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.<JCExpression>nil(), exType, List.<JCExpression>of(message), null);
+ JCStatement onNull = maker.Throw(exception);
+ statements.prepend(maker.If(incomingIsNull, onNull, null));
+ } else {
+ LombokImmutableList<String> method = behavior.getMethod();
+ JCExpression invoke = maker.Apply(List.<JCExpression>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<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All");
JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getPluralName())));
- return new ListBuffer<JCStatement>().append(maker.Exec(invokeAdd));
+ statements.append(maker.Exec(invokeAdd));
+
+ return statements;
}
protected abstract JCExpression getPluralMethodParamType(JavacNode builderType);