1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.platform.spectatio.configs.validators; 18 19 import com.google.gson.Gson; 20 import com.google.gson.TypeAdapter; 21 import com.google.gson.TypeAdapterFactory; 22 import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; 23 import com.google.gson.reflect.TypeToken; 24 25 import java.lang.reflect.Field; 26 import java.util.LinkedHashMap; 27 import java.util.Map; 28 29 /** 30 * {@link ValidateSpectatioConfigForUnknownProperties} is an GSON Type Adaptor Factory for 31 * validating the properties in Spectatio JSON Config. 32 * 33 * <p>GSON by default ignores any extra properties in the JSON configuration. This class ensures 34 * that the JSON is checked for unknown properties and throw an exception if any unknown properties 35 * are present. 36 */ 37 public class ValidateSpectatioConfigForUnknownProperties implements TypeAdapterFactory { 38 @Override create(Gson gson, TypeToken<T> type)39 public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 40 TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); 41 42 if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) { 43 return delegate; 44 } 45 46 try { 47 // Patch the reflection to be compatible with the changes in 48 // ReflectiveTypeAdapterFactory 49 // introduced by 50 // https://android-review.git.corp.google.com/c/platform/external/gson/+/2298458 51 Field f = ReflectiveTypeAdapterFactory.Adapter.class.getDeclaredField("boundFields"); 52 f.setAccessible(true); 53 54 Map boundFieldsMap = 55 new LinkedHashMap((Map) f.get(delegate)) { 56 @Override 57 public Object get(Object key) { 58 if (!super.containsKey(key) || super.get(key) == null) { 59 throw new RuntimeException( 60 String.format( 61 "Unknown property %s in Spectatio JSON Config.", 62 key)); 63 } 64 return super.get(key); 65 } 66 }; 67 68 f.set(delegate, boundFieldsMap); 69 } catch (Exception ex) { 70 throw new RuntimeException(ex); 71 } 72 73 return delegate; 74 } 75 } 76