From ff742248158fe6a13f6d13aa027c2d5b79fbe807 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Mon, 5 Oct 2009 18:17:52 +0000 Subject: [PATCH] Revised equals and hashcode of ObjectTypePair to ensure reference equality of object instead of value equality. Improved JavaDocs for various 1.4 API methods. --- .../com/google/gson/FieldNamingPolicy.java | 5 +- gson/src/main/java/com/google/gson/Gson.java | 20 +++---- .../google/gson/JsonSerializationVisitor.java | 6 +- .../com/google/gson/JsonStreamParser.java | 20 +++++-- .../java/com/google/gson/MemoryRefStack.java | 3 +- .../java/com/google/gson/ObjectNavigator.java | 2 +- .../java/com/google/gson/ObjectTypePair.java | 60 ++++++++++++++----- .../com/google/gson/annotations/Expose.java | 12 ++-- .../com/google/gson/MemoryRefStackTest.java | 11 ++-- 9 files changed, 90 insertions(+), 49 deletions(-) diff --git a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java index 5b20df67..e1826243 100644 --- a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java +++ b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java @@ -64,7 +64,10 @@ public enum FieldNamingPolicy { *
  • aStringField ---> a-string-field
  • *
  • aURL ---> a-u-r-l
  • * - * + * Using dashes in JavaScript is not recommended since dash is also used for a minus sign in + * expressions. This requires that a field named with dashes is always accessed as a quoted + * property like {@code myobject['my-field']}. Accessing it as an object field + * {@code myobject.my-field} will result in an unintended javascript expression. * @since 1.4 */ LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-")); diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 06533d98..9806f34e 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -187,13 +187,12 @@ public final class Gson { /** * This method serializes the specified object into its equivalent representation as a tree of - * {JsonElement}s. This method should be used when the specified object is not a generic type. - * This method uses {@link Class#getClass()} to get the type for the specified object, but the - * {@code getClass()} loses the generic type information because of the Type Erasure feature + * {@link JsonElement}s. This method should be used when the specified object is not a generic + * type. This method uses {@link Class#getClass()} to get the type for the specified object, but + * the {@code getClass()} loses the generic type information because of the Type Erasure feature * of Java. Note that this method works fine if the any of the object fields are of generic type, * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJson(Object, Type)} instead. If you want to write out the object to a - * {@link Writer}, use {@link #toJson(Object, Appendable)} instead. + * {@link #toJsonTree(Object, Type)} instead. * * @param src the object for which Json representation is to be created setting for Gson * @return Json representation of {@code src}. @@ -209,9 +208,8 @@ public final class Gson { /** * This method serializes the specified object, including those of generic types, into its * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the - * specified object is a generic type. For non-generic objects, use {@link #toJson(Object)} - * instead. If you want to write out the object to a {@link Appendable}, - * use {@link #toJson(Object, Type, Appendable)} instead. + * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)} + * instead. * * @param src the object for which JSON representation is to be created * @param typeOfSrc The specific genericized type of src. You can obtain @@ -321,7 +319,7 @@ public final class Gson { /** * Converts a tree of {@link JsonElement}s into its equivalent JSON representation. * - * @param jsonElement root of the tree of {@link JsonElement}s + * @param jsonElement root of a tree of {@link JsonElement}s * @return JSON String representation of the tree * @since 1.4 */ @@ -332,9 +330,9 @@ public final class Gson { } /** - * Writes out the equivalent JSON for the tree of {@link JsonElement}s. + * Writes out the equivalent JSON for a tree of {@link JsonElement}s. * - * @param jsonElement root of the tree of {@link JsonElement}s + * @param jsonElement root of a tree of {@link JsonElement}s * @param writer Writer to which the Json representation needs to be written * @since 1.4 */ diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index fbd54df0..b179492c 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -150,7 +150,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { } private void addAsArrayElement(ObjectTypePair elementTypePair) { - if (elementTypePair.getObj() == null) { + if (elementTypePair.getObject() == null) { root.getAsJsonArray().add(JsonNull.createJsonNull()); } else { JsonElement childElement = getJsonElementForChild(elementTypePair); @@ -169,7 +169,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { @SuppressWarnings("unchecked") public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { try { - Object obj = objTypePair.getObj(); + Object obj = objTypePair.getObject(); Type objType = objTypePair.getType(); JsonSerializer serializer = serializers.getHandlerFor(objType); if (serializer == null && obj != null) { @@ -194,7 +194,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { private JsonElement invokeCustomHandler(ObjectTypePair objTypePair, JsonSerializer serializer) { start(objTypePair); try { - return serializer.serialize(objTypePair.getObj(), objTypePair.getType(), context); + return serializer.serialize(objTypePair.getObject(), objTypePair.getType(), context); } finally { end(objTypePair); } diff --git a/gson/src/main/java/com/google/gson/JsonStreamParser.java b/gson/src/main/java/com/google/gson/JsonStreamParser.java index 3bb27501..613e693c 100644 --- a/gson/src/main/java/com/google/gson/JsonStreamParser.java +++ b/gson/src/main/java/com/google/gson/JsonStreamParser.java @@ -25,14 +25,12 @@ import java.util.NoSuchElementException; * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader * asynchronously. * - *

    This class is thread-compatible. For some more literature on these definitions, refer to - * Effective Java. - * - *

    To properly use this class across multiple thread, you will need to add some external - * synchronization to your classes/thread to get this to work properly. For example: + *

    This class is conditionally thread-safe (see Item 70, Effective Java second edition). To + * properly use this class across multiple threads, you will need to add some external + * synchronization. For example: * *

    - * JsonStreamParser parser = new JsonStreamParser("blah blah blah");
    + * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
      * JsonElement element;
      * synchronized (someCommonObject) {
      *   if (parser.hasNext()) {
    @@ -104,6 +102,11 @@ public final class JsonStreamParser implements Iterator {
         }
       }
     
    +  /**
    +   * Returns true if a {@link JsonElement} is available on the input for consumption
    +   * @return true if a {@link JsonElement} is available on the input, false otherwise
    +   * @since 1.4
    +   */
       public boolean hasNext() {
         synchronized (lock) {
           try {
    @@ -116,6 +119,11 @@ public final class JsonStreamParser implements Iterator {
         }
       }
     
    +  /**
    +   * This optional {@link Iterator} method is not relevant for stream parsing and hence is not
    +   * implemented.
    +   * @since 1.4
    +   */
       public void remove() {
         throw new UnsupportedOperationException();
       }
    diff --git a/gson/src/main/java/com/google/gson/MemoryRefStack.java b/gson/src/main/java/com/google/gson/MemoryRefStack.java
    index 7db962fc..c4103aa6 100644
    --- a/gson/src/main/java/com/google/gson/MemoryRefStack.java
    +++ b/gson/src/main/java/com/google/gson/MemoryRefStack.java
    @@ -77,7 +77,8 @@ final class MemoryRefStack {
         }
     
         for (ObjectTypePair stackObject : stack) {
    -      if (stackObject.getObj() == obj.getObj() && stackObject.getType().equals(obj.getType()) ) {
    +      if (stackObject.getObject() == obj.getObject()
    +          && 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 7a56756b..118442f6 100644
    --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java
    +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java
    @@ -93,7 +93,7 @@ final class ObjectNavigator {
       public void accept(Visitor visitor) {
         boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(objTypePair);
         if (!visitedWithCustomHandler) {
    -      Object obj = objTypePair.getObj();
    +      Object obj = objTypePair.getObject();
           Object objectToVisit = (obj == null) ? visitor.getTarget() : obj;
           if (objectToVisit == null) {
             return;
    diff --git a/gson/src/main/java/com/google/gson/ObjectTypePair.java b/gson/src/main/java/com/google/gson/ObjectTypePair.java
    index 79b72182..ea4dc35f 100644
    --- a/gson/src/main/java/com/google/gson/ObjectTypePair.java
    +++ b/gson/src/main/java/com/google/gson/ObjectTypePair.java
    @@ -1,47 +1,79 @@
    +/*
    + * Copyright (C) 2009 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.Type;
     
    - final class ObjectTypePair {
    +/**
    + * A holder class for an object and its type
    + *
    + * @author Inderjeet Singh
    + */
    +final class ObjectTypePair {
    +  private static final int PRIME = 31;
    +
       private final Object obj;
       private final Type type;
    +
       public ObjectTypePair(Object obj, Type type) {
         this.obj = obj;
         this.type = type;
       }
    -  public Object getObj() {
    +
    +  public Object getObject() {
         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;
    +    // Not using type.hashCode() since I am not sure if the subclasses of type reimplement
    +    // hashCode() to be equal for equal types
    +    return ((obj == null) ? PRIME : obj.hashCode());
       }
    +
       @Override
       public boolean equals(Object obj) {
    -    if (this == obj)
    +    if (this == obj) {
           return true;
    -    if (obj == null)
    +    }
    +    if (obj == null) {
           return false;
    -    if (getClass() != obj.getClass())
    +    }
    +    if (getClass() != obj.getClass()) {
           return false;
    +    }
         ObjectTypePair other = (ObjectTypePair) obj;
         if (this.obj == null) {
    -      if (other.obj != null)
    +      if (other.obj != null) {
             return false;
    -    } else if (!this.obj.equals(other.obj))
    +      }
    +    } else if (this.obj != other.obj) { // Checking for reference equality
           return false;
    +    }
         if (type == null) {
    -      if (other.type != null)
    +      if (other.type != null) {
             return false;
    -    } else if (!type.equals(other.type))
    +      }
    +    } else if (!type.equals(other.type)) {
           return false;
    +    }
         return true;
       }
     }
    diff --git a/gson/src/main/java/com/google/gson/annotations/Expose.java b/gson/src/main/java/com/google/gson/annotations/Expose.java
    index 706f76e0..1b9c70df 100644
    --- a/gson/src/main/java/com/google/gson/annotations/Expose.java
    +++ b/gson/src/main/java/com/google/gson/annotations/Expose.java
    @@ -62,17 +62,17 @@ import java.lang.annotation.Target;
     public @interface Expose {
       
       /**
    -   * If true, the field marked with this annotation is written out in the JSON while serializing.
    -   * If false, the field marked with this annotation is skipped from the serialized output. 
    -   * Defaults to true.
    +   * If {@code true}, the field marked with this annotation is written out in the JSON while
    +   * serializing. If {@code false}, the field marked with this annotation is skipped from the
    +   * serialized output. Defaults to {@code true}.
        * @since 1.4
        */
       public boolean serialize() default true;
     
       /**
    -   * If true, the field marked with this annotation is deserialized from the JSON.
    -   * If false, the field marked with this annotation is skipped during deserialization. 
    -   * Defaults to true.
    +   * If {@code true}, the field marked with this annotation is deserialized from the JSON.
    +   * If {@code false}, the field marked with this annotation is skipped during deserialization. 
    +   * Defaults to {@code true}.
        * @since 1.4
        */
       public boolean deserialize() default true;
    diff --git a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java b/gson/src/test/java/com/google/gson/MemoryRefStackTest.java
    index 908bfebb..d146038b 100644
    --- a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java
    +++ b/gson/src/test/java/com/google/gson/MemoryRefStackTest.java
    @@ -61,13 +61,12 @@ public class MemoryRefStackTest extends TestCase {
       }
     
       public void testContains() throws Exception {
    -    ObjectTypePair objA = new ObjectTypePair(new MockObject(), MockObject.class);
    -    ObjectTypePair objB = new ObjectTypePair(new MockObject(), MockObject.class);
    +    MockObject objA = new MockObject();
    +    MockObject objB = new MockObject();
         assertEquals(objA, objB);
    -
    -    stack.push(objA);
    -    assertFalse(stack.contains(objB));
    -    assertTrue(stack.contains(objA));
    +    stack.push(new ObjectTypePair(objA, MockObject.class));
    +    assertTrue(stack.contains(new ObjectTypePair(objA, MockObject.class)));
    +    assertFalse(stack.contains(new ObjectTypePair(objB, MockObject.class)));
       }
     
       private static class MockObject {