diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index f1021f12..b1a10df2 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -16,14 +16,12 @@ package com.google.gson; +import java.lang.reflect.Type; + import com.google.gson.internal.Preconditions; import com.google.gson.internal.Primitives; import com.google.gson.internal.Types; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Type; - /** * Provides ability to apply a visitor to an object and all of its fields * recursively. @@ -83,6 +81,7 @@ final class ObjectNavigator { private final ExclusionStrategy exclusionStrategy; private final ObjectTypePair objTypePair; + private ReflectingFieldNavigator reflectingFieldNavigator; /** * @param objTypePair @@ -92,6 +91,7 @@ final class ObjectNavigator { * object. */ ObjectNavigator(ObjectTypePair objTypePair, ExclusionStrategy exclusionStrategy) { + reflectingFieldNavigator = new ReflectingFieldNavigator(exclusionStrategy); this.objTypePair = objTypePair; this.exclusionStrategy = Preconditions.checkNotNull(exclusionStrategy); } @@ -123,14 +123,7 @@ final class ObjectNavigator { visitor.getTarget(); } else { visitor.startVisitingObject(objectToVisit); - ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType(); - Class topLevelClass = Types.getRawType(currObjTypePair.type); - for (Class curr = topLevelClass; curr != null && !curr.equals(Object.class); curr = - curr.getSuperclass()) { - if (!curr.isSynthetic()) { - navigateClassFields(objectToVisit, curr, visitor); - } - } + reflectingFieldNavigator.visitFieldsReflectively(objTypePair, visitor); } } finally { visitor.end(objTypePair); @@ -138,49 +131,9 @@ final class ObjectNavigator { } } - private boolean isPrimitiveOrString(Object objectToVisit) { + private static boolean isPrimitiveOrString(Object objectToVisit) { Class realClazz = objectToVisit.getClass(); return realClazz == Object.class || realClazz == String.class || Primitives.unwrap(realClazz).isPrimitive(); } - - private void navigateClassFields(Object obj, Class clazz, Visitor visitor) { - Field[] fields = clazz.getDeclaredFields(); - AccessibleObject.setAccessible(fields, true); - for (Field f : fields) { - FieldAttributes fieldAttributes = new FieldAttributes(clazz, f); - if (exclusionStrategy.shouldSkipField(fieldAttributes) - || exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) { - continue; // skip - } - Type declaredTypeOfField = getTypeInfoForField(f, objTypePair.type); - boolean visitedWithCustomHandler = - visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj); - if (!visitedWithCustomHandler) { - if (Types.isArray(declaredTypeOfField)) { - visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj); - } else { - visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj); - } - } - } - } - - - /** - * Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a - * "TypeVariable" in a parameterized type then it evaluates the real type. - * - * @param f the actual field object to retrieve the type from - * @param typeDefiningF the type that contains the field {@code f} - * @return the type information for the field - */ - public static Type getTypeInfoForField(Field f, Type typeDefiningF) { - Class rawType = Types.getRawType(typeDefiningF); - if (!f.getDeclaringClass().isAssignableFrom(rawType)) { - // this field is unrelated to the type; the user probably omitted type information - return f.getGenericType(); - } - return Types.resolve(typeDefiningF, rawType, f.getGenericType()); - } } diff --git a/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java b/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java new file mode 100644 index 00000000..f9ed9015 --- /dev/null +++ b/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java @@ -0,0 +1,100 @@ +/* + * 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 com.google.gson; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Type; + +import com.google.gson.ObjectNavigator.Visitor; +import com.google.gson.internal.Preconditions; +import com.google.gson.internal.Primitives; +import com.google.gson.internal.Types; + +/** + * Visits each of the fields of the specified class using reflection + * + * @author Inderjeet Singh + * @author Joel Leitch + * @author Jesse Wilson + */ +final class ReflectingFieldNavigator { + + private final ExclusionStrategy exclusionStrategy; + + /** + * @param objTypePair + * The object,type (fully genericized) being navigated + * @param exclusionStrategy + * the concrete strategy object to be used to filter out fields of an + * object. + */ + ReflectingFieldNavigator(ExclusionStrategy exclusionStrategy) { + this.exclusionStrategy = Preconditions.checkNotNull(exclusionStrategy); + } + + void visitFieldsReflectively(ObjectTypePair objTypePair, Visitor visitor) { + ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType(); + Class topLevelClass = Types.getRawType(currObjTypePair.type); + for (Class curr = topLevelClass; curr != null && !curr.equals(Object.class); curr = + curr.getSuperclass()) { + if (!curr.isSynthetic()) { + navigateClassFields(objTypePair.getObject(), objTypePair.type, curr, visitor); + } + } + } + + private void navigateClassFields(Object obj, Type objType, + Class classInInheritanceHierarchyForObj, Visitor visitor) { + Field[] fields = classInInheritanceHierarchyForObj.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field f : fields) { + FieldAttributes fieldAttributes = new FieldAttributes(classInInheritanceHierarchyForObj, f); + if (exclusionStrategy.shouldSkipField(fieldAttributes) + || exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) { + continue; // skip + } + Type declaredTypeOfField = getTypeInfoForField(f, objType); + boolean visitedWithCustomHandler = + visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj); + if (!visitedWithCustomHandler) { + if (Types.isArray(declaredTypeOfField)) { + visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj); + } else { + visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj); + } + } + } + } + + + /** + * Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a + * "TypeVariable" in a parameterized type then it evaluates the real type. + * + * @param f the actual field object to retrieve the type from + * @param typeDefiningF the type that contains the field {@code f} + * @return the type information for the field + */ + public static Type getTypeInfoForField(Field f, Type typeDefiningF) { + Class rawType = Types.getRawType(typeDefiningF); + if (!f.getDeclaringClass().isAssignableFrom(rawType)) { + // this field is unrelated to the type; the user probably omitted type information + return f.getGenericType(); + } + return Types.resolve(typeDefiningF, rawType, f.getGenericType()); + } +}