| // 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"}) | |
| @Nullable | |
| @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)); | |
| } | |
| } | |
| } |