| /* | 
|  * Copyright (C) 2011 Google Inc. | 
|  * | 
|  * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  * you may not use this file except in compliance with the License. | 
|  * You may obtain a copy of the License at | 
|  * | 
|  *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  * | 
|  * Unless required by applicable law or agreed to in writing, software | 
|  * distributed under the License is distributed on an "AS IS" BASIS, | 
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  * See the License for the specific language governing permissions and | 
|  * limitations under the License. | 
|  */ | 
|   | 
| package cn.emay.sdk.util.json.gson.internal.bind; | 
|   | 
| import static cn.emay.sdk.util.json.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter; | 
|   | 
| import java.io.IOException; | 
| import java.lang.reflect.Field; | 
| import java.lang.reflect.Type; | 
| import java.util.LinkedHashMap; | 
| import java.util.LinkedList; | 
| import java.util.List; | 
| import java.util.Map; | 
|   | 
| import cn.emay.sdk.util.json.gson.FieldNamingStrategy; | 
| import cn.emay.sdk.util.json.gson.Gson; | 
| import cn.emay.sdk.util.json.gson.JsonSyntaxException; | 
| import cn.emay.sdk.util.json.gson.TypeAdapter; | 
| import cn.emay.sdk.util.json.gson.TypeAdapterFactory; | 
| import cn.emay.sdk.util.json.gson.annotations.JsonAdapter; | 
| import cn.emay.sdk.util.json.gson.annotations.SerializedName; | 
| import cn.emay.sdk.util.json.gson.internal.$Gson$Types; | 
| import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor; | 
| import cn.emay.sdk.util.json.gson.internal.Excluder; | 
| import cn.emay.sdk.util.json.gson.internal.ObjectConstructor; | 
| import cn.emay.sdk.util.json.gson.internal.Primitives; | 
| import cn.emay.sdk.util.json.gson.reflect.TypeToken; | 
| import cn.emay.sdk.util.json.gson.stream.JsonReader; | 
| import cn.emay.sdk.util.json.gson.stream.JsonToken; | 
| import cn.emay.sdk.util.json.gson.stream.JsonWriter; | 
|   | 
| /** | 
|  * Type adapter that reflects over the fields and methods of a class. | 
|  */ | 
| 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 excludeField(f, serialize, excluder); | 
|     } | 
|   | 
|     static boolean excludeField(Field f, boolean serialize, Excluder excluder) { | 
|         return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); | 
|     } | 
|   | 
|     /** first element holds the default name */ | 
|     private List<String> getFieldNames(Field f) { | 
|         return getFieldName(fieldNamingPolicy, f); | 
|     } | 
|   | 
|     /** first element holds the default name */ | 
|     static List<String> getFieldName(FieldNamingStrategy fieldNamingPolicy, Field f) { | 
|         SerializedName serializedName = f.getAnnotation(SerializedName.class); | 
|         List<String> fieldNames = new LinkedList<String>(); | 
|         if (serializedName == null) { | 
|             fieldNames.add(fieldNamingPolicy.translateName(f)); | 
|         } else { | 
|             fieldNames.add(serializedName.value()); | 
|             for (String alternate : serializedName.alternate()) { | 
|                 fieldNames.add(alternate); | 
|             } | 
|         } | 
|         return fieldNames; | 
|     } | 
|   | 
|     @Override | 
|     public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) { | 
|         Class<? super T> raw = type.getRawType(); | 
|   | 
|         if (!Object.class.isAssignableFrom(raw)) { | 
|             return null; // it's a primitive! | 
|         } | 
|   | 
|         ObjectConstructor<T> constructor = constructorConstructor.get(type); | 
|         return new Adapter<T>(constructor, getBoundFields(gson, type, raw)); | 
|     } | 
|   | 
|     private ReflectiveTypeAdapterFactory.BoundField createBoundField(final Gson context, final Field field, final String name, final TypeToken<?> fieldType, boolean serialize, boolean deserialize) { | 
|         final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); | 
|         // special casing primitives here saves ~5% on Android... | 
|         return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { | 
|             final TypeAdapter<?> typeAdapter = getFieldAdapter(context, field, fieldType); | 
|   | 
|             @SuppressWarnings({ "unchecked", "rawtypes" }) // the type adapter and field type always agree | 
|             @Override | 
|             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); | 
|             } | 
|   | 
|             @Override | 
|             void read(JsonReader reader, Object value) throws IOException, IllegalAccessException { | 
|                 Object fieldValue = typeAdapter.read(reader); | 
|                 if (fieldValue != null || !isPrimitive) { | 
|                     field.set(value, fieldValue); | 
|                 } | 
|             } | 
|   | 
|             @Override | 
|             public boolean writeField(Object value) throws IOException, IllegalAccessException { | 
|                 if (!serialized) | 
|                     return false; | 
|                 Object fieldValue = field.get(value); | 
|                 return fieldValue != value; // avoid recursion for example for Throwable.cause | 
|             } | 
|         }; | 
|     } | 
|   | 
|     TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) { | 
|         JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); | 
|         if (annotation != null) { | 
|             TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation); | 
|             if (adapter != null) | 
|                 return adapter; | 
|         } | 
|         return gson.getAdapter(fieldType); | 
|     } | 
|   | 
|     private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) { | 
|         Map<String, BoundField> result = new LinkedHashMap<String, BoundField>(); | 
|         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) { | 
|                     continue; | 
|                 } | 
|                 field.setAccessible(true); | 
|                 Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); | 
|                 List<String> fieldNames = getFieldNames(field); | 
|                 BoundField previous = null; | 
|                 for (int i = 0; i < fieldNames.size(); ++i) { | 
|                     String name = fieldNames.get(i); | 
|                     if (i != 0) | 
|                         serialize = false; // only serialize the default name | 
|                     BoundField boundField = createBoundField(context, field, name, TypeToken.get(fieldType), serialize, deserialize); | 
|                     BoundField replaced = result.put(name, boundField); | 
|                     if (previous == null) | 
|                         previous = replaced; | 
|                 } | 
|                 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; | 
|     } | 
|   | 
|     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 boolean writeField(Object value) throws IOException, IllegalAccessException; | 
|   | 
|         abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException; | 
|   | 
|         abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException; | 
|     } | 
|   | 
|     public static final class Adapter<T> extends TypeAdapter<T> { | 
|         private final ObjectConstructor<T> constructor; | 
|         private final Map<String, BoundField> boundFields; | 
|   | 
|         Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) { | 
|             this.constructor = constructor; | 
|             this.boundFields = boundFields; | 
|         } | 
|   | 
|         @Override | 
|         public T read(JsonReader in) throws IOException { | 
|             if (in.peek() == JsonToken.NULL) { | 
|                 in.nextNull(); | 
|                 return null; | 
|             } | 
|   | 
|             T instance = constructor.construct(); | 
|   | 
|             try { | 
|                 in.beginObject(); | 
|                 while (in.hasNext()) { | 
|                     String name = in.nextName(); | 
|                     BoundField field = 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; | 
|         } | 
|   | 
|         @Override | 
|         public void write(JsonWriter out, T value) throws IOException { | 
|             if (value == null) { | 
|                 out.nullValue(); | 
|                 return; | 
|             } | 
|   | 
|             out.beginObject(); | 
|             try { | 
|                 for (BoundField boundField : boundFields.values()) { | 
|                     if (boundField.writeField(value)) { | 
|                         out.name(boundField.name); | 
|                         boundField.write(out, value); | 
|                     } | 
|                 } | 
|             } catch (IllegalAccessException e) { | 
|                 throw new AssertionError(e); | 
|             } | 
|             out.endObject(); | 
|         } | 
|     } | 
| } |