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