Skip to content

Commit 3631f76

Browse files
committed
Add support for Nullable on Optional property builders
1 parent 73452e4 commit 3631f76

4 files changed

Lines changed: 86 additions & 12 deletions

File tree

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Set;
3737

3838
import javax.annotation.processing.ProcessingEnvironment;
39+
import javax.lang.model.element.AnnotationMirror;
3940
import javax.lang.model.element.Element;
4041
import javax.lang.model.element.ElementKind;
4142
import javax.lang.model.element.ExecutableElement;
@@ -314,11 +315,13 @@ public class PropertySetter {
314315
private final String name;
315316
private final String parameterTypeString;
316317
private final String copyOf;
318+
private final String nullableAnnotation;
317319

318320
public PropertySetter(
319321
ExecutableElement setter, TypeMirror propertyType, TypeSimplifier typeSimplifier) {
320322
this.name = setter.getSimpleName().toString();
321-
TypeMirror parameterType = Iterables.getOnlyElement(setter.getParameters()).asType();
323+
VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
324+
TypeMirror parameterType = parameterElement.asType();
322325
String simplifiedParameterType = typeSimplifier.simplify(parameterType);
323326
if (setter.isVarArgs()) {
324327
simplifiedParameterType = simplifiedParameterType.replaceAll("\\[\\]$", "...");
@@ -329,10 +332,30 @@ public PropertySetter(
329332
boolean sameType = typeUtils.isSameType(typeUtils.erasure(parameterType), erasedPropertyType);
330333
if (sameType) {
331334
this.copyOf = null;
335+
this.nullableAnnotation = "";
332336
} else {
333337
String rawTarget = typeSimplifier.simplifyRaw(erasedPropertyType);
334-
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+
}
335357
this.copyOf = rawTarget + "." + of + "(%s)";
358+
this.nullableAnnotation = nullableAnnotation;
336359
}
337360
}
338361

@@ -344,18 +367,22 @@ public String getParameterType() {
344367
return parameterTypeString;
345368
}
346369

370+
public String getNullableAnnotation() {
371+
return nullableAnnotation;
372+
}
373+
347374
public String copy(AutoValueProcessor.Property property) {
348375
if (copyOf == null) {
349376
return property.toString();
350377
}
351-
378+
352379
String copy = String.format(copyOf, property);
353-
380+
354381
// Add a null guard only in cases where we are using copyOf and the property is @Nullable.
355382
if (property.isNullable()) {
356383
copy = String.format("(%s == null ? null : %s)", property, copy);
357384
}
358-
385+
359386
return copy;
360387
}
361388
}

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,29 @@ public String getRawType() {
100100
public String getEmpty() {
101101
TypeElement typeElement = MoreElements.asType(optionalType.asElement());
102102
String empty = typeElement.getQualifiedName().toString().startsWith("java.util.")
103-
? ".empty()"
104-
: ".absent()";
103+
? ".empty()"
104+
: ".absent()";
105105
return rawTypeSpelling + empty;
106106
}
107107

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

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,15 @@ ${gwtCompatibleAnnotation}
246246

247247
#foreach ($setter in $builderSetters[$p.name])
248248

249+
#if ($p.nullable)
250+
#set ($nullableAnnotation = $p.nullableAnnotation)
251+
#else
252+
#set ($nullableAnnotation = $setter.nullableAnnotation)
253+
#end
254+
249255
@Override
250256
public ${builderTypeName}${builderActualTypes} ##
251-
${setter.name}(${p.nullableAnnotation}$setter.parameterType $p) {
257+
${setter.name}(${nullableAnnotation}$setter.parameterType $p) {
252258

253259
#if ($propertyBuilder)
254260

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
@@ -604,6 +604,7 @@ public void correctBuilder() throws Exception {
604604
" public abstract List<T> aList();",
605605
" public abstract ImmutableList<T> anImmutableList();",
606606
" public abstract Optional<String> anOptionalString();",
607+
" public abstract Optional<Object> anOptionalObject();",
607608
"",
608609
" public abstract Builder<T> toBuilder();",
609610
"",
@@ -617,6 +618,7 @@ public void correctBuilder() throws Exception {
617618
" public abstract ImmutableList.Builder<T> anImmutableListBuilder();",
618619
" public abstract Builder<T> anOptionalString(Optional<String> s);",
619620
" public abstract Builder<T> anOptionalString(String s);",
621+
" public abstract Builder<T> anOptionalObject(@Nullable Object s);",
620622
"",
621623
" public Builder<T> aList(ArrayList<T> x) {",
622624
// ArrayList should not be imported in the generated class.
@@ -653,20 +655,23 @@ public void correctBuilder() throws Exception {
653655
" private final List<T> aList;",
654656
" private final ImmutableList<T> anImmutableList;",
655657
" private final Optional<String> anOptionalString;",
658+
" private final Optional<Object> anOptionalObject;",
656659
"",
657660
" private AutoValue_Baz(",
658661
" int anInt,",
659662
" byte[] aByteArray,",
660663
" @Nullable int[] aNullableIntArray,",
661664
" List<T> aList,",
662665
" ImmutableList<T> anImmutableList,",
663-
" Optional<String> anOptionalString) {",
666+
" Optional<String> anOptionalString,",
667+
" Optional<Object> anOptionalObject) {",
664668
" this.anInt = anInt;",
665669
" this.aByteArray = aByteArray;",
666670
" this.aNullableIntArray = aNullableIntArray;",
667671
" this.aList = aList;",
668672
" this.anImmutableList = anImmutableList;",
669673
" this.anOptionalString = anOptionalString;",
674+
" this.anOptionalObject = anOptionalObject;",
670675
" }",
671676
"",
672677
" @Override public int anInt() {",
@@ -696,14 +701,19 @@ public void correctBuilder() throws Exception {
696701
" return anOptionalString;",
697702
" }",
698703
"",
704+
" @Override public Optional<Object> anOptionalObject() {",
705+
" return anOptionalObject;",
706+
" }",
707+
"",
699708
" @Override public String toString() {",
700709
" return \"Baz{\"",
701710
" + \"anInt=\" + anInt + \", \"",
702711
" + \"aByteArray=\" + Arrays.toString(aByteArray) + \", \"",
703712
" + \"aNullableIntArray=\" + Arrays.toString(aNullableIntArray) + \", \"",
704713
" + \"aList=\" + aList + \", \"",
705714
" + \"anImmutableList=\" + anImmutableList + \", \"",
706-
" + \"anOptionalString=\" + anOptionalString",
715+
" + \"anOptionalString=\" + anOptionalString + \", \"",
716+
" + \"anOptionalObject=\" + anOptionalObject",
707717
" + \"}\";",
708718
" }",
709719
"",
@@ -722,7 +732,8 @@ public void correctBuilder() throws Exception {
722732
+ "? ((AutoValue_Baz) that).aNullableIntArray : that.aNullableIntArray()))",
723733
" && (this.aList.equals(that.aList()))",
724734
" && (this.anImmutableList.equals(that.anImmutableList()))",
725-
" && (this.anOptionalString.equals(that.anOptionalString()));",
735+
" && (this.anOptionalString.equals(that.anOptionalString()))",
736+
" && (this.anOptionalObject.equals(that.anOptionalObject()));",
726737
" }",
727738
" return false;",
728739
" }",
@@ -741,6 +752,8 @@ public void correctBuilder() throws Exception {
741752
" h ^= this.anImmutableList.hashCode();",
742753
" h *= 1000003;",
743754
" h ^= this.anOptionalString.hashCode();",
755+
" h *= 1000003;",
756+
" h ^= this.anOptionalObject.hashCode();",
744757
" return h;",
745758
" }",
746759
"",
@@ -756,6 +769,7 @@ public void correctBuilder() throws Exception {
756769
" private ImmutableList.Builder<T> anImmutableListBuilder$;",
757770
" private ImmutableList<T> anImmutableList;",
758771
" private Optional<String> anOptionalString = Optional.absent();",
772+
" private Optional<Object> anOptionalObject = Optional.absent();",
759773
"",
760774
" Builder() {",
761775
" this.anImmutableList = ImmutableList.of();",
@@ -768,6 +782,7 @@ public void correctBuilder() throws Exception {
768782
" this.aList = source.aList();",
769783
" this.anImmutableList = source.anImmutableList();",
770784
" this.anOptionalString = source.anOptionalString();",
785+
" this.anOptionalObject = source.anOptionalObject();",
771786
" }",
772787
"",
773788
" @Override",
@@ -852,6 +867,12 @@ public void correctBuilder() throws Exception {
852867
" }",
853868
"",
854869
" @Override",
870+
" public Baz.Builder<T> anOptionalObject(@Nullable Object anOptionalObject) {",
871+
" this.anOptionalObject = Optional.fromNullable(anOptionalObject);",
872+
" return this;",
873+
" }",
874+
"",
875+
" @Override",
855876
" public Baz<T> build() {",
856877
" if (anImmutableListBuilder$ != null) {",
857878
" anImmutableList = anImmutableListBuilder$.build();",
@@ -875,7 +896,8 @@ public void correctBuilder() throws Exception {
875896
" this.aNullableIntArray,",
876897
" this.aList,",
877898
" this.anImmutableList,",
878-
" this.anOptionalString);",
899+
" this.anOptionalString,",
900+
" this.anOptionalObject);",
879901
" }",
880902
" }",
881903
"}");

0 commit comments

Comments
 (0)