refactored the field navigation using reflection out of ObjectNavigator.

This commit is contained in:
Inderjeet Singh 2011-03-24 18:28:23 +00:00
parent 005c93e383
commit b819307de8
2 changed files with 106 additions and 53 deletions

View File

@ -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());
}
}

View File

@ -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());
}
}