/*
* Copyright (C) 2008 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.smartandroid.sa.json;
import java.lang.reflect.Type;
import com.smartandroid.sa.json.internal.$Gson$Types;
/**
* Provides ability to apply a visitor to an object and all of its fields
* recursively.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class ObjectNavigator {
public interface Visitor {
public void start(ObjectTypePair node);
public void end(ObjectTypePair node);
/**
* This is called before the object navigator starts visiting the
* current object
*/
void startVisitingObject(Object node);
/**
* This is called to visit the current object if it is an array
*/
void visitArray(Object array, Type componentType);
/**
* This is called to visit an object field of the current object
*/
void visitObjectField(FieldAttributes f, Type typeOfF, Object obj);
/**
* This is called to visit an array field of the current object
*/
void visitArrayField(FieldAttributes f, Type typeOfF, Object obj);
/**
* This is called to visit an object using a custom handler
*
* @return true if a custom handler exists, false otherwise
*/
public boolean visitUsingCustomHandler(ObjectTypePair objTypePair);
/**
* This is called to visit a field of the current object using a custom
* handler
*/
public boolean visitFieldUsingCustomHandler(FieldAttributes f,
Type actualTypeOfField, Object parent);
void visitPrimitive(Object primitive);
/**
* Retrieve the current target
*/
Object getTarget();
}
private final ExclusionStrategy exclusionStrategy;
private final ReflectingFieldNavigator reflectingFieldNavigator;
/**
* @param strategy
* the concrete exclusion strategy object to be used to filter
* out fields of an object.
*/
ObjectNavigator(ExclusionStrategy strategy) {
this.exclusionStrategy = strategy == null ? new NullExclusionStrategy()
: strategy;
this.reflectingFieldNavigator = new ReflectingFieldNavigator(
exclusionStrategy);
}
/**
* Navigate all the fields of the specified object. If a field is null, it
* does not get visited.
*
* @param objTypePair
* The object,type (fully genericized) being navigated
*/
public void accept(ObjectTypePair objTypePair, Visitor visitor) {
if (exclusionStrategy.shouldSkipClass($Gson$Types
.getRawType(objTypePair.type))) {
return;
}
boolean visitedWithCustomHandler = visitor
.visitUsingCustomHandler(objTypePair);
if (!visitedWithCustomHandler) {
objTypePair = objTypePair.toMoreSpecificType();
Object obj = objTypePair.getObject();
Object objectToVisit = (obj == null) ? visitor.getTarget() : obj;
if (objectToVisit == null) {
return;
}
objTypePair.setObject(objectToVisit);
visitor.start(objTypePair);
try {
if ($Gson$Types.isArray(objTypePair.getMoreSpecificType())) {
visitor.visitArray(objectToVisit, objTypePair.type);
} else if (objTypePair.type == Object.class
&& isPrimitiveOrString(objectToVisit)) {
// TODO(Joel): this is only used for deserialization of
// "primitives"
// we should rethink this!!!
visitor.visitPrimitive(objectToVisit);
visitor.getTarget();
} else {
visitor.startVisitingObject(objectToVisit);
reflectingFieldNavigator.visitFieldsReflectively(
objTypePair, visitor);
}
} finally {
visitor.end(objTypePair);
}
}
}
private static boolean isPrimitiveOrString(Object objectToVisit) {
Class<?> realClazz = objectToVisit.getClass();
return realClazz == Object.class || realClazz == String.class
|| Primitives.unwrap(realClazz).isPrimitive();
}
}