2 * Copyright (C) 2011 Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.google
.gson
.internal
.bind
;
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
;
41 * Type adapter that reflects over the fields and methods of a class.
43 public final class ReflectiveTypeAdapterFactory
implements TypeAdapterFactory
{
44 private final ConstructorConstructor constructorConstructor
;
45 private final FieldNamingStrategy fieldNamingPolicy
;
46 private final Excluder excluder
;
48 public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor
,
49 FieldNamingStrategy fieldNamingPolicy
, Excluder excluder
) {
50 this.constructorConstructor
= constructorConstructor
;
51 this.fieldNamingPolicy
= fieldNamingPolicy
;
52 this.excluder
= excluder
;
55 public boolean excludeField(Field f
, boolean serialize
) {
56 return !excluder
.excludeClass(f
.getType(), serialize
) && !excluder
.excludeField(f
, serialize
);
59 private String
getFieldName(Field f
) {
60 SerializedName serializedName
= f
.getAnnotation(SerializedName
.class);
61 return serializedName
== null ? fieldNamingPolicy
.translateName(f
) : serializedName
.value();
64 public <T
> TypeAdapter
<T
> create(Gson gson
, final TypeToken
<T
> type
) {
65 Class
<?
super T
> raw
= type
.getRawType();
67 if (!Object
.class.isAssignableFrom(raw
)) {
68 return null; // it's a primitive!
71 ObjectConstructor
<T
> constructor
= constructorConstructor
.get(type
);
72 return new Adapter
<T
>(constructor
, getBoundFields(gson
, type
, raw
));
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());
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
);
88 new TypeAdapterRuntimeTypeWrapper(context
, this.typeAdapter
, fieldType
.getType());
89 t
.write(writer
, fieldValue
);
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
);
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()) {
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
) {
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
);
126 type
= TypeToken
.get($Gson$Types
.resolve(type
.getType(), raw
, raw
.getGenericSuperclass()));
127 raw
= type
.getRawType();
132 static abstract class BoundField
{
134 final boolean serialized
;
135 final boolean deserialized
;
137 protected BoundField(String name
, boolean serialized
, boolean deserialized
) {
139 this.serialized
= serialized
;
140 this.deserialized
= deserialized
;
143 abstract void write(JsonWriter writer
, Object value
) throws IOException
, IllegalAccessException
;
144 abstract void read(JsonReader reader
, Object value
) throws IOException
, IllegalAccessException
;
147 public static final class Adapter
<T
> extends TypeAdapter
<T
> {
148 private final ObjectConstructor
<T
> constructor
;
149 private final Map
<String
, BoundField
> boundFields
;
151 private Adapter(ObjectConstructor
<T
> constructor
, Map
<String
, BoundField
> boundFields
) {
152 this.constructor
= constructor
;
153 this.boundFields
= boundFields
;
156 @Override public T
read(JsonReader in
) throws IOException
{
157 if (in
.peek() == JsonToken
.NULL
) {
162 T instance
= constructor
.construct();
166 while (in
.hasNext()) {
167 String name
= in
.nextName();
168 BoundField field
= boundFields
.get(name
);
169 if (field
== null || !field
.deserialized
) {
172 field
.read(in
, instance
);
175 } catch (IllegalStateException e
) {
176 throw new JsonSyntaxException(e
);
177 } catch (IllegalAccessException e
) {
178 throw new AssertionError(e
);
184 @Override public void write(JsonWriter out
, T value
) throws IOException
{
192 for (BoundField boundField
: boundFields
.values()) {
193 if (boundField
.serialized
) {
194 out
.name(boundField
.name
);
195 boundField
.write(out
, value
);
198 } catch (IllegalAccessException e
) {
199 throw new AssertionError();