package com.google.gson.internal.bind; import com.google.gson.FieldNamingStrategy; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.annotations.SerializedName; import com.google.gson.internal..Gson.Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.Excluder; import com.google.gson.internal.ObjectConstructor; import com.google.gson.internal.Primitives; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.LinkedHashMap; import java.util.Map; public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; private final FieldNamingStrategy fieldNamingPolicy; private final Excluder excluder; public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, FieldNamingStrategy fieldNamingPolicy, Excluder excluder) { this.constructorConstructor = constructorConstructor; this.fieldNamingPolicy = fieldNamingPolicy; this.excluder = excluder; } public boolean excludeField(Field f, boolean serialize) { return (!this.excluder.excludeClass(f.getType(), serialize)) && (!this.excluder.excludeField(f, serialize)); } private String getFieldName(Field f) { SerializedName serializedName = (SerializedName)f.getAnnotation(SerializedName.class); return serializedName == null ? this.fieldNamingPolicy.translateName(f) : serializedName.value(); } public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { Class raw = type.getRawType(); if (!Object.class.isAssignableFrom(raw)) { return null; } ObjectConstructor constructor = this.constructorConstructor.get(type); return new Adapter(constructor, getBoundFields(gson, type, raw), null); } private BoundField createBoundField(final Gson context, final Field field, String name, final TypeToken<?> fieldType, boolean serialize, boolean deserialize) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); return new BoundField(name, serialize, deserialize) { final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType); void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException { Object fieldValue = field.get(value); TypeAdapter t = new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); t.write(writer, fieldValue); } void read(JsonReader reader, Object value) throws IOException, IllegalAccessException { Object fieldValue = this.typeAdapter.read(reader); if ((fieldValue != null) || (!isPrimitive)) field.set(value, fieldValue); } }; } private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) { Map result = new LinkedHashMap(); if (raw.isInterface()) { return result; } Type declaredType = type.getType(); while (raw != Object.class) { Field[] fields = raw.getDeclaredFields(); for (Field field : fields) { boolean serialize = excludeField(field, true); boolean deserialize = excludeField(field, false); if ((serialize) || (deserialize)) { field.setAccessible(true); Type fieldType = .Gson.Types.resolve(type.getType(), raw, field.getGenericType()); BoundField boundField = createBoundField(context, field, getFieldName(field), TypeToken.get(fieldType), serialize, deserialize); BoundField previous = (BoundField)result.put(boundField.name, boundField); if (previous != null) { throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name); } } } type = TypeToken.get(.Gson.Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); raw = type.getRawType(); } return result; } public final class Adapter<T> extends TypeAdapter<T> { private final ObjectConstructor<T> constructor; private final Map<String, ReflectiveTypeAdapterFactory.BoundField> boundFields; private Adapter(Map<String, ReflectiveTypeAdapterFactory.BoundField> constructor) { this.constructor = constructor; this.boundFields = boundFields; } public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } Object instance = this.constructor.construct(); try { in.beginObject(); while (in.hasNext()) { String name = in.nextName(); ReflectiveTypeAdapterFactory.BoundField field = (ReflectiveTypeAdapterFactory.BoundField)this.boundFields.get(name); if ((field == null) || (!field.deserialized)) in.skipValue(); else field.read(in, instance); } } catch (IllegalStateException e) { throw new JsonSyntaxException(e); } catch (IllegalAccessException e) { throw new AssertionError(e); } in.endObject(); return instance; } public void write(JsonWriter out, T value) throws IOException { if (value == null) { out.nullValue(); return; } out.beginObject(); try { for (ReflectiveTypeAdapterFactory.BoundField boundField : this.boundFields.values()) if (boundField.serialized) { out.name(boundField.name); boundField.write(out, value); } } catch (IllegalAccessException e) { throw new AssertionError(); } out.endObject(); } } static abstract class BoundField { final String name; final boolean serialized; final boolean deserialized; protected BoundField(String name, boolean serialized, boolean deserialized) { this.name = name; this.serialized = serialized; this.deserialized = deserialized; } abstract void write(JsonWriter paramJsonWriter, Object paramObject) throws IOException, IllegalAccessException; abstract void read(JsonReader paramJsonReader, Object paramObject) throws IOException, IllegalAccessException; } }