Skip to content

Commit

Permalink
Allow @AutoOneOf properties to be void.
Browse files Browse the repository at this point in the history
An abstract void method means that the associated kind has no value. The factory method has no parameter and calling the implementation of the void method does nothing unless the instance is of another kind.

RELNOTES=Allow @AutoOneOf properties to be void.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=249301961
  • Loading branch information
eamonnmcmanus authored and ronshapiro committed May 27, 2019
1 parent 0b58d04 commit d5e4f55
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
import static org.junit.Assert.fail;

import com.google.common.testing.EqualsTester;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -518,4 +522,83 @@ public void classAnnotationsCopiedIfCopyAnnotations() {
assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isTrue();
assertThat(ace.getClass().getAnnotation(CopyTest.class).value()).isEqualTo(23);
}

@AutoOneOf(MaybeEmpty.Kind.class)
public abstract static class MaybeEmpty implements Serializable {
public enum Kind {
EMPTY, STRING,
}

public abstract Kind getKind();

public abstract void empty();

public abstract String string();

public static MaybeEmpty ofEmpty() {
return AutoOneOf_AutoOneOfTest_MaybeEmpty.empty();
}

public static MaybeEmpty ofString(String s) {
return AutoOneOf_AutoOneOfTest_MaybeEmpty.string(s);
}
}

@Test
public void voidPropertyIsSingleton() {
MaybeEmpty empty1 = MaybeEmpty.ofEmpty();
MaybeEmpty empty2 = MaybeEmpty.ofEmpty();
assertThat(empty1).isSameInstanceAs(empty2);
}

@Test
public void voidPropertyRemainsSingletonWhenDeserialized() throws Exception {
MaybeEmpty empty1 = MaybeEmpty.ofEmpty();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// We're still compiling this with -source 6, so we can't use try-with-resources.
ObjectOutputStream dos = new ObjectOutputStream(baos);
dos.writeObject(empty1);
dos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
MaybeEmpty empty2 = (MaybeEmpty) ois.readObject();
assertThat(empty2).isSameInstanceAs(empty1);
}

@Test
public void voidPropertyToString() {
MaybeEmpty empty = MaybeEmpty.ofEmpty();
assertThat(empty.toString()).isEqualTo("MaybeEmpty{empty}");
}

@Test
public void voidPropertyHashCodeIsIdentity() {
MaybeEmpty empty = MaybeEmpty.ofEmpty();
assertThat(empty.hashCode()).isEqualTo(System.identityHashCode(empty));
}

@Test
public void voidPropertyGetterDoesNothing() {
MaybeEmpty empty = MaybeEmpty.ofEmpty();
empty.empty();
}

@Test
public void voidPropertyNotEqualToNonVoid() {
MaybeEmpty empty = MaybeEmpty.ofEmpty();
MaybeEmpty notEmpty = MaybeEmpty.ofString("foo");
assertThat(empty).isNotEqualTo(notEmpty);
assertThat(notEmpty).isNotEqualTo(empty);
}

