diff --git a/core/pom.xml b/core/pom.xml index 4cb265ec21..22a5182131 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -106,7 +106,7 @@ com.google.code.gson gson - 1.6 + 1.7.1 com.google.guava diff --git a/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java b/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java deleted file mode 100644 index b7fd52655a..0000000000 --- a/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== - * 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; - -/** - * handles the limitation in gson 1.6 where - * {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)} is package private. - * - * @see gson issue 271 - * @author Adrian Cole - */ -public class JcloudsGsonPackageAccessor { - public static GsonBuilder registerTypeHierarchyAdapter(GsonBuilder builder, Class baseType, Object typeAdapter) { - return builder.registerTypeHierarchyAdapter(baseType, typeAdapter); - } -} diff --git a/core/src/main/java/com/google/gson/JsonLiteral.java b/core/src/main/java/com/google/gson/JsonLiteral.java index bef99b7ea7..e3cb87bc02 100644 --- a/core/src/main/java/com/google/gson/JsonLiteral.java +++ b/core/src/main/java/com/google/gson/JsonLiteral.java @@ -23,11 +23,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; /** - * The gson project use package to control access to their objects. However, - * this prevents us from doing valid work, like controling the json emitted on a - * per-object basis. This is here to afford us to do this. + * The gson project use package to control access to their objects. However, this prevents us from + * doing valid work, like controling the json emitted on a per-object basis. This is here to afford + * us to do this. * * @author Adrian Cole + * @see */ public final class JsonLiteral extends JsonElement { private final CharSequence literal; diff --git a/core/src/main/java/com/google/gson/MapTypeAdapter.java b/core/src/main/java/com/google/gson/MapTypeAdapter.java deleted file mode 100644 index cfab58edc9..0000000000 --- a/core/src/main/java/com/google/gson/MapTypeAdapter.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.ParameterizedType; -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import org.jclouds.json.internal.ParseObjectFromElement; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class MapTypeAdapter implements JsonSerializer, JsonDeserializer, InstanceCreator { - - public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) { - JsonObject map = new JsonObject(); - Type childGenericType = null; - if (typeOfSrc instanceof ParameterizedType) { - childGenericType = new TypeInfoMap(typeOfSrc).getValueType(); - } - - for (Map.Entry entry : (Set) src.entrySet()) { - Object value = entry.getValue(); - - JsonElement valueElement; - if (value == null) { - valueElement = JsonNull.createJsonNull(); - } else { - Type childType = (childGenericType == null) ? value.getClass() : childGenericType; - valueElement = context.serialize(value, childType); - } - map.add(String.valueOf(entry.getKey()), valueElement); - } - return map; - } - - public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - // Use ObjectConstructor to create instance instead of hard-coding a specific type. - // This handles cases where users are using their own subclass of Map. - Map map = constructMapType(typeOfT, context); - TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT); - for (Map.Entry entry : json.getAsJsonObject().entrySet()) { - Object key = context.deserialize(new JsonPrimitive(entry.getKey()), mapTypeInfo.getKeyType()); - // START JCLOUDS PATCH - Object value = null; - if (mapTypeInfo.getValueType() == Object.class) { - value = ParseObjectFromElement.SINGLETON.apply(entry.getValue()); - } - if (value == null) { - value = context.deserialize(entry.getValue(), mapTypeInfo.getValueType()); - } - // END JCLOUDS PATCH - map.put(key, value); - } - return map; - } - - private Map constructMapType(Type mapType, JsonDeserializationContext context) { - JsonDeserializationContextDefault contextImpl = (JsonDeserializationContextDefault) context; - ObjectConstructor objectConstructor = contextImpl.getObjectConstructor(); - return (Map) objectConstructor.construct(mapType); - } - - public Map createInstance(Type type) { - return new LinkedHashMap(); - } - - @Override - public String toString() { - return MapTypeAdapter.class.getSimpleName(); - } -} \ No newline at end of file diff --git a/core/src/main/java/com/google/gson/ObjectMapTypeAdapter.java b/core/src/main/java/com/google/gson/ObjectMapTypeAdapter.java new file mode 100644 index 0000000000..ed91ae86ee --- /dev/null +++ b/core/src/main/java/com/google/gson/ObjectMapTypeAdapter.java @@ -0,0 +1,90 @@ +/* + * 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.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; + +import org.jclouds.json.internal.ParseObjectFromElement; + +import com.google.gson.internal.$Gson$Types; + +/** + * Default serialization and deserialization of a map type. This implementation really only works + * well with simple primitive types as the map key. If the key is not a simple primitive then the + * object is {@code toString}ed and that value is used as its key. + *

+ * Patched depending on this + * @author Joel Leitch + */ +@SuppressWarnings("unchecked") +public final class ObjectMapTypeAdapter extends BaseMapTypeAdapter { + + public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject map = new JsonObject(); + Type childGenericType = null; + if (typeOfSrc instanceof ParameterizedType) { + Class rawTypeOfSrc = $Gson$Types.getRawType(typeOfSrc); + childGenericType = $Gson$Types.getMapKeyAndValueTypes(typeOfSrc, rawTypeOfSrc)[1]; + } + + for (Map.Entry entry : (Set) src.entrySet()) { + Object value = entry.getValue(); + + JsonElement valueElement; + if (value == null) { + valueElement = JsonNull.createJsonNull(); + } else { + Type childType = (childGenericType == null) + ? value.getClass() : childGenericType; + valueElement = serialize(context, value, childType); + } + map.add(String.valueOf(entry.getKey()), valueElement); + } + return map; + } + + public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + // Use ObjectConstructor to create instance instead of hard-coding a specific type. + // This handles cases where users are using their own subclass of Map. + Map map = constructMapType(typeOfT, context); + Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(typeOfT, $Gson$Types.getRawType(typeOfT)); + for (Map.Entry entry : json.getAsJsonObject().entrySet()) { + Object key = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueTypes[0]); + // START JCLOUDS PATCH + // http://code.google.com/p/google-gson/issues/detail?id=325 + Object value = null; + if (keyAndValueTypes[1] == Object.class) { + value = ParseObjectFromElement.SINGLETON.apply(entry.getValue()); + } + if (value == null) { + value = context.deserialize(entry.getValue(), keyAndValueTypes[1]); + } + // END JCLOUDS PATCH + map.put(key, value); + } + return map; + } + + @Override + public String toString() { + return MapTypeAdapter.class.getSimpleName(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/google/gson/Streams.java b/core/src/main/java/com/google/gson/Streams.java index 24e2167553..c6b05f89bf 100644 --- a/core/src/main/java/com/google/gson/Streams.java +++ b/core/src/main/java/com/google/gson/Streams.java @@ -1,8 +1,6 @@ -/** +/* + * Copyright (C) 2010 Google Inc. * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== * 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 @@ -14,8 +12,8 @@ * 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 com.google.gson.stream.JsonReader; @@ -104,7 +102,8 @@ final class Streams { if (serializeNulls) { writer.nullValue(); } -//BEGIN JCLOUDS PATCH + //BEGIN JCLOUDS PATCH + // * @see } else if (element instanceof JsonLiteral ) { writer.value(JsonLiteral.class.cast(element)); //END JCLOUDS PATCH diff --git a/core/src/main/java/com/google/gson/stream/JsonWriter.java b/core/src/main/java/com/google/gson/stream/JsonWriter.java index bd01290581..bcf12bac95 100644 --- a/core/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/core/src/main/java/com/google/gson/stream/JsonWriter.java @@ -1,21 +1,19 @@ -/** +/* + * Copyright (C) 2010 Google Inc. * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== * 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 + * 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.stream; import java.io.Closeable; @@ -344,7 +342,7 @@ public final class JsonWriter implements Closeable { return this; } //BEGIN JCLOUDS PATCH - +// * @see /** * Writes {@code value} literally * @@ -356,7 +354,6 @@ public final class JsonWriter implements Closeable { return this; } //END JCLOUDS PATCH - /** * Encodes {@code value}. * diff --git a/core/src/main/java/org/jclouds/domain/JsonBall.java b/core/src/main/java/org/jclouds/domain/JsonBall.java index 227d5797d3..b5f524ad48 100644 --- a/core/src/main/java/org/jclouds/domain/JsonBall.java +++ b/core/src/main/java/org/jclouds/domain/JsonBall.java @@ -27,6 +27,7 @@ import org.jclouds.util.Patterns; * As String is final, using a different marker to imply this is a json object * * @author Adrian Cole + * @see */ public class JsonBall implements java.io.Serializable, Comparable, CharSequence { diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java index 1760798702..c56ff1e4d1 100644 --- a/core/src/main/java/org/jclouds/json/config/GsonModule.java +++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java @@ -33,11 +33,12 @@ import org.jclouds.json.Json; import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue; import org.jclouds.json.internal.GsonWrapper; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.primitives.Bytes; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JcloudsGsonPackageAccessor; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -46,7 +47,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import com.google.gson.MapTypeAdapter; +import com.google.gson.ObjectMapTypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.inject.AbstractModule; import com.google.inject.ImplementedBy; @@ -63,11 +64,11 @@ public class GsonModule extends AbstractModule { @Provides @Singleton Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter, - ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception { + ByteArrayAdapter byteArrayAdapter, SerializePropertiesDefaults propertiesAdapter, + JsonAdapterBindings bindings) throws ClassNotFoundException, Exception { GsonBuilder builder = new GsonBuilder(); - JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Enum.class, - new EnumTypeAdapterThatReturnsFromValue()); - JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Map.class, new MapTypeAdapter()); + builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue()); + builder.registerTypeHierarchyAdapter(Map.class, new ObjectMapTypeAdapter()); builder.registerTypeAdapter(JsonBall.class, jsonAdapter); builder.registerTypeAdapter(Date.class, adapter); builder.registerTypeAdapter(new TypeToken>() { @@ -79,6 +80,7 @@ public class GsonModule extends AbstractModule { return builder.create(); } + // http://code.google.com/p/google-gson/issues/detail?id=326 @ImplementedBy(JsonBallAdapterImpl.class) public static interface JsonBallAdapter extends JsonSerializer, JsonDeserializer { @@ -92,7 +94,7 @@ public class GsonModule extends AbstractModule { } public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { return new JsonBall(json.toString()); } @@ -118,7 +120,7 @@ public class GsonModule extends AbstractModule { @Override public List deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { return Bytes.asList(CryptoStreams.hex(json.getAsString())); } @@ -134,7 +136,7 @@ public class GsonModule extends AbstractModule { @Override public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { return CryptoStreams.hex(json.getAsString()); } @@ -158,7 +160,7 @@ public class GsonModule extends AbstractModule { } public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { String toParse = json.getAsJsonPrimitive().getAsString(); try { return dateService.iso8601DateParse(toParse); @@ -183,7 +185,7 @@ public class GsonModule extends AbstractModule { } public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { String toParse = json.getAsJsonPrimitive().getAsString(); Date toReturn = dateService.cDateParse(toParse); return toReturn; @@ -199,7 +201,7 @@ public class GsonModule extends AbstractModule { } public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { long toParse = json.getAsJsonPrimitive().getAsLong(); if (toParse == -1) return null; diff --git a/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java b/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java index c25969dc6a..f307da024f 100644 --- a/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java +++ b/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java @@ -27,7 +27,7 @@ import com.google.gson.JsonObject; /** * Exposes the JsonObject as a map so that we can use gauva apis on it. - * + * http://code.google.com/p/google-gson/issues/detail?id=325 * @author Adrian Cole */ public enum JsonObjectAsMap implements Function> { diff --git a/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java b/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java index ed3a293b97..8aa5ca8ca2 100644 --- a/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java +++ b/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java @@ -24,12 +24,11 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; -import com.google.gson.MapTypeAdapter; /** * This is a class that helps the default {@link MapTypeAdapter} make a sane object graph when the * value is set to {@code Object} - * + * http://code.google.com/p/google-gson/issues/detail?id=325 * @author Adrian Cole */ public enum ParseObjectFromElement implements Function { diff --git a/core/src/test/java/org/jclouds/json/JsonTest.java b/core/src/test/java/org/jclouds/json/JsonTest.java index 5b7d8bcb49..0dea4e9e2a 100644 --- a/core/src/test/java/org/jclouds/json/JsonTest.java +++ b/core/src/test/java/org/jclouds/json/JsonTest.java @@ -36,6 +36,52 @@ import com.google.inject.TypeLiteral; public class JsonTest { private Json json = Guice.createInjector(new GsonModule()).getInstance(Json.class); + private static class ObjectNoDefaultConstructor { + private final String stringValue; + private final int intValue; + + public ObjectNoDefaultConstructor(String stringValue, int intValue) { + this.stringValue = stringValue; + this.intValue = intValue; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + intValue; + result = prime * result + ((stringValue == null) ? 0 : stringValue.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; + ObjectNoDefaultConstructor other = (ObjectNoDefaultConstructor) obj; + if (intValue != other.intValue) + return false; + if (stringValue == null) { + if (other.stringValue != null) + return false; + } else if (!stringValue.equals(other.stringValue)) + return false; + return true; + } + } + + public void testObjectNoDefaultConstructor() { + ObjectNoDefaultConstructor obj = new ObjectNoDefaultConstructor("foo", 1); + assertEquals(json.toJson(obj), "{\"stringValue\":\"foo\",\"intValue\":1}"); + ObjectNoDefaultConstructor obj2 = json.fromJson(json.toJson(obj), ObjectNoDefaultConstructor.class); + assertEquals(obj2, obj); + assertEquals(json.toJson(obj2), json.toJson(obj)); + } + private static class EnumInside { private static enum Test { FOO, BAR; @@ -52,7 +98,7 @@ public class JsonTest { map.put("map", ImmutableMap.of("key", "value")); map.put("list", ImmutableList.of("key", "value")); assertEquals(json.toJson(map), - "{\"string\":\"string\",\"map\":{\"key\":\"value\"},\"list\":[\"key\",\"value\"],\"boolean\":true,\"number\":1}"); + "{\"string\":\"string\",\"map\":{\"key\":\"value\"},\"list\":[\"key\",\"value\"],\"boolean\":true,\"number\":1}"); Map map2 = json.fromJson(json.toJson(map), new TypeLiteral>() { }.getType()); assertEquals(map2, map); @@ -107,12 +153,12 @@ public class JsonTest { public void testDeserializeEnumWithParser() { assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInsideWithParser.class).enumValue, - EnumInsideWithParser.Test.FOO); + EnumInsideWithParser.Test.FOO); } public void testDeserializeEnumWithParserAndBadValue() { assertEquals(json.fromJson("{enumValue : \"sd\"}", EnumInsideWithParser.class).enumValue, - EnumInsideWithParser.Test.UNRECOGNIZED); + EnumInsideWithParser.Test.UNRECOGNIZED); } }