blob: d35b8fb3fda9f5452b2b496642e5c0491c8a30c0 [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.json;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.SubmitRequirementExpressionResult;
import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.inject.TypeLiteral;
import java.io.IOException;
import java.util.Optional;
/**
* A {@code TypeAdapterFactory} for Optional {@code SubmitRequirementExpressionResult}.
*
* <p>{@link SubmitRequirementResult#submittabilityExpressionResult} was previously serialized as a
* mandatory field, but was later on migrated to an optional field. The server needs to handle
* deserializing of both formats.
*/
public class OptionalSubmitRequirementExpressionResultAdapterFactory implements TypeAdapterFactory {
private static final TypeToken<?> OPTIONAL_SR_EXPRESSION_RESULT_TOKEN =
TypeToken.get(new TypeLiteral<Optional<SubmitRequirementExpressionResult>>() {}.getType());
private static final TypeToken<?> SR_EXPRESSION_RESULT_TOKEN =
TypeToken.get(SubmitRequirementExpressionResult.class);
@SuppressWarnings({"unchecked"})
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (typeToken.equals(OPTIONAL_SR_EXPRESSION_RESULT_TOKEN)) {
return (TypeAdapter<T>)
new OptionalSubmitRequirementExpressionResultTypeAdapter(
SubmitRequirementExpressionResult.typeAdapter(gson));
} else if (typeToken.equals(SR_EXPRESSION_RESULT_TOKEN)) {
return (TypeAdapter<T>)
new SubmitRequirementExpressionResultTypeAdapter(
SubmitRequirementExpressionResult.typeAdapter(gson));
}
return null;
}
/**
* Reads json representation of either {@code Optional<SubmitRequirementExpressionResult>} or
* {@code SubmitRequirementExpressionResult}, converting it to {@code Nullable} {@code
* SubmitRequirementExpressionResult}.
*/
@Nullable
private static SubmitRequirementExpressionResult readOptionalOrMandatory(
TypeAdapter<SubmitRequirementExpressionResult> submitRequirementExpressionResultAdapter,
JsonReader in) {
JsonElement parsed = JsonParser.parseReader(in);
if (parsed == null) {
return null;
}
// If it does not have 'value' field, then it was serialized as
// SubmitRequirementExpressionResult directly
if (parsed.getAsJsonObject().has("value")) {
parsed = parsed.getAsJsonObject().get("value");
}
if (parsed == null || parsed.isJsonNull() || parsed.getAsJsonObject().entrySet().isEmpty()) {
return null;
}
return submitRequirementExpressionResultAdapter.fromJsonTree(parsed);
}
/**
* A {@code TypeAdapter} that provides backward compatibility for reading previously non-optional
* {@code SubmitRequirementExpressionResult} field.
*/
private static class OptionalSubmitRequirementExpressionResultTypeAdapter
extends TypeAdapter<Optional<SubmitRequirementExpressionResult>> {
private final TypeAdapter<SubmitRequirementExpressionResult>
submitRequirementExpressionResultAdapter;
public OptionalSubmitRequirementExpressionResultTypeAdapter(
TypeAdapter<SubmitRequirementExpressionResult> submitRequirementResultAdapter) {
this.submitRequirementExpressionResultAdapter = submitRequirementResultAdapter;
}
@Override
public Optional<SubmitRequirementExpressionResult> read(JsonReader in) throws IOException {
return Optional.ofNullable(
readOptionalOrMandatory(submitRequirementExpressionResultAdapter, in));
}
@Override
public void write(JsonWriter out, Optional<SubmitRequirementExpressionResult> value)
throws IOException {
// Serialize the field using the same format used by the AutoValue's default Gson serializer.
out.beginObject();
out.name("value");
if (value.isPresent()) {
out.jsonValue(submitRequirementExpressionResultAdapter.toJson(value.get()));
} else {
out.nullValue();
}
out.endObject();
}
}
/**
* A {@code TypeAdapter} that provides forward compatibility for reading the optional {@code
* SubmitRequirementExpressionResult} field.
*
* <p>TODO(mariasavtchouk): Remove once updated to read the new format only.
*/
private static class SubmitRequirementExpressionResultTypeAdapter
extends TypeAdapter<SubmitRequirementExpressionResult> {
private final TypeAdapter<SubmitRequirementExpressionResult>
submitRequirementExpressionResultAdapter;
public SubmitRequirementExpressionResultTypeAdapter(
TypeAdapter<SubmitRequirementExpressionResult> submitRequirementResultAdapter) {
this.submitRequirementExpressionResultAdapter = submitRequirementResultAdapter;
}
@Override
public SubmitRequirementExpressionResult read(JsonReader in) throws IOException {
return readOptionalOrMandatory(submitRequirementExpressionResultAdapter, in);
}
@Override
public void write(JsonWriter out, SubmitRequirementExpressionResult value) throws IOException {
// Serialize the field using the same format used by the AutoValue's default Gson serializer.
out.jsonValue(submitRequirementExpressionResultAdapter.toJson(value));
}
}
}