Skip to content

Commit d19bb33

Browse files
committed
Add support for Nullable on Optional property builders
1 parent 66d0be1 commit d19bb33

File tree

4 files changed

+84
-9
lines changed

4 files changed

+84
-9
lines changed

value/src/main/java/com/google/auto/value/processor/BuilderSpec.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@
3030
import com.google.common.collect.ImmutableSet;
3131
import com.google.common.collect.Iterables;
3232
import com.google.common.collect.Sets;
33+
import jdk.internal.dynalink.linker.LinkerServices;
34+
3335
import java.util.Map;
3436
import java.util.Set;
3537
import javax.annotation.processing.ProcessingEnvironment;
38+
import javax.lang.model.element.AnnotationMirror;
3639
import javax.lang.model.element.Element;
3740
import javax.lang.model.element.ElementKind;
3841
import javax.lang.model.element.ExecutableElement;
@@ -310,12 +313,14 @@ public class PropertySetter {
310313
private final String parameterTypeString;
311314
private final boolean primitiveParameter;
312315
private final String copyOf;
316+
private final String nullableAnnotation;
313317

314318
public PropertySetter(
315319
ExecutableElement setter, TypeMirror propertyType, TypeSimplifier typeSimplifier) {
316320
this.access = AutoValueProcessor.access(setter);
317321
this.name = setter.getSimpleName().toString();
318-
TypeMirror parameterType = Iterables.getOnlyElement(setter.getParameters()).asType();
322+
VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
323+
TypeMirror parameterType = parameterElement.asType();
319324
primitiveParameter = parameterType.getKind().isPrimitive();
320325
String simplifiedParameterType = typeSimplifier.simplify(parameterType);
321326
if (setter.isVarArgs()) {
@@ -327,10 +332,30 @@ public PropertySetter(
327332
boolean sameType = typeUtils.isSameType(typeUtils.erasure(parameterType), erasedPropertyType);
328333
if (sameType) {
329334
this.copyOf = null;
335+
this.nullableAnnotation = "";
330336
} else {
331337
String rawTarget = typeSimplifier.simplifyRaw(erasedPropertyType);
332-
String of = Optionalish.isOptional(propertyType) ? "of" : "copyOf";
338+
Optionalish optional = Optionalish.createIfOptional(propertyType, rawTarget);
339+
String nullableAnnotation = "";
340+
String of = null;
341+
if (optional != null) {
342+
for (AnnotationMirror annotationMirror : parameterElement.getAnnotationMirrors()) {
343+
AnnotationOutput annotationOutput = new AnnotationOutput(typeSimplifier);
344+
String annotationName = annotationOutput.sourceFormForAnnotation(annotationMirror);
345+
if (annotationName.equals("@Nullable") || annotationName.endsWith(".Nullable")) {
346+
of = optional.getNullable();
347+
nullableAnnotation = annotationName + " ";
348+
break;
349+
}
350+
}
351+
if (of == null) {
352+
of = "of";
353+
}
354+
} else {
355+
of = "copyOf";
356+
}
333357
this.copyOf = rawTarget + "." + of + "(%s)";
358+
this.nullableAnnotation = nullableAnnotation;
334359
}
335360
}
336361

@@ -350,6 +375,10 @@ public boolean getPrimitiveParameter() {
350375
return primitiveParameter;
351376
}
352377

378+
public String getNullableAnnotation() {
379+
return nullableAnnotation;
380+
}
381+
353382
public String copy(AutoValueProcessor.Property property) {
354383
if (copyOf == null) {
355384
return property.toString();
@@ -358,7 +387,7 @@ public String copy(AutoValueProcessor.Property property) {
358387
String copy = String.format(copyOf, property);
359388

360389
// Add a null guard only in cases where we are using copyOf and the property is @Nullable.
361-
if (property.isNullable()) {
390+
if (property.isNullable() || nullableAnnotation != null) {
362391
copy = String.format("(%s == null ? null : %s)", property, copy);
363392
}
364393

value/src/main/java/com/google/auto/value/processor/Optionalish.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ public String getEmpty() {
103103
return rawTypeSpelling + empty;
104104
}
105105

106+
/**
107+
* Returns a string representing the method call to obtain the nullable version of this Optional.
108+
* This will be something like {@code "fromNullable()"} or possibly {@code "ofNullable()"}. It does not have a final semicolon.
109+
*
110+
* <p>This method is public so that it can be referenced as {@code p.optional.nullable} from
111+
* templates.
112+
*/
113+
public String getNullable() {
114+
if (optionalType.getTypeArguments().isEmpty()) {
115+
// No typeArguments means a primitive wrapper -- it has no nullable input
116+
return "of";
117+
}
118+
TypeElement typeElement = MoreElements.asType(optionalType.asElement());
119+
return typeElement.getQualifiedName().toString().startsWith("java.util.")
120+
? "ofNullable"
121+
: "fromNullable";
122+
}
123+
106124
TypeMirror getContainedType(Types typeUtils) {
107125
List<? extends TypeMirror> typeArguments = optionalType.getTypeArguments();
108126
switch (typeArguments.size()) {

value/src/main/java/com/google/auto/value/processor/autovalue.vm

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,17 @@ $a
244244

245245
#foreach ($setter in $builderSetters[$p.name])
246246

247+
#if ($p.nullable)
248+
#set ($nullableAnnotation = $p.nullableAnnotation)
249+
#else
250+
#set ($nullableAnnotation = $setter.nullableAnnotation)
251+
#end
252+
247253
@Override
248254
${setter.access}${builderTypeName}${builderActualTypes} ##
249-
${setter.name}(${p.nullableAnnotation}$setter.parameterType $p) {
255+
${setter.name}(${nullableAnnotation}$setter.parameterType $p) {
250256

251-
#if (!$setter.primitiveParameter && !$p.nullable)
257+
#if (!$setter.primitiveParameter && !$nullableAnnotation)
252258

253259
if ($p == null) {
254260
throw new NullPointerException("Null $p.name");

value/src/test/java/com/google/auto/value/processor/CompilationTest.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ public void correctBuilder() throws Exception {
602602
" public abstract ImmutableList<T> anImmutableList();",
603603
" public abstract Optional<String> anOptionalString();",
604604
" public abstract NestedAutoValue<T> aNestedAutoValue();",
605+
" public abstract Optional<Object> anOptionalObject();",
605606
"",
606607
" public abstract Builder<T> toBuilder();",
607608
"",
@@ -616,6 +617,7 @@ public void correctBuilder() throws Exception {
616617
" public abstract Builder<T> anOptionalString(Optional<String> s);",
617618
" public abstract Builder<T> anOptionalString(String s);",
618619
" public abstract NestedAutoValue.Builder<T> aNestedAutoValueBuilder();",
620+
" public abstract Builder<T> anOptionalObject(@Nullable Object s);",
619621
"",
620622
" public Builder<T> aList(ArrayList<T> x) {",
621623
// ArrayList should not be imported in the generated class.
@@ -675,6 +677,7 @@ public void correctBuilder() throws Exception {
675677
" private final ImmutableList<T> anImmutableList;",
676678
" private final Optional<String> anOptionalString;",
677679
" private final NestedAutoValue<T> aNestedAutoValue;",
680+
" private final Optional<Object> anOptionalObject;",
678681
"",
679682
" private AutoValue_Baz(",
680683
" int anInt,",
@@ -683,14 +686,16 @@ public void correctBuilder() throws Exception {
683686
" List<T> aList,",
684687
" ImmutableList<T> anImmutableList,",
685688
" Optional<String> anOptionalString,",
686-
" NestedAutoValue<T> aNestedAutoValue) {",
689+
" NestedAutoValue<T> aNestedAutoValue,",
690+
" Optional<Object> anOptionalObject) {",
687691
" this.anInt = anInt;",
688692
" this.aByteArray = aByteArray;",
689693
" this.aNullableIntArray = aNullableIntArray;",
690694
" this.aList = aList;",
691695
" this.anImmutableList = anImmutableList;",
692696
" this.anOptionalString = anOptionalString;",
693697
" this.aNestedAutoValue = aNestedAutoValue;",
698+
" this.anOptionalObject = anOptionalObject;",
694699
" }",
695700
"",
696701
" @Override public int anInt() {",
@@ -724,6 +729,10 @@ public void correctBuilder() throws Exception {
724729
" return aNestedAutoValue;",
725730
" }",
726731
"",
732+
" @Override public Optional<Object> anOptionalObject() {",
733+
" return anOptionalObject;",
734+
" }",
735+
"",
727736
" @Override public String toString() {",
728737
" return \"Baz{\"",
729738
" + \"anInt=\" + anInt + \", \"",
@@ -732,7 +741,8 @@ public void correctBuilder() throws Exception {
732741
" + \"aList=\" + aList + \", \"",
733742
" + \"anImmutableList=\" + anImmutableList + \", \"",
734743
" + \"anOptionalString=\" + anOptionalString + \", \"",
735-
" + \"aNestedAutoValue=\" + aNestedAutoValue",
744+
" + \"aNestedAutoValue=\" + aNestedAutoValue + \", \"",
745+
" + \"anOptionalObject=\" + anOptionalObject",
736746
" + \"}\";",
737747
" }",
738748
"",
@@ -752,7 +762,8 @@ public void correctBuilder() throws Exception {
752762
" && (this.aList.equals(that.aList()))",
753763
" && (this.anImmutableList.equals(that.anImmutableList()))",
754764
" && (this.anOptionalString.equals(that.anOptionalString()))",
755-
" && (this.aNestedAutoValue.equals(that.aNestedAutoValue()));",
765+
" && (this.aNestedAutoValue.equals(that.aNestedAutoValue()))",
766+
" && (this.anOptionalObject.equals(that.anOptionalObject()));",
756767
" }",
757768
" return false;",
758769
" }",
@@ -773,6 +784,8 @@ public void correctBuilder() throws Exception {
773784
" h ^= this.anOptionalString.hashCode();",
774785
" h *= 1000003;",
775786
" h ^= this.aNestedAutoValue.hashCode();",
787+
" h *= 1000003;",
788+
" h ^= this.anOptionalObject.hashCode();",
776789
" return h;",
777790
" }",
778791
"",
@@ -790,6 +803,7 @@ public void correctBuilder() throws Exception {
790803
" private Optional<String> anOptionalString = Optional.absent();",
791804
" private NestedAutoValue.Builder<T> aNestedAutoValueBuilder$;",
792805
" private NestedAutoValue<T> aNestedAutoValue;",
806+
" private Optional<Object> anOptionalObject = Optional.absent();",
793807
"",
794808
" Builder() {",
795809
" }",
@@ -802,6 +816,7 @@ public void correctBuilder() throws Exception {
802816
" this.anImmutableList = source.anImmutableList();",
803817
" this.anOptionalString = source.anOptionalString();",
804818
" this.aNestedAutoValue = source.aNestedAutoValue();",
819+
" this.anOptionalObject = source.anOptionalObject();",
805820
" }",
806821
"",
807822
" @Override",
@@ -921,6 +936,12 @@ public void correctBuilder() throws Exception {
921936
" }",
922937
"",
923938
" @Override",
939+
" public Baz.Builder<T> anOptionalObject(@Nullable Object anOptionalObject) {",
940+
" this.anOptionalObject = Optional.fromNullable(anOptionalObject);",
941+
" return this;",
942+
" }",
943+
"",
944+
" @Override",
924945
" public Baz<T> build() {",
925946
" if (anImmutableListBuilder$ != null) {",
926947
" this.anImmutableList = anImmutableListBuilder$.build();",
@@ -953,7 +974,8 @@ public void correctBuilder() throws Exception {
953974
" this.aList,",
954975
" this.anImmutableList,",
955976
" this.anOptionalString,",
956-
" this.aNestedAutoValue);",
977+
" this.aNestedAutoValue,",
978+
" this.anOptionalObject);",
957979
" }",
958980
" }",
959981
"}");

0 commit comments

Comments
 (0)