e64ed17b2e824db8da65f971339ae43f8d84a481
[unical.git] / gson / com / google / gson / internal / bind / ReflectiveTypeAdapterFactory.java
1 /*
2 * Copyright (C) 2011 Google Inc.
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 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.SerializedName;
25 import com.google.gson.internal.$Gson$Types;
26 import com.google.gson.internal.ConstructorConstructor;
27 import com.google.gson.internal.Excluder;
28 import com.google.gson.internal.ObjectConstructor;
29 import com.google.gson.internal.Primitives;
30 import com.google.gson.reflect.TypeToken;
31 import com.google.gson.stream.JsonReader;
32 import com.google.gson.stream.JsonToken;
33 import com.google.gson.stream.JsonWriter;
34 import java.io.IOException;
35 import java.lang.reflect.Field;
36 import java.lang.reflect.Type;
37 import java.util.LinkedHashMap;
38 import java.util.Map;
39
40 /**
41 * Type adapter that reflects over the fields and methods of a class.
42 */
43 public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
44 private final ConstructorConstructor constructorConstructor;
45 private final FieldNamingStrategy fieldNamingPolicy;
46 private final Excluder excluder;
47
48 public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
49 FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
50 this.constructorConstructor = constructorConstructor;
51 this.fieldNamingPolicy = fieldNamingPolicy;
52 this.excluder = excluder;
53 }
54
55 public boolean excludeField(Field f, boolean serialize) {
56 return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
57 }
58
59 private String getFieldName(Field f) {
60 SerializedName serializedName = f.getAnnotation(SerializedName.class);
61 return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
62 }
63
64 public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
65 Class<? super T> raw = type.getRawType();
66
67 if (!Object.class.isAssignableFrom(raw)) {
68 return null; // it's a primitive!
69 }
70
71 ObjectConstructor<T> constructor = constructorConstructor.get(type);
72 return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
73 }
74
75 private ReflectiveTypeAdapterFactory.BoundField createBoundField(
76 final Gson context, final Field field, final String name,
77 final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
78 final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
79
80 // special casing primitives here saves ~5% on Android...
81 return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
82 final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
83 @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
84 @Override void write(JsonWriter writer, Object value)
85 throws IOException, IllegalAccessException {
86 Object fieldValue = field.get(value);
87 TypeAdapter t =
88 new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
89 t.write(writer, fieldValue);
90 }
91 @Override void read(JsonReader reader, Object value)
92 throws IOException, IllegalAccessException {
93 Object fieldValue = typeAdapter.read(reader);
94 if (fieldValue != null || !isPrimitive) {
95 field.set(value, fieldValue);
96 }
97 }
98 };
99 }
100
101 private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
102 Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
103 if (raw.isInterface()) {
104 return result;
105 }
106
107 Type declaredType = type.getType();
108 while (raw != Object.class) {
109 Field[] fields = raw.getDeclaredFields();
110 for (Field field : fields) {
111 boolean serialize = excludeField(field, true);
112 boolean deserialize = excludeField(field, false);
113 if (!serialize && !deserialize) {
114 continue;
115 }
116 field.setAccessible(true);
117 Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
118 BoundField boundField = createBoundField(context, field, getFieldName(field),
119 TypeToken.get(fieldType), serialize, deserialize);
120 BoundField previous = result.put(boundField.name, boundField);
121 if (previous != null) {
122 throw new IllegalArgumentException(declaredType
123 + " declares multiple JSON fields named " + previous.name);
124 }
125 }
126 type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
127 raw = type.getRawType();
128 }
129 return result;
130 }
131
132 static abstract class BoundField {
133 final String name;
134 final boolean serialized;
135 final boolean deserialized;
136
137 protected BoundField(String name, boolean serialized, boolean deserialized) {
138 this.name = name;
139 this.serialized = serialized;
140 this.deserialized = deserialized;
141 }
142
143 abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
144 abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
145 }
146
147 public static final class Adapter<T> extends TypeAdapter<T> {
148 private final ObjectConstructor<T> constructor;
149 private final Map<String, BoundField> boundFields;
150
151 private Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
152 this.constructor = constructor;
153 this.boundFields = boundFields;
154 }
155
156 @Override public T read(JsonReader in) throws IOException {
157 if (in.peek() == JsonToken.NULL) {
158 in.nextNull();
159 return null;
160 }
161
162 T instance = constructor.construct();
163
164 try {
165 in.beginObject();
166 while (in.hasNext()) {
167 String name = in.nextName();
168 BoundField field = boundFields.get(name);
169 if (field == null || !field.deserialized) {
170 in.skipValue();
171 } else {
172 field.read(in, instance);
173 }
174 }
175 } catch (IllegalStateException e) {
176 throw new JsonSyntaxException(e);
177 } catch (IllegalAccessException e) {
178 throw new AssertionError(e);
179 }
180 in.endObject();
181 return instance;
182 }
183
184 @Override public void write(JsonWriter out, T value) throws IOException {
185 if (value == null) {
186 out.nullValue();
187 return;
188 }
189
190 out.beginObject();
191 try {
192 for (BoundField boundField : boundFields.values()) {
193 if (boundField.serialized) {
194 out.name(boundField.name);
195 boundField.write(out, value);
196 }
197 }
198 } catch (IllegalAccessException e) {
199 throw new AssertionError();
200 }
201 out.endObject();
202 }
203 }
204 }
This page took 0.026647 seconds and 3 git commands to generate.