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