@Test
public void voidPropertyWrongType() {
MaybeEmpty notEmpty = MaybeEmpty.ofString("foo");
try {
notEmpty.empty();
fail();
} catch (UnsupportedOperationException e) {
assertThat(e).hasMessageThat().containsMatch("(?i:string)");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public AutoOneOfProcessor() {
super(AUTO_ONE_OF_NAME);
}

@Override
boolean propertiesCanBeVoid() {
return true;
}

@Override
void processType(TypeElement autoOneOfType) {
if (autoOneOfType.getKind() != ElementKind.CLASS) {
Expand Down Expand Up @@ -255,6 +260,10 @@ private void defineVarsForType(
propertySet(type, propertyMethods, ImmutableListMultimap.of(), ImmutableListMultimap.of());
vars.kindGetter = kindGetter.getSimpleName().toString();
vars.kindType = TypeEncoder.encode(kindGetter.getReturnType());
TypeElement javaIoSerializable = elementUtils().getTypeElement("java.io.Serializable");
vars.serializable =
javaIoSerializable != null // just in case
&& typeUtils().isAssignable(type.asType(), javaIoSerializable.asType());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class AutoOneOfTemplateVars extends AutoValueOrOneOfTemplateVars {
/** Maps property names like {@code dog} to enum constants like {@code DOG}. */
Map<String, String> propertyToKind;

/** True if this {@code @AutoOneOf} class is Serializable. */
Boolean serializable;

private static final Template TEMPLATE = parsedTemplateForResource("autooneof.vm");

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,18 +719,23 @@ static ImmutableSet<ExecutableElement> abstractMethodsIn(
* Returns the subset of property methods in the given set of abstract methods. A property method
* has no arguments, is not void, and is not {@code hashCode()} or {@code toString()}.
*/
static ImmutableSet<ExecutableElement> propertyMethodsIn(Set<ExecutableElement> abstractMethods) {
ImmutableSet<ExecutableElement> propertyMethodsIn(Set<ExecutableElement> abstractMethods) {
ImmutableSet.Builder<ExecutableElement> properties = ImmutableSet.builder();
for (ExecutableElement method : abstractMethods) {
if (method.getParameters().isEmpty()
&& method.getReturnType().getKind() != TypeKind.VOID
&& (method.getReturnType().getKind() != TypeKind.VOID || propertiesCanBeVoid())
&& objectMethodToOverride(method) == ObjectMethod.NONE) {
properties.add(method);
}
}
return properties.build();
}

/** True if void properties are allowed. */
boolean propertiesCanBeVoid() {
return false;
}

/**
* Checks that the return type of the given property method is allowed. Currently, this means that
* it cannot be an array, unless it is a primitive array.
Expand Down
89 changes: 77 additions & 12 deletions value/src/main/java/com/google/auto/value/processor/autooneof.vm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ final class $generatedClass {
## Factory methods.
#foreach ($p in $props)

#if ($p.type == "void")

static $origClass$wildcardTypes $p() {
return Impl_${p}.INSTANCE;
}

#else

## If the @AutoOneOf type is TaskResult<V extends Serializable>, then we might have here:
## static <V extends Serializable> TaskResult<V> value(V value) {
## return new Impl_value<V>(value);
Expand All @@ -55,17 +63,19 @@ final class $generatedClass {

static $formalTypes $origClass$actualTypes $p($p.type $p) {

#if (!$p.kind.primitive)
#if (!$p.kind.primitive)

if ($p == null) {
throw new NullPointerException();
}

#end
#end

return new Impl_$p$actualTypes($p);
}

#end

#end

#foreach ($a in $annotations)
Expand Down Expand Up @@ -99,32 +109,80 @@ final class $generatedClass {

// Implementation when the contained property is "${p}".
private static final class Impl_$p$formalTypes extends Parent_$actualTypes {
private final $p.type $p;

Impl_$p($p.type $p) {
this.$p = $p;
#if ($p.type == "void")

// There is only one instance of this class.
static final Impl_$p$wildcardTypes INSTANCE = new ##
#if ($wildcardTypes == "") Impl_$p() #else Impl_$p<>() #end;

private Impl_$p() {}

@Override
public void ${p.getter}() {}

#if ($serializable)

private Object readResolve() {
return INSTANCE;
}

#end

#if ($toString)

@Override
public $kindType ${kindGetter}() {
return ${kindType}.$propertyToKind[$p.name];
public String toString() {
return "${simpleClassName}{$p.name}";
}

#end

## The implementations of equals and hashCode are equivalent to the ones
## we inherit from Object. We only need to define them if they're redeclared
## as abstract in an ancestor class. But currently we define them always.

#if ($equals)

@Override
public boolean equals($equalsParameterType x) {
return x == this;
}

#end

#if ($hashCode)

@Override
public int hashCode() {
return System.identityHashCode(this);
}

#end

#else

private final $p.type $p;

Impl_$p($p.type $p) {
this.$p = $p;
}

@Override
public $p.type ${p.getter}() {
return $p;
}

#if ($toString)
#if ($toString)

@Override
public String toString() {
return "${simpleClassName}{$p.name=" + this.$p + "}";
}

#end
#end

#if ($equals)
#if ($equals)

@Override
public boolean equals($equalsParameterType x) {
Expand All @@ -137,17 +195,24 @@ final class $generatedClass {
}
}

#end
#end

#if ($hashCode)
#if ($hashCode)

@Override
public int hashCode() {
return #hashCodeExpression($p);
}

#end

#end

@Override
public $kindType ${kindGetter}() {
return ${kindType}.$propertyToKind[$p.name];
}

}

#end
Expand Down
Loading

0 comments on commit d5e4f55

Please sign in to comment.