diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java index c3f56a3e..8a5c9ef5 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java @@ -60,7 +60,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont JsonDeserializationContext context) throws JsonParseException { JsonArrayDeserializationVisitor visitor = new JsonArrayDeserializationVisitor( jsonArray, arrayType, navigatorFactory, objectConstructor, deserializers, context); - ObjectNavigator on = navigatorFactory.create(null, arrayType); + ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, arrayType)); on.accept(visitor); return visitor.getTarget(); } @@ -69,7 +69,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont JsonDeserializationContext context) throws JsonParseException { JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context); - ObjectNavigator on = navigatorFactory.create(null, typeOfT); + ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, typeOfT)); on.accept(visitor); return visitor.getTarget(); } @@ -79,7 +79,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont JsonDeserializationContext context) throws JsonParseException { JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( json, typeOfT, navigatorFactory, objectConstructor, deserializers, context); - ObjectNavigator on = navigatorFactory.create(json.getAsObject(), typeOfT); + ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(json.getAsObject(), typeOfT)); on.accept(visitor); Object target = visitor.getTarget(); return (T) target; diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index ab0a10c2..23583c39 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -58,14 +58,15 @@ abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor protected abstract T constructTarget(); - public void start(Object node) { + public void start(ObjectTypePair node) { } - public void end(Object node) { + public void end(ObjectTypePair node) { } @SuppressWarnings("unchecked") - public final boolean visitUsingCustomHandler(Object obj, Type objType) { + public final boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { + Type objType = objTypePair.getType(); JsonDeserializer deserializer = deserializers.getHandlerFor(objType); if (deserializer != null) { target = (T) deserializer.deserialize(json, objType, context); @@ -89,7 +90,7 @@ abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor } private Object visitChild(Type type, JsonDeserializationVisitor childVisitor) { - ObjectNavigator on = factory.create(null, type); + ObjectNavigator on = factory.create(new ObjectTypePair(null, type)); on.accept(childVisitor); // the underlying object may have changed during the construction phase // This happens primarily because of custom deserializers diff --git a/gson/src/main/java/com/google/gson/JsonSerializationContextDefault.java b/gson/src/main/java/com/google/gson/JsonSerializationContextDefault.java index 01f4252f..6e906f53 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationContextDefault.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationContextDefault.java @@ -28,14 +28,14 @@ final class JsonSerializationContextDefault implements JsonSerializationContext private final ObjectNavigatorFactory factory; private final ParameterizedTypeHandlerMap> serializers; private final boolean serializeNulls; - private final MemoryRefStack ancestors; + private final MemoryRefStack ancestors; JsonSerializationContextDefault(ObjectNavigatorFactory factory, boolean serializeNulls, ParameterizedTypeHandlerMap> serializers) { this.factory = factory; this.serializeNulls = serializeNulls; this.serializers = serializers; - this.ancestors = new MemoryRefStack(); + this.ancestors = new MemoryRefStack(); } public JsonElement serialize(Object src) { @@ -43,7 +43,7 @@ final class JsonSerializationContextDefault implements JsonSerializationContext } public JsonElement serialize(Object src, Type typeOfSrc) { - ObjectNavigator on = factory.create(src, typeOfSrc); + ObjectNavigator on = factory.create(new ObjectTypePair(src, typeOfSrc)); JsonSerializationVisitor visitor = new JsonSerializationVisitor(factory, serializeNulls, serializers, this, ancestors); on.accept(visitor); diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index ed26a383..fbd54df0 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -32,12 +32,12 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { private final ParameterizedTypeHandlerMap> serializers; private final boolean serializeNulls; private final JsonSerializationContext context; - private final MemoryRefStack ancestors; + private final MemoryRefStack ancestors; private JsonElement root; JsonSerializationVisitor(ObjectNavigatorFactory factory, boolean serializeNulls, ParameterizedTypeHandlerMap> serializers, - JsonSerializationContext context, MemoryRefStack ancestors) { + JsonSerializationContext context, MemoryRefStack ancestors) { this.factory = factory; this.serializeNulls = serializeNulls; this.serializers = serializers; @@ -49,7 +49,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { return null; } - public void start(Object node) { + public void start(ObjectTypePair node) { if (node == null) { return; } @@ -59,7 +59,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { ancestors.push(node); } - public void end(Object node) { + public void end(ObjectTypePair node) { if (node != null) { ancestors.pop(); } @@ -80,7 +80,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { if (child != null) { childType = getActualTypeIfMoreSpecific(childType, child.getClass()); } - addAsArrayElement(childType, child); + addAsArrayElement(new ObjectTypePair(child, childType)); } } @@ -92,7 +92,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { } } else { Object array = getFieldValue(f, obj); - addAsChildOfObject(f, typeOfF, array); + addAsChildOfObject(f, new ObjectTypePair(array, typeOfF)); } } catch (CircularReferenceException e) { throw e.createDetailedException(f); @@ -110,7 +110,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { if (fieldValue != null) { typeOfF = getActualTypeIfMoreSpecific(typeOfF, fieldValue.getClass()); } - addAsChildOfObject(f, typeOfF, fieldValue); + addAsChildOfObject(f, new ObjectTypePair(fieldValue, typeOfF)); } } catch (CircularReferenceException e) { throw e.createDetailedException(f); @@ -139,8 +139,8 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { assignToRoot(json); } - private void addAsChildOfObject(Field f, Type fieldType, Object fieldValue) { - JsonElement childElement = getJsonElementForChild(fieldType, fieldValue); + private void addAsChildOfObject(Field f, ObjectTypePair fieldValuePair) { + JsonElement childElement = getJsonElementForChild(fieldValuePair); addChildAsElement(f, childElement); } @@ -149,17 +149,17 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { root.getAsJsonObject().add(namingPolicy.translateName(f), childElement); } - private void addAsArrayElement(Type elementType, Object elementValue) { - if (elementValue == null) { + private void addAsArrayElement(ObjectTypePair elementTypePair) { + if (elementTypePair.getObj() == null) { root.getAsJsonArray().add(JsonNull.createJsonNull()); } else { - JsonElement childElement = getJsonElementForChild(elementType, elementValue); + JsonElement childElement = getJsonElementForChild(elementTypePair); root.getAsJsonArray().add(childElement); } } - private JsonElement getJsonElementForChild(Type fieldType, Object fieldValue) { - ObjectNavigator on = factory.create(fieldValue, fieldType); + private JsonElement getJsonElementForChild(ObjectTypePair fieldValueTypePair) { + ObjectNavigator on = factory.create(fieldValueTypePair); JsonSerializationVisitor childVisitor = new JsonSerializationVisitor(factory, serializeNulls, serializers, context, ancestors); on.accept(childVisitor); @@ -167,8 +167,10 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { } @SuppressWarnings("unchecked") - public boolean visitUsingCustomHandler(Object obj, Type objType) { + public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { try { + Object obj = objTypePair.getObj(); + Type objType = objTypePair.getType(); JsonSerializer serializer = serializers.getHandlerFor(objType); if (serializer == null && obj != null) { serializer = serializers.getHandlerFor(obj.getClass()); @@ -178,7 +180,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { if (obj == null) { assignToRoot(JsonNull.createJsonNull()); } else { - assignToRoot(invokeCustomHandler(obj, objType, serializer)); + assignToRoot(invokeCustomHandler(objTypePair, serializer)); } return true; } @@ -189,12 +191,12 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { } @SuppressWarnings("unchecked") - private JsonElement invokeCustomHandler(Object obj, Type objType, JsonSerializer serializer) { - start(obj); + private JsonElement invokeCustomHandler(ObjectTypePair objTypePair, JsonSerializer serializer) { + start(objTypePair); try { - return serializer.serialize(obj, objType, context); + return serializer.serialize(objTypePair.getObj(), objTypePair.getType(), context); } finally { - end(obj); + end(objTypePair); } } @@ -211,7 +213,8 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { } JsonSerializer serializer = serializers.getHandlerFor(actualTypeOfField); if (serializer != null) { - JsonElement child = invokeCustomHandler(obj, actualTypeOfField, serializer); + ObjectTypePair objTypePair = new ObjectTypePair(obj, actualTypeOfField); + JsonElement child = invokeCustomHandler(objTypePair, serializer); addChildAsElement(f, child); return true; } diff --git a/gson/src/main/java/com/google/gson/MemoryRefStack.java b/gson/src/main/java/com/google/gson/MemoryRefStack.java index 2c41ff1e..7db962fc 100644 --- a/gson/src/main/java/com/google/gson/MemoryRefStack.java +++ b/gson/src/main/java/com/google/gson/MemoryRefStack.java @@ -25,8 +25,8 @@ import java.util.Stack; * * @author Joel Leitch */ -final class MemoryRefStack { - private final Stack stack = new Stack(); +final class MemoryRefStack { + private final Stack stack = new Stack(); /** * Adds a new element to the top of the stack. @@ -34,7 +34,7 @@ final class MemoryRefStack { * @param obj the object to add to the stack * @return the object that was added */ - public T push(T obj) { + public ObjectTypePair push(ObjectTypePair obj) { Preconditions.checkNotNull(obj); return stack.push(obj); @@ -46,7 +46,7 @@ final class MemoryRefStack { * @return the element being removed from the stack * @throws java.util.EmptyStackException thrown if the stack is empty */ - public T pop() { + public ObjectTypePair pop() { return stack.pop(); } @@ -60,7 +60,7 @@ final class MemoryRefStack { * @return the item from the top of the stack * @throws java.util.EmptyStackException thrown if the stack is empty */ - public T peek() { + public ObjectTypePair peek() { return stack.peek(); } @@ -71,13 +71,13 @@ final class MemoryRefStack { * @param obj the object to search for in the stack * @return true if this object is already in the stack otherwise false */ - public boolean contains(T obj) { + public boolean contains(ObjectTypePair obj) { if (obj == null) { return false; } - for (T stackObject : stack) { - if (obj == stackObject) { + for (ObjectTypePair stackObject : stack) { + if (stackObject.getObj() == obj.getObj() && stackObject.getType().equals(obj.getType()) ) { return true; } } diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index dd82c2cb..7a56756b 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -29,8 +29,8 @@ import java.lang.reflect.Type; final class ObjectNavigator { public interface Visitor { - public void start(Object node); - public void end(Object node); + public void start(ObjectTypePair node); + public void end(ObjectTypePair node); /** * This is called before the object navigator starts visiting the current object @@ -56,7 +56,7 @@ final class ObjectNavigator { * This is called to visit an object using a custom handler * @return true if a custom handler exists, false otherwise */ - public boolean visitUsingCustomHandler(Object obj, Type objType); + public boolean visitUsingCustomHandler(ObjectTypePair objTypePair); /** * This is called to visit a field of the current object using a custom handler @@ -72,20 +72,17 @@ final class ObjectNavigator { } private final ExclusionStrategy exclusionStrategy; - private final Object obj; - private final Type objType; + private final ObjectTypePair objTypePair; /** - * @param obj The object being navigated - * @param objType The (fully genericized) type of the object being navigated + * @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. */ - ObjectNavigator(Object obj, Type objType, ExclusionStrategy exclusionStrategy) { + ObjectNavigator(ObjectTypePair objTypePair, ExclusionStrategy exclusionStrategy) { Preconditions.checkNotNull(exclusionStrategy); - this.obj = obj; - this.objType = objType; + this.objTypePair = objTypePair; this.exclusionStrategy = exclusionStrategy; } @@ -94,20 +91,21 @@ final class ObjectNavigator { * If a field is null, it does not get visited. */ public void accept(Visitor visitor) { - boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType); + boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(objTypePair); if (!visitedWithCustomHandler) { + Object obj = objTypePair.getObj(); Object objectToVisit = (obj == null) ? visitor.getTarget() : obj; if (objectToVisit == null) { return; } - TypeInfo objTypeInfo = new TypeInfo(objType); + TypeInfo objTypeInfo = new TypeInfo(objTypePair.getType()); if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) { return; } - visitor.start(obj); + visitor.start(objTypePair); try { if (objTypeInfo.isArray()) { - visitor.visitArray(objectToVisit, objType); + visitor.visitArray(objectToVisit, objTypePair.getType()); } else if (objTypeInfo.getActualType() == Object.class && isPrimitiveOrString(objectToVisit)) { // TODO(Joel): this is only used for deserialization of "primitves" @@ -128,7 +126,7 @@ final class ObjectNavigator { } } } finally { - visitor.end(obj); + visitor.end(objTypePair); } } } @@ -146,7 +144,7 @@ final class ObjectNavigator { if (exclusionStrategy.shouldSkipField(f)) { continue; // skip } else { - TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objType); + TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.getType()); Type actualTypeOfField = fieldTypeInfo.getActualType(); boolean visitedWithCustomHandler = visitor.visitFieldUsingCustomHandler(f, actualTypeOfField, obj); diff --git a/gson/src/main/java/com/google/gson/ObjectNavigatorFactory.java b/gson/src/main/java/com/google/gson/ObjectNavigatorFactory.java index c803193a..69fef7ce 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigatorFactory.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigatorFactory.java @@ -16,8 +16,6 @@ package com.google.gson; -import java.lang.reflect.Type; - /** * A factory class used to simplify {@link ObjectNavigator} creation. * This object holds on to a reference of the {@link ExclusionStrategy} @@ -48,15 +46,13 @@ final class ObjectNavigatorFactory { * Creates a new {@link ObjectNavigator} for this {@code srcObject}, * {@code type} pair. * - * @param srcObject object to navigate - * @param type the "actual" type of this {@code srcObject}. NOTE: this can - * be a {@link java.lang.reflect.ParameterizedType} rather than a {@link Class}. + * @param objTypePair The object,type (fully genericized) being navigated * @return a new instance of a {@link ObjectNavigator} ready to navigate the * {@code srcObject} while taking into consideration the * {@code type}. */ - public ObjectNavigator create(Object srcObject, Type type) { - return new ObjectNavigator(srcObject, type, strategy); + public ObjectNavigator create(ObjectTypePair objTypePair) { + return new ObjectNavigator(objTypePair, strategy); } FieldNamingStrategy getFieldNamingPolicy() { diff --git a/gson/src/main/java/com/google/gson/ObjectTypePair.java b/gson/src/main/java/com/google/gson/ObjectTypePair.java new file mode 100644 index 00000000..79b72182 --- /dev/null +++ b/gson/src/main/java/com/google/gson/ObjectTypePair.java @@ -0,0 +1,47 @@ +package com.google.gson; + +import java.lang.reflect.Type; + + final class ObjectTypePair { + private final Object obj; + private final Type type; + public ObjectTypePair(Object obj, Type type) { + this.obj = obj; + this.type = type; + } + public Object getObj() { + return obj; + } + public Type getType() { + return type; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((obj == null) ? 0 : obj.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ObjectTypePair other = (ObjectTypePair) obj; + if (this.obj == null) { + if (other.obj != null) + return false; + } else if (!this.obj.equals(other.obj)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } +} diff --git a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java b/gson/src/test/java/com/google/gson/MemoryRefStackTest.java index a05d2b8c..908bfebb 100644 --- a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java +++ b/gson/src/test/java/com/google/gson/MemoryRefStackTest.java @@ -28,12 +28,12 @@ import java.util.EmptyStackException; * @author Joel Leitch */ public class MemoryRefStackTest extends TestCase { - private MemoryRefStack stack; + private MemoryRefStack stack; @Override protected void setUp() throws Exception { super.setUp(); - stack = new MemoryRefStack(); + stack = new MemoryRefStack(); } public void testPeekEmptyStack() throws Exception { @@ -43,7 +43,7 @@ public class MemoryRefStackTest extends TestCase { } public void testPushPeekAndPop() throws Exception { - MockObject obj = new MockObject(); + ObjectTypePair obj = new ObjectTypePair(this, getClass()); assertEquals(obj, stack.push(obj)); assertEquals(obj, stack.peek()); @@ -51,7 +51,7 @@ public class MemoryRefStackTest extends TestCase { } public void testPopTooMany() throws Exception { - MockObject obj = new MockObject(); + ObjectTypePair obj = new ObjectTypePair(this, getClass()); stack.push(obj); assertEquals(obj, stack.pop()); @@ -61,8 +61,8 @@ public class MemoryRefStackTest extends TestCase { } public void testContains() throws Exception { - MockObject objA = new MockObject(); - MockObject objB = new MockObject(); + ObjectTypePair objA = new ObjectTypePair(new MockObject(), MockObject.class); + ObjectTypePair objB = new ObjectTypePair(new MockObject(), MockObject.class); assertEquals(objA, objB); stack.push(objA); diff --git a/gson/src/test/java/com/google/gson/functional/InterfaceTest.java b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java index f8caf773..6851f1e9 100644 --- a/gson/src/test/java/com/google/gson/functional/InterfaceTest.java +++ b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java @@ -32,6 +32,7 @@ public class InterfaceTest extends TestCase { private Gson gson; private TestObject obj; + @Override protected void setUp() throws Exception { super.setUp(); gson = new Gson();