refactored the field navigation using reflection out of ObjectNavigator.
This commit is contained in:
parent
005c93e383
commit
b819307de8
@ -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());
|
||||
}
|
||||
}
|
||||
|
100
gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java
Normal file
100
gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java
Normal 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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user