/* * 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.Array; import java.lang.reflect.Type; import com.smartandroid.sa.json.internal.$Gson$Preconditions; import com.smartandroid.sa.json.internal.$Gson$Types; /** * A visitor that adds JSON elements corresponding to each field of an object * * @author Inderjeet Singh * @author Joel Leitch */ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { private final ObjectNavigator objectNavigator; private final FieldNamingStrategy2 fieldNamingPolicy; private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers; private final boolean serializeNulls; private final JsonSerializationContext context; private final MemoryRefStack ancestors; private JsonElement root; JsonSerializationVisitor(ObjectNavigator objectNavigator, FieldNamingStrategy2 fieldNamingPolicy, boolean serializeNulls, ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers, JsonSerializationContext context, MemoryRefStack ancestors) { this.objectNavigator = objectNavigator; this.fieldNamingPolicy = fieldNamingPolicy; this.serializeNulls = serializeNulls; this.serializers = serializers; this.context = context; this.ancestors = ancestors; } public Object getTarget() { return null; } public void start(ObjectTypePair node) { if (node == null || node.isSystemOnly()) { return; } if (ancestors.contains(node)) { throw new CircularReferenceException(node); } ancestors.push(node); } public void end(ObjectTypePair node) { if (node != null && !node.isSystemOnly()) { ancestors.pop(); } } public void startVisitingObject(Object node) { assignToRoot(new JsonObject()); } public void visitArray(Object array, Type arrayType) { assignToRoot(new JsonArray()); int length = Array.getLength(array); Type componentType = $Gson$Types.getArrayComponentType(arrayType); for (int i = 0; i < length; ++i) { Object child = Array.get(array, i); // we should not get more specific component type yet since it is // possible // that a custom serializer is registered for the componentType addAsArrayElement(new ObjectTypePair(child, componentType, false, false)); } } public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) { try { if (isFieldNull(f, obj)) { if (serializeNulls) { addChildAsElement(f, JsonNull.INSTANCE); } } else { Object array = getFieldValue(f, obj); addAsChildOfObject(f, new ObjectTypePair(array, typeOfF, false, false)); } } catch (CircularReferenceException e) { throw e.createDetailedException(f); } } public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) { try { if (isFieldNull(f, obj)) { if (serializeNulls) { addChildAsElement(f, JsonNull.INSTANCE); } } else { Object fieldValue = getFieldValue(f, obj); // we should not get more specific component type yet since it // is // possible that a custom // serializer is registered for the componentType addAsChildOfObject(f, new ObjectTypePair(fieldValue, typeOfF, false, false)); } } catch (CircularReferenceException e) { throw e.createDetailedException(f); } } public void visitPrimitive(Object obj) { JsonElement json = obj == null ? JsonNull.INSTANCE : new JsonPrimitive( obj); assignToRoot(json); } private void addAsChildOfObject(FieldAttributes f, ObjectTypePair fieldValuePair) { JsonElement childElement = getJsonElementForChild(fieldValuePair); addChildAsElement(f, childElement); } private void addChildAsElement(FieldAttributes f, JsonElement childElement) { root.getAsJsonObject().add(fieldNamingPolicy.translateName(f), childElement); } private void addAsArrayElement(ObjectTypePair elementTypePair) { if (elementTypePair.getObject() == null) { root.getAsJsonArray().add(JsonNull.INSTANCE); } else { JsonElement childElement = getJsonElementForChild(elementTypePair); root.getAsJsonArray().add(childElement); } } private JsonElement getJsonElementForChild(ObjectTypePair fieldValueTypePair) { JsonSerializationVisitor childVisitor = new JsonSerializationVisitor( objectNavigator, fieldNamingPolicy, serializeNulls, serializers, context, ancestors); objectNavigator.accept(fieldValueTypePair, childVisitor); return childVisitor.getJsonElement(); } public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { try { Object obj = objTypePair.getObject(); if (obj == null) { if (serializeNulls) { assignToRoot(JsonNull.INSTANCE); } return true; } JsonElement element = findAndInvokeCustomSerializer(objTypePair); if (element != null) { assignToRoot(element); return true; } return false; } catch (CircularReferenceException e) { throw e.createDetailedException(null); } } /** * objTypePair.getObject() must not be null */ @SuppressWarnings("unchecked") private JsonElement findAndInvokeCustomSerializer(ObjectTypePair objTypePair) { Pair<JsonSerializer<?>, ObjectTypePair> pair = objTypePair .getMatchingHandler(serializers); if (pair == null) { return null; } JsonSerializer serializer = pair.first; objTypePair = pair.second; start(objTypePair); try { JsonElement element = serializer.serialize(objTypePair.getObject(), objTypePair.getType(), context); return element == null ? JsonNull.INSTANCE : element; } finally { end(objTypePair); } } public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) { try { $Gson$Preconditions.checkState(root.isJsonObject()); Object obj = f.get(parent); if (obj == null) { if (serializeNulls) { addChildAsElement(f, JsonNull.INSTANCE); } return true; } ObjectTypePair objTypePair = new ObjectTypePair(obj, declaredTypeOfField, false, false); JsonElement child = findAndInvokeCustomSerializer(objTypePair); if (child != null) { addChildAsElement(f, child); return true; } return false; } catch (IllegalAccessException e) { throw new RuntimeException(); } catch (CircularReferenceException e) { throw e.createDetailedException(f); } } private void assignToRoot(JsonElement newRoot) { root = $Gson$Preconditions.checkNotNull(newRoot); } private boolean isFieldNull(FieldAttributes f, Object obj) { return getFieldValue(f, obj) == null; } private Object getFieldValue(FieldAttributes f, Object obj) { try { return f.get(obj); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public JsonElement getJsonElement() { return root; } }