Skip to content

Commit

Permalink
For InjectingObjectCodec, also forwards dependency to serialize.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 181105886
  • Loading branch information
aoeui authored and Copybara-Service committed Jan 8, 2018
1 parent 590057a commit ecb481d
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,26 @@
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;

/**
* Like ObjectCodec, but handles cases where deserialization requires injection of non-serializable
* dependencies.
*/
/** Like ObjectCodec, but allows user-specified injected dependencies. */
public interface InjectingObjectCodec<T, D> extends BaseCodec<T> {

/**
* Serializes {@code obj}, inverse of {@link #deserialize(CodedInputStream)}.
*
* @param dependency the injected dependency
* @param obj the object to serialize
* @param codedOut the {@link CodedOutputStream} to write this object into. Implementations need
* not call {@link CodedOutputStream#flush()}, this should be handled by the caller.
* @throws SerializationException on failure to serialize
* @throws IOException on {@link IOException} during serialization
*/
void serialize(T obj, CodedOutputStream codedOut) throws SerializationException, IOException;
void serialize(D dependency, T obj, CodedOutputStream codedOut)
throws SerializationException, IOException;

/**
* Deserializes from {@code codedIn}, inverse of {@link #serialize(Object, CodedOutputStream)}.
*
* @param dependency the non-serializable, injected dependency
* @param dependency the injected dependency
* @param codedIn the {@link CodedInputStream} to read the serialized object from
* @return the object deserialized from {@code codedIn}
* @throws SerializationException on failure to deserialize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public Class<T> getEncodedClass() {
@Override
public void serialize(T obj, CodedOutputStream codedOut)
throws SerializationException, IOException {
codec.serialize(obj, codedOut);
codec.serialize(dependency, obj, codedOut);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,16 @@ public class PolymorphicHelper {

private PolymorphicHelper() {}

/**
* Serializes a polymorphic type.
*
* @param dependency if null, it means that the parent, polymorphic type, provides no dependency.
* It is valid for dependency to be non-null, with an enclosed null value. This means that
* dependency itself is null (as opposed to non-existent).
*/
@SuppressWarnings("unchecked")
public static void serialize(Object input, CodedOutputStream codedOut)
public static void serialize(
Object input, CodedOutputStream codedOut, @Nullable Optional<?> dependency)
throws IOException, SerializationException {
if (input != null) {
Class<?> clazz = input.getClass();
Expand All @@ -39,7 +47,11 @@ public static void serialize(Object input, CodedOutputStream codedOut)
if (codec instanceof ObjectCodec) {
((ObjectCodec) codec).serialize(input, codedOut);
} else if (codec instanceof InjectingObjectCodec) {
((InjectingObjectCodec) codec).serialize(input, codedOut);
if (dependency == null) {
throw new SerializationException(
clazz.getCanonicalName() + " serialize parent class lacks required dependency.");
}
((InjectingObjectCodec) codec).serialize(dependency.orElse(null), input, codedOut);
} else {
throw new SerializationException(
clazz.getCanonicalName()
Expand All @@ -58,7 +70,8 @@ public static void serialize(Object input, CodedOutputStream codedOut)
* Deserializes a polymorphic type.
*
* @param dependency if null, it means that the parent, polymorphic type, provides no dependency.
* It is valid for dependency to be non-null, with an enclosed null value.
* It is valid for dependency to be non-null, with an enclosed null value. This means the
* dependency itself is null (as opposed to non-existent).
*/
@SuppressWarnings("unchecked")
public static Object deserialize(CodedInputStream codedIn, @Nullable Optional<?> dependency)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ private TypeSpec.Builder buildClassWithConstructorStrategy(

initializeUnsafeOffsets(codecClassBuilder, encodedType, parameters.fields);

codecClassBuilder.addMethod(
buildSerializeMethodWithConstructor(encodedType, parameters.fields));
codecClassBuilder.addMethod(buildSerializeMethodWithConstructor(encodedType, parameters));

MethodSpec.Builder deserializeBuilder =
AutoCodecUtil.initializeDeserializeMethodBuilder(encodedType, parameters.dependency);
Expand Down Expand Up @@ -233,10 +232,10 @@ private static ExecutableElement selectConstructorForConstructorStrategy(
}

private MethodSpec buildSerializeMethodWithConstructor(
TypeElement encodedType, List<? extends VariableElement> parameters) {
TypeElement encodedType, PartitionedParameters parameters) {
MethodSpec.Builder serializeBuilder =
AutoCodecUtil.initializeSerializeMethodBuilder(encodedType);
for (VariableElement parameter : parameters) {
AutoCodecUtil.initializeSerializeMethodBuilder(encodedType, parameters.dependency);
for (VariableElement parameter : parameters.fields) {
VariableElement field = getFieldByName(encodedType, parameter.getSimpleName().toString());
TypeKind typeKind = field.asType().getKind();
switch (typeKind) {
Expand Down Expand Up @@ -282,7 +281,8 @@ private TypeSpec.Builder buildClassWithPublicFieldsStrategy(
.stream()
.filter(this::isPublicField)
.collect(toImmutableList());
codecClassBuilder.addMethod(buildSerializeMethodWithPublicFields(encodedType, publicFields));
codecClassBuilder.addMethod(
buildSerializeMethodWithPublicFields(encodedType, publicFields, dependency));
MethodSpec.Builder deserializeBuilder =
AutoCodecUtil.initializeDeserializeMethodBuilder(encodedType, dependency);
buildDeserializeBody(deserializeBuilder, publicFields);
Expand All @@ -300,9 +300,11 @@ private boolean isPublicField(VariableElement element) {
}

private MethodSpec buildSerializeMethodWithPublicFields(
TypeElement encodedType, List<? extends VariableElement> parameters) {
TypeElement encodedType,
List<? extends VariableElement> parameters,
@Nullable TypeElement dependency) {
MethodSpec.Builder serializeBuilder =
AutoCodecUtil.initializeSerializeMethodBuilder(encodedType);
AutoCodecUtil.initializeSerializeMethodBuilder(encodedType, dependency);
for (VariableElement parameter : parameters) {
String paramAccessor = "input." + parameter.getSimpleName();
TypeKind typeKind = parameter.asType().getKind();
Expand Down Expand Up @@ -476,14 +478,23 @@ private static TypeSpec.Builder buildClassWithPolymorphicStrategy(
}
TypeSpec.Builder codecClassBuilder =
AutoCodecUtil.initializeCodecClassBuilder(encodedType, dependency);
codecClassBuilder.addMethod(buildPolymorphicSerializeMethod(encodedType));
codecClassBuilder.addMethod(buildPolymorphicSerializeMethod(encodedType, dependency));
codecClassBuilder.addMethod(buildPolymorphicDeserializeMethod(encodedType, dependency));
return codecClassBuilder;
}

private static MethodSpec buildPolymorphicSerializeMethod(TypeElement encodedType) {
MethodSpec.Builder builder = AutoCodecUtil.initializeSerializeMethodBuilder(encodedType);
builder.addStatement("$T.serialize(input, codedOut)", PolymorphicHelper.class);
private static MethodSpec buildPolymorphicSerializeMethod(
TypeElement encodedType, @Nullable TypeElement dependency) {
MethodSpec.Builder builder =
AutoCodecUtil.initializeSerializeMethodBuilder(encodedType, dependency);
if (dependency == null) {
builder.addStatement("$T.serialize(input, codedOut, null)", PolymorphicHelper.class);
} else {
builder.addStatement(
"$T.serialize(input, codedOut, $T.ofNullable(dependency))",
PolymorphicHelper.class,
Optional.class);
}
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,33 @@ static MethodSpec.Builder initializeGetEncodedClassMethod(TypeElement encodedTyp
}

static MethodSpec.Builder initializeSerializeMethodBuilder(TypeElement encodedType) {
return MethodSpec.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
return initializeSerializeMethodBuilder(encodedType, null);
}

/**
* Initializes the appropriate deserialize method based on presence of dependency.
*
* <p>{@link InjectingObjectCodec#serialize} if dependency is non-null and {@link
* ObjectCodec#serialize} otherwise.
*
* @param encodedType type being serialized
* @param dependency type being injected
*/
static MethodSpec.Builder initializeSerializeMethodBuilder(
TypeElement encodedType, @Nullable TypeElement dependency) {
MethodSpec.Builder builder =
MethodSpec.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addAnnotation(Override.class)
.addException(SerializationException.class)
.addException(IOException.class);
if (dependency != null) {
builder.addParameter(TypeName.get(dependency.asType()), "dependency");
}
return builder
.addParameter(TypeName.get(encodedType.asType()), "input")
.addParameter(CodedOutputStream.class, "codedOut")
.addAnnotation(Override.class)
.addException(SerializationException.class)
.addException(IOException.class);
.addParameter(CodedOutputStream.class, "codedOut");
}

/** Initializes {@link ObjectCodec#deserialize}. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,21 @@ public boolean matches(DeclaredType type) {

@Override
public void addSerializationCode(Context context) {
context.builder.addStatement(
"$T.CODEC.serialize($L, codedOut)", context.getTypeName(), context.name);
TypeMirror codecType = getCodec(context.type).get().asType();
if (isSubtypeErased(codecType, ObjectCodec.class)) {
context.builder.addStatement(
"$T.CODEC.serialize($L, codedOut)", context.getTypeName(), context.name);
} else if (isSubtypeErased(codecType, InjectingObjectCodec.class)) {
context.builder.addStatement(
"$T.CODEC.serialize(dependency, $L, codedOut)",
context.getTypeName(),
context.name);
} else {
throw new IllegalArgumentException(
"CODEC field of "
+ ((TypeElement) context.type.asElement()).getQualifiedName()
+ " is neither ObjectCodec nor InjectingCodec");
}
}

@Override
Expand Down

0 comments on commit ecb481d

Please sign in to comment.