1
16
17 package com.google.gson.internal.bind;
18
19 import com.google.gson.FieldNamingStrategy;
20 import com.google.gson.Gson;
21 import com.google.gson.JsonSyntaxException;
22 import com.google.gson.TypeAdapter;
23 import com.google.gson.TypeAdapterFactory;
24 import com.google.gson.annotations.JsonAdapter;
25 import com.google.gson.annotations.SerializedName;
26 import com.google.gson.internal.$Gson$Types;
27 import com.google.gson.internal.ConstructorConstructor;
28 import com.google.gson.internal.Excluder;
29 import com.google.gson.internal.ObjectConstructor;
30 import com.google.gson.internal.Primitives;
31 import com.google.gson.internal.reflect.ReflectionAccessor;
32 import com.google.gson.reflect.TypeToken;
33 import com.google.gson.stream.JsonReader;
34 import com.google.gson.stream.JsonToken;
35 import com.google.gson.stream.JsonWriter;
36 import java.io.IOException;
37 import java.lang.reflect.Field;
38 import java.lang.reflect.Type;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map;
44
45
48 public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
49 private final ConstructorConstructor constructorConstructor;
50 private final FieldNamingStrategy fieldNamingPolicy;
51 private final Excluder excluder;
52 private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
53 private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
54
55 public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
56 FieldNamingStrategy fieldNamingPolicy, Excluder excluder,
57 JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) {
58 this.constructorConstructor = constructorConstructor;
59 this.fieldNamingPolicy = fieldNamingPolicy;
60 this.excluder = excluder;
61 this.jsonAdapterFactory = jsonAdapterFactory;
62 }
63
64 public boolean excludeField(Field f, boolean serialize) {
65 return excludeField(f, serialize, excluder);
66 }
67
68 static boolean excludeField(Field f, boolean serialize, Excluder excluder) {
69 return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
70 }
71
72
73 private List<String> getFieldNames(Field f) {
74 SerializedName annotation = f.getAnnotation(SerializedName.class);
75 if (annotation == null) {
76 String name = fieldNamingPolicy.translateName(f);
77 return Collections.singletonList(name);
78 }
79
80 String serializedName = annotation.value();
81 String[] alternates = annotation.alternate();
82 if (alternates.length == 0) {
83 return Collections.singletonList(serializedName);
84 }
85
86 List<String> fieldNames = new ArrayList<String>(alternates.length + 1);
87 fieldNames.add(serializedName);
88 for (String alternate : alternates) {
89 fieldNames.add(alternate);
90 }
91 return fieldNames;
92 }
93
94 @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
95 Class<? super T> raw = type.getRawType();
96
97 if (!Object.class.isAssignableFrom(raw)) {
98 return null;
99 }
100
101 ObjectConstructor<T> constructor = constructorConstructor.get(type);
102 return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
103 }
104
105 private ReflectiveTypeAdapterFactory.BoundField createBoundField(
106 final Gson context, final Field field, final String name,
107 final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
108 final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
109
110 JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
111 TypeAdapter<?> mapped = null;
112 if (annotation != null) {
113 mapped = jsonAdapterFactory.getTypeAdapter(
114 constructorConstructor, context, fieldType, annotation);
115 }
116 final boolean jsonAdapterPresent = mapped != null;
117 if (mapped == null) mapped = context.getAdapter(fieldType);
118
119 final TypeAdapter<?> typeAdapter = mapped;
120 return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
121 @SuppressWarnings({"unchecked", "rawtypes"})
122 @Override void write(JsonWriter writer, Object value)
123 throws IOException, IllegalAccessException {
124 Object fieldValue = field.get(value);
125 TypeAdapter t = jsonAdapterPresent ? typeAdapter
126 : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
127 t.write(writer, fieldValue);
128 }
129 @Override void read(JsonReader reader, Object value)
130 throws IOException, IllegalAccessException {
131 Object fieldValue = typeAdapter.read(reader);
132 if (fieldValue != null || !isPrimitive) {
133 field.set(value, fieldValue);
134 }
135 }
136 @Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
137 if (!serialized) return false;
138 Object fieldValue = field.get(value);
139 return fieldValue != value;
140 }
141 };
142 }
143
144 private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
145 Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
146 if (raw.isInterface()) {
147 return result;
148 }
149
150 Type declaredType = type.getType();
151 while (raw != Object.class) {
152 Field[] fields = raw.getDeclaredFields();
153 for (Field field : fields) {
154 boolean serialize = excludeField(field, true);
155 boolean deserialize = excludeField(field, false);
156 if (!serialize && !deserialize) {
157 continue;
158 }
159 accessor.makeAccessible(field);
160 Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
161 List<String> fieldNames = getFieldNames(field);
162 BoundField previous = null;
163 for (int i = 0, size = fieldNames.size(); i < size; ++i) {
164 String name = fieldNames.get(i);
165 if (i != 0) serialize = false;
166 BoundField boundField = createBoundField(context, field, name,
167 TypeToken.get(fieldType), serialize, deserialize);
168 BoundField replaced = result.put(name, boundField);
169 if (previous == null) previous = replaced;
170 }
171 if (previous != null) {
172 throw new IllegalArgumentException(declaredType
173 + " declares multiple JSON fields named " + previous.name);
174 }
175 }
176 type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
177 raw = type.getRawType();
178 }
179 return result;
180 }
181
182 static abstract class BoundField {
183 final String name;
184 final boolean serialized;
185 final boolean deserialized;
186
187 protected BoundField(String name, boolean serialized, boolean deserialized) {
188 this.name = name;
189 this.serialized = serialized;
190 this.deserialized = deserialized;
191 }
192 abstract boolean writeField(Object value) throws IOException, IllegalAccessException;
193 abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
194 abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
195 }
196
197 public static final class Adapter<T> extends TypeAdapter<T> {
198 private final ObjectConstructor<T> constructor;
199 private final Map<String, BoundField> boundFields;
200
201 Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
202 this.constructor = constructor;
203 this.boundFields = boundFields;
204 }
205
206 @Override public T read(JsonReader in) throws IOException {
207 if (in.peek() == JsonToken.NULL) {
208 in.nextNull();
209 return null;
210 }
211
212 T instance = constructor.construct();
213
214 try {
215 in.beginObject();
216 while (in.hasNext()) {
217 String name = in.nextName();
218 BoundField field = boundFields.get(name);
219 if (field == null || !field.deserialized) {
220 in.skipValue();
221 } else {
222 field.read(in, instance);
223 }
224 }
225 } catch (IllegalStateException e) {
226 throw new JsonSyntaxException(e);
227 } catch (IllegalAccessException e) {
228 throw new AssertionError(e);
229 }
230 in.endObject();
231 return instance;
232 }
233
234 @Override public void write(JsonWriter out, T value) throws IOException {
235 if (value == null) {
236 out.nullValue();
237 return;
238 }
239
240 out.beginObject();
241 try {
242 for (BoundField boundField : boundFields.values()) {
243 if (boundField.writeField(value)) {
244 out.name(boundField.name);
245 boundField.write(out, value);
246 }
247 }
248 } catch (IllegalAccessException e) {
249 throw new AssertionError(e);
250 }
251 out.endObject();
252 }
253 }
254 }
255