Line data Source code
1 : // Copyright (C) 2022 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.json; 16 : 17 : import com.google.gerrit.common.Nullable; 18 : import com.google.gerrit.entities.SubmitRequirementExpressionResult; 19 : import com.google.gerrit.entities.SubmitRequirementResult; 20 : import com.google.gson.Gson; 21 : import com.google.gson.JsonElement; 22 : import com.google.gson.JsonParser; 23 : import com.google.gson.TypeAdapter; 24 : import com.google.gson.TypeAdapterFactory; 25 : import com.google.gson.reflect.TypeToken; 26 : import com.google.gson.stream.JsonReader; 27 : import com.google.gson.stream.JsonWriter; 28 : import com.google.inject.TypeLiteral; 29 : import java.io.IOException; 30 : import java.util.Optional; 31 : 32 : /** 33 : * A {@code TypeAdapterFactory} for Optional {@code SubmitRequirementExpressionResult}. 34 : * 35 : * <p>{@link SubmitRequirementResult#submittabilityExpressionResult} was previously serialized as a 36 : * mandatory field, but was later on migrated to an optional field. The server needs to handle 37 : * deserializing of both formats. 38 : */ 39 153 : public class OptionalSubmitRequirementExpressionResultAdapterFactory implements TypeAdapterFactory { 40 : 41 153 : private static final TypeToken<?> OPTIONAL_SR_EXPRESSION_RESULT_TOKEN = 42 153 : TypeToken.get(new TypeLiteral<Optional<SubmitRequirementExpressionResult>>() {}.getType()); 43 : 44 153 : private static final TypeToken<?> SR_EXPRESSION_RESULT_TOKEN = 45 153 : TypeToken.get(SubmitRequirementExpressionResult.class); 46 : 47 : @SuppressWarnings({"unchecked"}) 48 : @Nullable 49 : @Override 50 : public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 51 65 : if (typeToken.equals(OPTIONAL_SR_EXPRESSION_RESULT_TOKEN)) { 52 3 : return (TypeAdapter<T>) 53 : new OptionalSubmitRequirementExpressionResultTypeAdapter( 54 3 : SubmitRequirementExpressionResult.typeAdapter(gson)); 55 65 : } else if (typeToken.equals(SR_EXPRESSION_RESULT_TOKEN)) { 56 0 : return (TypeAdapter<T>) 57 : new SubmitRequirementExpressionResultTypeAdapter( 58 0 : SubmitRequirementExpressionResult.typeAdapter(gson)); 59 : } 60 65 : return null; 61 : } 62 : 63 : /** 64 : * Reads json representation of either {@code Optional<SubmitRequirementExpressionResult>} or 65 : * {@code SubmitRequirementExpressionResult}, converting it to {@code Nullable} {@code 66 : * SubmitRequirementExpressionResult}. 67 : */ 68 : @Nullable 69 : private static SubmitRequirementExpressionResult readOptionalOrMandatory( 70 : TypeAdapter<SubmitRequirementExpressionResult> submitRequirementExpressionResultAdapter, 71 : JsonReader in) { 72 3 : JsonElement parsed = JsonParser.parseReader(in); 73 3 : if (parsed == null) { 74 0 : return null; 75 : } 76 : // If it does not have 'value' field, then it was serialized as 77 : // SubmitRequirementExpressionResult directly 78 3 : if (parsed.getAsJsonObject().has("value")) { 79 3 : parsed = parsed.getAsJsonObject().get("value"); 80 : } 81 3 : if (parsed == null || parsed.isJsonNull() || parsed.getAsJsonObject().entrySet().isEmpty()) { 82 3 : return null; 83 : } 84 3 : return submitRequirementExpressionResultAdapter.fromJsonTree(parsed); 85 : } 86 : 87 : /** 88 : * A {@code TypeAdapter} that provides backward compatibility for reading previously non-optional 89 : * {@code SubmitRequirementExpressionResult} field. 90 : */ 91 : private static class OptionalSubmitRequirementExpressionResultTypeAdapter 92 : extends TypeAdapter<Optional<SubmitRequirementExpressionResult>> { 93 : 94 : private final TypeAdapter<SubmitRequirementExpressionResult> 95 : submitRequirementExpressionResultAdapter; 96 : 97 : public OptionalSubmitRequirementExpressionResultTypeAdapter( 98 3 : TypeAdapter<SubmitRequirementExpressionResult> submitRequirementResultAdapter) { 99 3 : this.submitRequirementExpressionResultAdapter = submitRequirementResultAdapter; 100 3 : } 101 : 102 : @Override 103 : public Optional<SubmitRequirementExpressionResult> read(JsonReader in) throws IOException { 104 3 : return Optional.ofNullable( 105 3 : readOptionalOrMandatory(submitRequirementExpressionResultAdapter, in)); 106 : } 107 : 108 : @Override 109 : public void write(JsonWriter out, Optional<SubmitRequirementExpressionResult> value) 110 : throws IOException { 111 : // Serialize the field using the same format used by the AutoValue's default Gson serializer. 112 3 : out.beginObject(); 113 3 : out.name("value"); 114 3 : if (value.isPresent()) { 115 3 : out.jsonValue(submitRequirementExpressionResultAdapter.toJson(value.get())); 116 : } else { 117 3 : out.nullValue(); 118 : } 119 3 : out.endObject(); 120 3 : } 121 : } 122 : 123 : /** 124 : * A {@code TypeAdapter} that provides forward compatibility for reading the optional {@code 125 : * SubmitRequirementExpressionResult} field. 126 : * 127 : * <p>TODO(mariasavtchouk): Remove once updated to read the new format only. 128 : */ 129 : private static class SubmitRequirementExpressionResultTypeAdapter 130 : extends TypeAdapter<SubmitRequirementExpressionResult> { 131 : 132 : private final TypeAdapter<SubmitRequirementExpressionResult> 133 : submitRequirementExpressionResultAdapter; 134 : 135 : public SubmitRequirementExpressionResultTypeAdapter( 136 0 : TypeAdapter<SubmitRequirementExpressionResult> submitRequirementResultAdapter) { 137 0 : this.submitRequirementExpressionResultAdapter = submitRequirementResultAdapter; 138 0 : } 139 : 140 : @Override 141 : public SubmitRequirementExpressionResult read(JsonReader in) throws IOException { 142 0 : return readOptionalOrMandatory(submitRequirementExpressionResultAdapter, in); 143 : } 144 : 145 : @Override 146 : public void write(JsonWriter out, SubmitRequirementExpressionResult value) throws IOException { 147 : // Serialize the field using the same format used by the AutoValue's default Gson serializer. 148 0 : out.jsonValue(submitRequirementExpressionResultAdapter.toJson(value)); 149 0 : } 150 : } 151 : }