diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java index 1cb9186002..900a948b0b 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -310,12 +311,14 @@ public class PropertySetter { private final String parameterTypeString; private final boolean primitiveParameter; private final String copyOf; + private final String nullableAnnotation; public PropertySetter( ExecutableElement setter, TypeMirror propertyType, TypeSimplifier typeSimplifier) { this.access = AutoValueProcessor.access(setter); this.name = setter.getSimpleName().toString(); - TypeMirror parameterType = Iterables.getOnlyElement(setter.getParameters()).asType(); + VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); + TypeMirror parameterType = parameterElement.asType(); primitiveParameter = parameterType.getKind().isPrimitive(); String simplifiedParameterType = typeSimplifier.simplify(parameterType); if (setter.isVarArgs()) { @@ -327,10 +330,30 @@ public PropertySetter( boolean sameType = typeUtils.isSameType(typeUtils.erasure(parameterType), erasedPropertyType); if (sameType) { this.copyOf = null; + this.nullableAnnotation = ""; } else { String rawTarget = typeSimplifier.simplifyRaw(erasedPropertyType); - String of = Optionalish.isOptional(propertyType) ? "of" : "copyOf"; + Optionalish optional = Optionalish.createIfOptional(propertyType, rawTarget); + String nullableAnnotation = ""; + String of = null; + if (optional != null) { + for (AnnotationMirror annotationMirror : parameterElement.getAnnotationMirrors()) { + AnnotationOutput annotationOutput = new AnnotationOutput(typeSimplifier); + String annotationName = annotationOutput.sourceFormForAnnotation(annotationMirror); + if (annotationName.equals("@Nullable") || annotationName.endsWith(".Nullable")) { + of = optional.getNullable(); + nullableAnnotation = annotationName + " "; + break; + } + } + if (of == null) { + of = "of"; + } + } else { + of = "copyOf"; + } this.copyOf = rawTarget + "." + of + "(%s)"; + this.nullableAnnotation = nullableAnnotation; } } @@ -350,6 +373,10 @@ public boolean getPrimitiveParameter() { return primitiveParameter; } + public String getNullableAnnotation() { + return nullableAnnotation; + } + public String copy(AutoValueProcessor.Property property) { if (copyOf == null) { return property.toString(); @@ -358,7 +385,7 @@ public String copy(AutoValueProcessor.Property property) { String copy = String.format(copyOf, property); // Add a null guard only in cases where we are using copyOf and the property is @Nullable. - if (property.isNullable()) { + if (property.isNullable() || nullableAnnotation != null) { copy = String.format("(%s == null ? null : %s)", property, copy); } diff --git a/value/src/main/java/com/google/auto/value/processor/Optionalish.java b/value/src/main/java/com/google/auto/value/processor/Optionalish.java index e7c27b5fad..5687fabb8c 100644 --- a/value/src/main/java/com/google/auto/value/processor/Optionalish.java +++ b/value/src/main/java/com/google/auto/value/processor/Optionalish.java @@ -103,6 +103,24 @@ public String getEmpty() { return rawTypeSpelling + empty; } + /** + * Returns a string representing the method call to obtain the nullable version of this Optional. + * This will be something like {@code "fromNullable()"} or possibly {@code "ofNullable()"}. It does not have a final semicolon. + * + *
This method is public so that it can be referenced as {@code p.optional.nullable} from
+ * templates.
+ */
+ public String getNullable() {
+ if (optionalType.getTypeArguments().isEmpty()) {
+ // No typeArguments means a primitive wrapper -- it has no nullable input
+ return "of";
+ }
+ TypeElement typeElement = MoreElements.asType(optionalType.asElement());
+ return typeElement.getQualifiedName().toString().startsWith("java.util.")
+ ? "ofNullable"
+ : "fromNullable";
+ }
+
TypeMirror getContainedType(Types typeUtils) {
List extends TypeMirror> typeArguments = optionalType.getTypeArguments();
switch (typeArguments.size()) {
diff --git a/value/src/main/java/com/google/auto/value/processor/autovalue.vm b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
index 1a5ed34b75..be88a64bac 100644
--- a/value/src/main/java/com/google/auto/value/processor/autovalue.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
@@ -244,11 +244,17 @@ $a
#foreach ($setter in $builderSetters[$p.name])
+ #if ($p.nullable)
+ #set ($nullableAnnotation = $p.nullableAnnotation)
+ #else
+ #set ($nullableAnnotation = $setter.nullableAnnotation)
+ #end
+
@Override
${setter.access}${builderTypeName}${builderActualTypes} ##
- ${setter.name}(${p.nullableAnnotation}$setter.parameterType $p) {
+ ${setter.name}(${nullableAnnotation}$setter.parameterType $p) {
- #if (!$setter.primitiveParameter && !$p.nullable)
+ #if (!$setter.primitiveParameter && !$nullableAnnotation)
if ($p == null) {
throw new NullPointerException("Null $p.name");
diff --git a/value/src/test/java/com/google/auto/value/processor/CompilationTest.java b/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
index 5392a6597f..2dd901fbef 100644
--- a/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
@@ -602,6 +602,7 @@ public void correctBuilder() throws Exception {
" public abstract ImmutableList