diff --git a/gson/src/main/java/com/google/gson/BaseMapTypeAdapter.java b/gson/src/main/java/com/google/gson/BaseMapTypeAdapter.java deleted file mode 100644 index b196e7cc..00000000 --- a/gson/src/main/java/com/google/gson/BaseMapTypeAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.Type; -import java.util.Map; - -/** - * Captures all the common/shared logic between the old, ({@link MapTypeAdapter}, and - * the new, {@link MapAsArrayTypeAdapter}, map type adapters. - * - * @author Joel Leitch - */ -abstract class BaseMapTypeAdapter - implements JsonSerializer>, JsonDeserializer> { - - protected static JsonElement serialize(JsonSerializationContext context, - Object src, Type srcType) { - return context.serialize(src, srcType, false, false); - } - - protected static Map constructMapType( - Type mapType, JsonDeserializationContext context) { - return context.construct(mapType); - } -} \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index 2870775f..c201178e 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -16,30 +16,19 @@ package com.google.gson; -import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ParameterizedTypeHandlerMap; import java.lang.reflect.Type; import java.sql.Time; import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.BitSet; import java.util.Calendar; -import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.SortedSet; import java.util.TimeZone; -import java.util.TreeSet; /** * List of all the default type adapters ({@link JsonSerializer}s, {@link JsonDeserializer}s, @@ -61,7 +50,6 @@ final class DefaultTypeAdapters { @SuppressWarnings("unchecked") private static final EnumTypeAdapter ENUM_TYPE_ADAPTER = new EnumTypeAdapter(); private static final BitSetTypeAdapter BIT_SET_ADAPTER = new BitSetTypeAdapter(); - private static final MapTypeAdapter MAP_TYPE_ADAPTER = new MapTypeAdapter(); private static final CharacterTypeAdapter CHARACTER_TYPE_ADAPTER = new CharacterTypeAdapter(); private static final NumberTypeAdapter NUMBER_TYPE_ADAPTER = new NumberTypeAdapter(); @@ -107,7 +95,6 @@ final class DefaultTypeAdapters { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER, true); - map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER, true); map.makeUnmodifiable(); return map; } @@ -135,35 +122,14 @@ final class DefaultTypeAdapters { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER), true); - map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER), true); map.makeUnmodifiable(); return map; } @SuppressWarnings("unchecked") private static ParameterizedTypeHandlerMap> createDefaultInstanceCreators() { - ParameterizedTypeHandlerMap> map = - new ParameterizedTypeHandlerMap>(); - DefaultConstructorAllocator allocator = new DefaultConstructorAllocator(50); - - // Map Instance Creators - map.registerForTypeHierarchy(Map.class, - new DefaultConstructorCreator(LinkedHashMap.class, allocator), true); - - // Add Collection type instance creators - DefaultConstructorCreator listCreator = - new DefaultConstructorCreator(ArrayList.class, allocator); - DefaultConstructorCreator queueCreator = - new DefaultConstructorCreator(LinkedList.class, allocator); - DefaultConstructorCreator setCreator = - new DefaultConstructorCreator(HashSet.class, allocator); - DefaultConstructorCreator sortedSetCreator = - new DefaultConstructorCreator(TreeSet.class, allocator); - map.registerForTypeHierarchy(Collection.class, listCreator, true); - map.registerForTypeHierarchy(Queue.class, queueCreator, true); - map.registerForTypeHierarchy(Set.class, setCreator, true); - map.registerForTypeHierarchy(SortedSet.class, sortedSetCreator, true); - + ParameterizedTypeHandlerMap> map + = new ParameterizedTypeHandlerMap>(); map.makeUnmodifiable(); return map; } @@ -566,33 +532,4 @@ final class DefaultTypeAdapters { return CharacterTypeAdapter.class.getSimpleName(); } } - - @SuppressWarnings("unchecked") - private static final class DefaultConstructorCreator implements InstanceCreator { - private final Class defaultInstance; - private final DefaultConstructorAllocator allocator; - - public DefaultConstructorCreator(Class defaultInstance, - DefaultConstructorAllocator allocator) { - this.defaultInstance = defaultInstance; - this.allocator = allocator; - } - - public T createInstance(Type type) { - Class rawType = $Gson$Types.getRawType(type); - try { - T specificInstance = (T) allocator.newInstance(rawType); - return (specificInstance == null) - ? allocator.newInstance(defaultInstance) - : specificInstance; - } catch (Exception e) { - throw new JsonIOException(e); - } - } - - @Override - public String toString() { - return DefaultConstructorCreator.class.getSimpleName(); - } - } } diff --git a/gson/src/main/java/com/google/gson/DelegatingJsonElementVisitor.java b/gson/src/main/java/com/google/gson/DelegatingJsonElementVisitor.java deleted file mode 100644 index a3970e4d..00000000 --- a/gson/src/main/java/com/google/gson/DelegatingJsonElementVisitor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Preconditions; - -import java.io.IOException; - -/** - * A simple implementation of the {@link JsonElementVisitor} that simply delegates the method - * invocation onto a {@code delegate} instance of the {@link JsonElementVisitor}. This object - * can be used to build a chain of visitors such that each Visitor instance can perform some - * operation on the {@link JsonElement} and then pass on the input to the delegate. This kind - * of pattern is sometimes referred as a "Chain of Responsibility". - * - *

The following is an example use case: - * - *

- * class JsonEscapingVisitor extends DelegatingJsonElementVisitor {
- *   public JsonEscapingVisitor(JsonElementVisitor) {
- *     super(visitor);
- *   }
- *
- *   public void visitPrimitive(JsonPrimitive primitive) {
- *     JsonPrimitive escapedPrimitive = escapePrimitiveObject(primitive);
- *     super.visitPrimitive(escapedPrimitive);
- *   }
- * }
- *
- * JsonElementVisitor visitor = new JsonEscapingVisitor(new FormattingVisitor());
- * 

- * - * @author Joel Leitch - */ -final class DelegatingJsonElementVisitor implements JsonElementVisitor { - private final JsonElementVisitor delegate; - - protected DelegatingJsonElementVisitor(JsonElementVisitor delegate) { - this.delegate = $Gson$Preconditions.checkNotNull(delegate); - } - - public void endArray(JsonArray array) throws IOException { - delegate.endArray(array); - } - - public void endObject(JsonObject object) throws IOException { - delegate.endObject(object); - } - - public void startArray(JsonArray array) throws IOException { - delegate.startArray(array); - } - - public void startObject(JsonObject object) throws IOException { - delegate.startObject(object); - } - - public void visitArrayMember(JsonArray parent, JsonPrimitive member, - boolean isFirst) throws IOException { - delegate.visitArrayMember(parent, member, isFirst); - } - - public void visitArrayMember(JsonArray parent, JsonArray member, - boolean isFirst) throws IOException { - delegate.visitArrayMember(parent, member, isFirst); - } - - public void visitArrayMember(JsonArray parent, JsonObject member, - boolean isFirst) throws IOException { - delegate.visitArrayMember(parent, member, isFirst); - } - - public void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member, - boolean isFirst) throws IOException { - delegate.visitObjectMember(parent, memberName, member, isFirst); - } - - public void visitObjectMember(JsonObject parent, String memberName, JsonArray member, - boolean isFirst) throws IOException { - delegate.visitObjectMember(parent, memberName, member, isFirst); - } - - public void visitObjectMember(JsonObject parent, String memberName, JsonObject member, - boolean isFirst) throws IOException { - delegate.visitObjectMember(parent, memberName, member, isFirst); - } - - public void visitNullObjectMember(JsonObject parent, String memberName, - boolean isFirst) throws IOException { - delegate.visitNullObjectMember(parent, memberName, isFirst); - } - - public void visitPrimitive(JsonPrimitive primitive) throws IOException { - delegate.visitPrimitive(primitive); - } - - public void visitNull() throws IOException { - delegate.visitNull(); - } - - public void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException { - delegate.visitNullArrayMember(parent, isFirst); - } -} diff --git a/gson/src/main/java/com/google/gson/FieldAttributes.java b/gson/src/main/java/com/google/gson/FieldAttributes.java index 0aa47511..1df8f2ff 100644 --- a/gson/src/main/java/com/google/gson/FieldAttributes.java +++ b/gson/src/main/java/com/google/gson/FieldAttributes.java @@ -19,6 +19,7 @@ package com.google.gson; import com.google.gson.internal.$Gson$Preconditions; import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.Pair; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Type; diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index ddc70ed8..17eefe2c 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -16,15 +16,19 @@ package com.google.gson; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.ParameterizedTypeHandlerMap; +import com.google.gson.internal.Primitives; import com.google.gson.internal.Streams; import com.google.gson.internal.bind.ArrayTypeAdapter; import com.google.gson.internal.bind.BigDecimalTypeAdapter; import com.google.gson.internal.bind.BigIntegerTypeAdapter; -import com.google.gson.internal.bind.CollectionTypeAdapter; -import com.google.gson.internal.bind.GsonCompatibleMapTypeAdapter; +import com.google.gson.internal.bind.CollectionTypeAdapterFactory; +import com.google.gson.internal.bind.ExcludedTypeAdapterFactory; +import com.google.gson.internal.bind.MapTypeAdapterFactory; import com.google.gson.internal.bind.MiniGson; import com.google.gson.internal.bind.ObjectTypeAdapter; -import com.google.gson.internal.bind.ReflectiveTypeAdapter; +import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; import com.google.gson.internal.bind.TypeAdapter; import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.reflect.TypeToken; @@ -32,7 +36,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import com.google.gson.stream.MalformedJsonException; - +import java.io.EOFException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; @@ -113,7 +117,7 @@ public final class Gson { private final ExclusionStrategy deserializationExclusionStrategy; private final ExclusionStrategy serializationExclusionStrategy; private final FieldNamingStrategy2 fieldNamingPolicy; - private final MappedObjectConstructor objectConstructor; + private final ConstructorConstructor constructorConstructor; /** Map containing Type or Class objects as keys */ private final ParameterizedTypeHandlerMap> serializers; @@ -164,7 +168,7 @@ public final class Gson { */ public Gson() { this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY, - new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()), + DefaultTypeAdapters.getDefaultInstanceCreators(), false, DefaultTypeAdapters.getAllDefaultSerializers(), DefaultTypeAdapters.getAllDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE, true, false, false, LongSerializationPolicy.DEFAULT); @@ -173,7 +177,7 @@ public final class Gson { Gson(final ExclusionStrategy deserializationExclusionStrategy, final ExclusionStrategy serializationExclusionStrategy, final FieldNamingStrategy2 fieldNamingPolicy, - final MappedObjectConstructor objectConstructor, boolean serializeNulls, + final ParameterizedTypeHandlerMap> instanceCreators, boolean serializeNulls, final ParameterizedTypeHandlerMap> serializers, final ParameterizedTypeHandlerMap> deserializers, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, @@ -181,7 +185,7 @@ public final class Gson { this.deserializationExclusionStrategy = deserializationExclusionStrategy; this.serializationExclusionStrategy = serializationExclusionStrategy; this.fieldNamingPolicy = fieldNamingPolicy; - this.objectConstructor = objectConstructor; + this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.serializeNulls = serializeNulls; this.serializers = serializers; this.deserializers = deserializers; @@ -196,33 +200,24 @@ public final class Gson { serializeNulls serializers */ - TypeAdapter.Factory reflectiveTypeAdapterFactory = - new ReflectiveTypeAdapter.FactoryImpl() { + TypeAdapter.Factory reflectiveTypeAdapterFactory + = new ReflectiveTypeAdapterFactory(constructorConstructor) { @Override public String getFieldName(Class declaringClazz, Field f, Type declaredType) { return fieldNamingPolicy.translateName(new FieldAttributes(declaringClazz, f, declaredType)); } @Override public boolean serializeField(Class declaringClazz, Field f, Type declaredType) { - return !Gson.this.serializationExclusionStrategy.shouldSkipField( - new FieldAttributes(declaringClazz, f, declaredType)); + ExclusionStrategy strategy = Gson.this.serializationExclusionStrategy; + return !strategy.shouldSkipClass(f.getType()) + && !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f, declaredType)); } + @Override public boolean deserializeField(Class declaringClazz, Field f, Type declaredType) { - return !Gson.this.deserializationExclusionStrategy.shouldSkipField( - new FieldAttributes(declaringClazz, f, declaredType)); - } - }; - - TypeAdapter.Factory excludedTypeFactory = new TypeAdapter.Factory() { - public TypeAdapter create(MiniGson context, TypeToken type) { - Class rawType = type.getRawType(); - if (serializationExclusionStrategy.shouldSkipClass(rawType) - || deserializationExclusionStrategy.shouldSkipClass(rawType)) { - return TypeAdapters.EXCLUDED_TYPE_ADAPTER; - } else { - return null; - } + ExclusionStrategy strategy = Gson.this.deserializationExclusionStrategy; + return !strategy.shouldSkipClass(f.getType()) + && !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f, declaredType)); } }; @@ -238,6 +233,8 @@ public final class Gson { doubleAdapter(serializeSpecialFloatingPointValues))) .factory(TypeAdapters.newFactory(float.class, Float.class, floatAdapter(serializeSpecialFloatingPointValues))) + .factory(new ExcludedTypeAdapterFactory( + serializationExclusionStrategy, deserializationExclusionStrategy)) .factory(TypeAdapters.STRING_FACTORY) .factory(TypeAdapters.STRING_BUILDER_FACTORY) .factory(TypeAdapters.STRING_BUFFER_FACTORY) @@ -248,11 +245,12 @@ public final class Gson { .factory(TypeAdapters.INET_ADDRESS_FACTORY) .typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter()) .typeAdapter(BigInteger.class, new BigIntegerTypeAdapter()) - .factory(excludedTypeFactory) - .factory(GsonCompatibleMapTypeAdapter.FACTORY) - .factory(CollectionTypeAdapter.FACTORY) + .factory(new MapTypeAdapterFactory(constructorConstructor)) + .factory(new CollectionTypeAdapterFactory(constructorConstructor)) .factory(ObjectTypeAdapter.FACTORY) - .factory(new GsonToMiniGsonTypeAdapter(serializers, deserializers, serializeNulls)) + .factory(new GsonToMiniGsonTypeAdapterFactory(serializers, deserializers, + new JsonDeserializationContext(this), new JsonSerializationContext(this), serializeNulls + )) .factory(ArrayTypeAdapter.FACTORY) .factory(reflectiveTypeAdapterFactory); @@ -265,9 +263,17 @@ public final class Gson { } return new TypeAdapter() { @Override public Double read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } return reader.nextDouble(); } @Override public void write(JsonWriter writer, Number value) throws IOException { + if (value == null) { + writer.nullValue(); // TODO: better policy here? + return; + } double doubleValue = value.doubleValue(); checkValidFloatingPoint(doubleValue); writer.value(value); @@ -281,9 +287,17 @@ public final class Gson { } return new TypeAdapter() { @Override public Float read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } return (float) reader.nextDouble(); } @Override public void write(JsonWriter writer, Number value) throws IOException { + if (value == null) { + writer.nullValue(); // TODO: better policy here? + return; + } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); writer.value(value); @@ -305,9 +319,17 @@ public final class Gson { } return new TypeAdapter() { @Override public Number read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } return reader.nextLong(); } @Override public void write(JsonWriter writer, Number value) throws IOException { + if (value == null) { + writer.nullValue(); // TODO: better policy here? + return; + } writer.value(value.toString()); } }; @@ -640,11 +662,23 @@ public final class Gson { */ @SuppressWarnings("unchecked") public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + boolean isEmpty = true; boolean oldLenient = reader.isLenient(); reader.setLenient(true); try { + reader.peek(); + isEmpty = false; TypeAdapter typeAdapter = (TypeAdapter) miniGson.getAdapter(TypeToken.get(typeOfT)); return typeAdapter.read(reader); + } catch (EOFException e) { + /* + * For compatibility with JSON 1.5 and earlier, we return null for empty + * documents instead of throwing. + */ + if (isEmpty) { + return null; + } + throw new JsonSyntaxException(e); } catch (IOException e) { // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException throw new JsonSyntaxException(e); @@ -710,8 +744,9 @@ public final class Gson { // using the name instanceCreator instead of ObjectConstructor since the users of Gson are // more familiar with the concept of Instance Creators. Moreover, the objectConstructor is // just a utility class around instance creators, and its toString() only displays them. - .append(",instanceCreators:").append(objectConstructor) + .append(",instanceCreators:").append(constructorConstructor) .append("}"); return sb.toString(); } + } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index cdd123cc..09f580c0 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -18,6 +18,8 @@ package com.google.gson; import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; import com.google.gson.internal.$Gson$Preconditions; +import com.google.gson.internal.ParameterizedTypeHandlerMap; +import com.google.gson.internal.Primitives; import java.lang.reflect.Type; import java.sql.Timestamp; import java.text.DateFormat; @@ -678,6 +680,7 @@ public final class GsonBuilder { addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers, customDeserializers); + customSerializers.registerIfAbsent(DefaultTypeAdapters.getDefaultSerializers()); customDeserializers.registerIfAbsent(DefaultTypeAdapters.getDefaultDeserializers()); ParameterizedTypeHandlerMap> customInstanceCreators = @@ -688,11 +691,9 @@ public final class GsonBuilder { customDeserializers.makeUnmodifiable(); instanceCreators.makeUnmodifiable(); - MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators); - return new Gson(new DisjunctionExclusionStrategy(deserializationStrategies), new DisjunctionExclusionStrategy(serializationStrategies), - fieldNamingPolicy, objConstructor, serializeNulls, + fieldNamingPolicy, instanceCreators, serializeNulls, customSerializers, customDeserializers, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, serializeSpecialFloatingPointValues, longSerializationPolicy); } diff --git a/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapter.java b/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapter.java deleted file mode 100644 index 0daa1ff4..00000000 --- a/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.io.IOException; -import java.lang.reflect.Type; - -import com.google.gson.internal.Streams; -import com.google.gson.internal.bind.MiniGson; -import com.google.gson.internal.bind.TypeAdapter; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -final class GsonToMiniGsonTypeAdapter implements TypeAdapter.Factory { - private final ParameterizedTypeHandlerMap> serializers; - private final ParameterizedTypeHandlerMap> deserializers; - private final boolean serializeNulls; - - GsonToMiniGsonTypeAdapter(ParameterizedTypeHandlerMap> serializers, - ParameterizedTypeHandlerMap> deserializers, boolean serializeNulls) { - this.serializers = serializers; - this.deserializers = deserializers; - this.serializeNulls = serializeNulls; - } - - public TypeAdapter create(final MiniGson miniGson, TypeToken type) { - final Type typeOfT = type.getType(); - final JsonSerializer serializer = serializers.getHandlerFor(typeOfT, false); - final JsonDeserializer deserializer = deserializers.getHandlerFor(typeOfT, false); - if (serializer == null && deserializer == null) { - return null; - } - return new TypeAdapter() { - @Override - public Object read(JsonReader reader) throws IOException { - if (deserializer == null) { - // TODO: handle if deserializer is null - throw new UnsupportedOperationException(); - } - JsonElement value = Streams.parse(reader); - if (value.isJsonNull()) { - return null; - } - return deserializer.deserialize(value, typeOfT, createDeserializationContext(miniGson)); - } - @Override - public void write(JsonWriter writer, Object value) throws IOException { - if (serializer == null) { - // TODO: handle if serializer is null - throw new UnsupportedOperationException(); - } - if (value == null) { - writer.nullValue(); - return; - } - JsonElement element = serializer.serialize(value, typeOfT, createSerializationContext(miniGson)); - Streams.write(element, serializeNulls, writer); - } - }; - } - - public JsonSerializationContext createSerializationContext(final MiniGson miniGson) { - return new JsonSerializationContext() { - @Override - JsonElement serialize(Object src, Type typeOfSrc, boolean preserveType, boolean defaultOnly) { - TypeToken typeToken = TypeToken.get(typeOfSrc); - return miniGson.getAdapter(typeToken).toJsonElement(src); - } - }; - } - public JsonDeserializationContext createDeserializationContext(final MiniGson miniGson) { - return new JsonDeserializationContext() { - @Override - public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { - TypeToken typeToken = TypeToken.get(typeOfT); - return (T) miniGson.getAdapter(typeToken).fromJsonElement(json); - } - - @Override public T construct(Type type) { - throw new UnsupportedOperationException(); - } - - @Override public Object constructArray(Type type, int length) { - throw new UnsupportedOperationException(); - } - - @Override public T deserializeDefault(JsonElement json, Type typeOfT) throws JsonParseException { - throw new UnsupportedOperationException(); - } - }; - } -} diff --git a/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java new file mode 100644 index 00000000..77fdd02a --- /dev/null +++ b/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java @@ -0,0 +1,99 @@ +/* + * 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 com.google.gson.internal.ParameterizedTypeHandlerMap; +import com.google.gson.internal.Streams; +import com.google.gson.internal.bind.MiniGson; +import com.google.gson.internal.bind.TypeAdapter; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; + +final class GsonToMiniGsonTypeAdapterFactory implements TypeAdapter.Factory { + private final ParameterizedTypeHandlerMap> serializers; + private final ParameterizedTypeHandlerMap> deserializers; + private final JsonDeserializationContext deserializationContext; + private final JsonSerializationContext serializationContext; + private final boolean serializeNulls; + + GsonToMiniGsonTypeAdapterFactory(ParameterizedTypeHandlerMap> serializers, + ParameterizedTypeHandlerMap> deserializers, + JsonDeserializationContext deserializationContext, + JsonSerializationContext serializationContext, boolean serializeNulls) { + this.serializers = serializers; + this.deserializers = deserializers; + this.serializeNulls = serializeNulls; + this.deserializationContext = deserializationContext; + this.serializationContext = serializationContext; + } + + public TypeAdapter create(final MiniGson context, final TypeToken typeToken) { + final Type type = typeToken.getType(); + + @SuppressWarnings("unchecked") // guaranteed to match typeOfT + final JsonSerializer serializer + = (JsonSerializer) serializers.getHandlerFor(type, false); + @SuppressWarnings("unchecked") // guaranteed to match typeOfT + final JsonDeserializer deserializer + = (JsonDeserializer) deserializers.getHandlerFor(type, false); + + if (serializer == null && deserializer == null) { + return null; + } + + return new TypeAdapter() { + /** + * The delegate is lazily created because it may not be needed, and + * creating it may fail. + */ + private TypeAdapter delegate; + + @Override public T read(JsonReader reader) throws IOException { + if (deserializer == null) { + return delegate().read(reader); + } + JsonElement value = Streams.parse(reader); + if (value.isJsonNull()) { + return null; + } + return deserializer.deserialize(value, type, deserializationContext); + } + + @Override public void write(JsonWriter writer, T value) throws IOException { + if (serializer == null) { + delegate().write(writer, value); + return; + } + if (value == null) { + writer.nullValue(); + return; + } + JsonElement element = serializer.serialize(value, type, serializationContext); + Streams.write(element, serializeNulls, writer); + } + + private TypeAdapter delegate() { + TypeAdapter d = delegate; + return d != null + ? d + : (delegate = context.getNextAdapter(GsonToMiniGsonTypeAdapterFactory.this, typeToken)); + } + }; + } +} diff --git a/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java deleted file mode 100644 index c7806acf..00000000 --- a/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Types; - -import java.lang.reflect.Array; -import java.lang.reflect.Type; - -/** - * A visitor that populates fields of an object with data from its equivalent - * JSON representation - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class JsonArrayDeserializationVisitor extends JsonDeserializationVisitor { - - JsonArrayDeserializationVisitor(JsonArray jsonArray, Type arrayType, - ObjectNavigator objectNavigator, FieldNamingStrategy2 fieldNamingPolicy, - ObjectConstructor objectConstructor, - ParameterizedTypeHandlerMap> deserializers, - JsonDeserializationContext context) { - super(jsonArray, arrayType, objectNavigator, fieldNamingPolicy, objectConstructor, deserializers, context); - } - - @Override - @SuppressWarnings("unchecked") - protected T constructTarget() { - if (!json.isJsonArray()) { - throw new JsonParseException("Expecting array found: " + json); - } - JsonArray jsonArray = json.getAsJsonArray(); - if ($Gson$Types.isArray(targetType)) { - // We know that we are getting back an array of the required type, so - // this typecasting is safe. - return (T) objectConstructor.constructArray($Gson$Types.getArrayComponentType(targetType), - jsonArray.size()); - } - // is a collection - return (T) objectConstructor.construct($Gson$Types.getRawType(targetType)); - } - - public void visitArray(Object array, Type arrayType) { - if (!json.isJsonArray()) { - throw new JsonParseException("Expecting array found: " + json); - } - JsonArray jsonArray = json.getAsJsonArray(); - for (int i = 0; i < jsonArray.size(); i++) { - JsonElement jsonChild = jsonArray.get(i); - Object child; - - if (jsonChild == null || jsonChild.isJsonNull()) { - child = null; - } else if (jsonChild instanceof JsonObject) { - child = visitChildAsObject($Gson$Types.getArrayComponentType(arrayType), jsonChild); - } else if (jsonChild instanceof JsonArray) { - child = visitChildAsArray($Gson$Types.getArrayComponentType(arrayType), - jsonChild.getAsJsonArray()); - } else if (jsonChild instanceof JsonPrimitive) { - child = visitChildAsObject($Gson$Types.getArrayComponentType(arrayType), - jsonChild.getAsJsonPrimitive()); - } else { - throw new IllegalStateException(); - } - Array.set(array, i, child); - } - } - - // We should not implement any other method from Visitor interface since - // all other methods should be invoked on JsonObjectDeserializationVisitor - // instead. - - public void startVisitingObject(Object node) { - throw new JsonParseException("Expecting array but found object: " + node); - } - - public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) { - throw new JsonParseException("Expecting array but found array field " + f.getName() + ": " - + obj); - } - - public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) { - throw new JsonParseException("Expecting array but found object field " + f.getName() + ": " - + obj); - } - - public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type actualTypeOfField, Object parent) { - throw new JsonParseException("Expecting array but found field " + f.getName() + ": " - + parent); - } - - public void visitPrimitive(Object primitive) { - throw new JsonParseException( - "Type information is unavailable, and the target is not a primitive: " + json); - } -} diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContext.java b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java index 3bfbe763..578e6e89 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContext.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java @@ -27,23 +27,10 @@ import java.lang.reflect.Type; * @author Joel Leitch */ public class JsonDeserializationContext { - private final ObjectNavigator objectNavigator; - private final FieldNamingStrategy2 fieldNamingPolicy; - private final ParameterizedTypeHandlerMap> deserializers; - private final MappedObjectConstructor objectConstructor; + private final Gson gson; - JsonDeserializationContext(ObjectNavigator objectNavigator, - FieldNamingStrategy2 fieldNamingPolicy, - ParameterizedTypeHandlerMap> deserializers, - MappedObjectConstructor objectConstructor) { - this.objectNavigator = objectNavigator; - this.fieldNamingPolicy = fieldNamingPolicy; - this.deserializers = deserializers; - this.objectConstructor = objectConstructor; - } - - JsonDeserializationContext() { - this(null, null, null, null); + JsonDeserializationContext(Gson gson) { + this.gson = gson; } /** @@ -51,41 +38,11 @@ public class JsonDeserializationContext { */ @SuppressWarnings("unchecked") @Deprecated public T construct(Type type) { - Object instance = objectConstructor.construct(type); - return (T) instance; + throw new UnsupportedOperationException(); } public Object constructArray(Type type, int length) { - return objectConstructor.constructArray(type, length); - } - - - private T fromJsonArray(Type arrayType, JsonArray jsonArray, - JsonDeserializationContext context, boolean systemOnly) throws JsonParseException { - JsonArrayDeserializationVisitor visitor = new JsonArrayDeserializationVisitor( - jsonArray, arrayType, objectNavigator, fieldNamingPolicy, - objectConstructor, deserializers, context); - objectNavigator.accept(new ObjectTypePair(null, arrayType, true, systemOnly), visitor); - return visitor.getTarget(); - } - - private T fromJsonObject(Type typeOfT, JsonObject jsonObject, - JsonDeserializationContext context, boolean systemOnly) throws JsonParseException { - JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( - jsonObject, typeOfT, objectNavigator, fieldNamingPolicy, - objectConstructor, deserializers, context); - objectNavigator.accept(new ObjectTypePair(null, typeOfT, true, systemOnly), visitor); - return visitor.getTarget(); - } - - @SuppressWarnings("unchecked") - private T fromJsonPrimitive(Type typeOfT, JsonPrimitive json, - JsonDeserializationContext context, boolean systemOnly) throws JsonParseException { - JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( - json, typeOfT, objectNavigator, fieldNamingPolicy, objectConstructor, deserializers, context); - objectNavigator.accept(new ObjectTypePair(json.getAsObject(), typeOfT, true, systemOnly), visitor); - Object target = visitor.getTarget(); - return (T) target; + throw new UnsupportedOperationException(); } /** @@ -102,37 +59,11 @@ public class JsonDeserializationContext { */ @SuppressWarnings("unchecked") public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { - if (json == null || json.isJsonNull()) { - return null; - } else if (json.isJsonArray()) { - Object array = fromJsonArray(typeOfT, json.getAsJsonArray(), this, false); - return (T) array; - } else if (json.isJsonObject()) { - Object object = fromJsonObject(typeOfT, json.getAsJsonObject(), this, false); - return (T) object; - } else if (json.isJsonPrimitive()) { - Object primitive = fromJsonPrimitive(typeOfT, json.getAsJsonPrimitive(), this, false); - return (T) primitive; - } else { - throw new JsonParseException("Failed parsing JSON source: " + json + " to Json"); - } + return gson.fromJson(json, typeOfT); } @SuppressWarnings("unchecked") public T deserializeDefault(JsonElement json, Type typeOfT) throws JsonParseException { - if (json == null || json.isJsonNull()) { - return null; - } else if (json.isJsonArray()) { - Object array = fromJsonArray(typeOfT, json.getAsJsonArray(), this, true); - return (T) array; - } else if (json.isJsonObject()) { - Object object = fromJsonObject(typeOfT, json.getAsJsonObject(), this, true); - return (T) object; - } else if (json.isJsonPrimitive()) { - Object primitive = fromJsonPrimitive(typeOfT, json.getAsJsonPrimitive(), this, true); - return (T) primitive; - } else { - throw new JsonParseException("Failed parsing JSON source: " + json + " to Json"); - } + throw new UnsupportedOperationException(); } } \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java deleted file mode 100644 index 033bf20d..00000000 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Preconditions; - -import java.lang.reflect.Type; - -/** - * Abstract data value container for the {@link ObjectNavigator.Visitor} - * implementations. This class exposes the {@link #getTarget()} method - * which returns the class that was visited by this object. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor { - - protected final ObjectNavigator objectNavigator; - protected final FieldNamingStrategy2 fieldNamingPolicy; - protected final ObjectConstructor objectConstructor; - protected final ParameterizedTypeHandlerMap> deserializers; - protected T target; - protected final JsonElement json; - protected final Type targetType; - protected final JsonDeserializationContext context; - protected boolean constructed; - - JsonDeserializationVisitor(JsonElement json, Type targetType, - ObjectNavigator objectNavigator, FieldNamingStrategy2 fieldNamingPolicy, - ObjectConstructor objectConstructor, - ParameterizedTypeHandlerMap> deserializers, - JsonDeserializationContext context) { - this.targetType = targetType; - this.objectNavigator = objectNavigator; - this.fieldNamingPolicy = fieldNamingPolicy; - this.objectConstructor = objectConstructor; - this.deserializers = deserializers; - this.json = $Gson$Preconditions.checkNotNull(json); - this.context = context; - this.constructed = false; - } - - public T getTarget() { - if (!constructed) { - target = constructTarget(); - constructed = true; - } - return target; - } - - protected abstract T constructTarget(); - - public void start(ObjectTypePair node) { - } - - public void end(ObjectTypePair node) { - } - - @SuppressWarnings("unchecked") - public final boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { - Pair, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers); - if (pair == null) { - return false; - } - Object value = invokeCustomDeserializer(json, pair); - target = (T) value; - constructed = true; - return true; - } - - protected Object invokeCustomDeserializer(JsonElement element, - Pair, ObjectTypePair> pair) { - if (element == null || element.isJsonNull()) { - return null; - } - Type objType = pair.second.type; - return (pair.first).deserialize(element, objType, context); - } - - final Object visitChildAsObject(Type childType, JsonElement jsonChild) { - JsonDeserializationVisitor childVisitor = - new JsonObjectDeserializationVisitor(jsonChild, childType, - objectNavigator, fieldNamingPolicy, objectConstructor, deserializers, context); - return visitChild(childType, childVisitor); - } - - final Object visitChildAsArray(Type childType, JsonArray jsonChild) { - JsonDeserializationVisitor childVisitor = - new JsonArrayDeserializationVisitor(jsonChild.getAsJsonArray(), childType, - objectNavigator, fieldNamingPolicy, objectConstructor, deserializers, context); - return visitChild(childType, childVisitor); - } - - private Object visitChild(Type type, JsonDeserializationVisitor childVisitor) { - objectNavigator.accept(new ObjectTypePair(null, type, false, false), childVisitor); - // the underlying object may have changed during the construction phase - // This happens primarily because of custom deserializers - return childVisitor.getTarget(); - } -} diff --git a/gson/src/main/java/com/google/gson/JsonElement.java b/gson/src/main/java/com/google/gson/JsonElement.java index 79938598..0a909c7b 100644 --- a/gson/src/main/java/com/google/gson/JsonElement.java +++ b/gson/src/main/java/com/google/gson/JsonElement.java @@ -143,7 +143,7 @@ public abstract class JsonElement { * more than a single element. */ public boolean getAsBoolean() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -156,7 +156,7 @@ public abstract class JsonElement { * more than a single element. */ Boolean getAsBooleanWrapper() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -169,7 +169,7 @@ public abstract class JsonElement { * more than a single element. */ public Number getAsNumber() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -182,7 +182,7 @@ public abstract class JsonElement { * more than a single element. */ public String getAsString() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -195,7 +195,7 @@ public abstract class JsonElement { * more than a single element. */ public double getAsDouble() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -208,7 +208,7 @@ public abstract class JsonElement { * more than a single element. */ public float getAsFloat() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -221,7 +221,7 @@ public abstract class JsonElement { * more than a single element. */ public long getAsLong() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -234,7 +234,7 @@ public abstract class JsonElement { * more than a single element. */ public int getAsInt() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -248,7 +248,7 @@ public abstract class JsonElement { * @since 1.3 */ public byte getAsByte() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -262,7 +262,7 @@ public abstract class JsonElement { * @since 1.3 */ public char getAsCharacter() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -276,7 +276,7 @@ public abstract class JsonElement { * @since 1.2 */ public BigDecimal getAsBigDecimal() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -290,7 +290,7 @@ public abstract class JsonElement { * @since 1.2 */ public BigInteger getAsBigInteger() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -303,7 +303,7 @@ public abstract class JsonElement { * more than a single element. */ public short getAsShort() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** @@ -316,7 +316,7 @@ public abstract class JsonElement { * more than a single element. */ Object getAsObject() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(getClass().getSimpleName()); } /** diff --git a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java deleted file mode 100644 index 173dd8a6..00000000 --- a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.google.gson; - - -import java.lang.reflect.Type; - -/** - * A visitor that populates fields of an object with data from its equivalent - * JSON representation - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class JsonObjectDeserializationVisitor extends JsonDeserializationVisitor { - - JsonObjectDeserializationVisitor(JsonElement json, Type type, - ObjectNavigator objectNavigator, FieldNamingStrategy2 fieldNamingPolicy, - ObjectConstructor objectConstructor, - ParameterizedTypeHandlerMap> deserializers, - JsonDeserializationContext context) { - super(json, type, objectNavigator, fieldNamingPolicy, objectConstructor, deserializers, context); - } - - @Override - @SuppressWarnings("unchecked") - protected T constructTarget() { - return (T) objectConstructor.construct(targetType); - } - - public void startVisitingObject(Object node) { - // do nothing - } - - public void visitArray(Object array, Type componentType) { - // should not be called since this case should invoke JsonArrayDeserializationVisitor - throw new JsonParseException("Expecting object but found array: " + array); - } - - public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) { - try { - if (!json.isJsonObject()) { - throw new JsonParseException("Expecting object found: " + json); - } - JsonObject jsonObject = json.getAsJsonObject(); - String fName = getFieldName(f); - JsonElement jsonChild = jsonObject.get(fName); - if (jsonChild != null) { - Object child = visitChildAsObject(typeOfF, jsonChild); - f.set(obj, child); - } else { - f.set(obj, null); - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) { - try { - if (!json.isJsonObject()) { - throw new JsonParseException("Expecting object found: " + json); - } - JsonObject jsonObject = json.getAsJsonObject(); - String fName = getFieldName(f); - JsonArray jsonChild = (JsonArray) jsonObject.get(fName); - if (jsonChild != null) { - Object array = visitChildAsArray(typeOfF, jsonChild); - f.set(obj, array); - } else { - f.set(obj, null); - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private String getFieldName(FieldAttributes f) { - return fieldNamingPolicy.translateName(f); - } - - public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) { - try { - String fName = getFieldName(f); - if (!json.isJsonObject()) { - throw new JsonParseException("Expecting object found: " + json); - } - JsonElement child = json.getAsJsonObject().get(fName); - boolean isPrimitive = Primitives.isPrimitive(declaredTypeOfField); - if (child == null) { // Child will be null if the field wasn't present in Json - return true; - } else if (child.isJsonNull()) { - if (!isPrimitive) { - f.set(parent, null); - } - return true; - } - ObjectTypePair objTypePair = new ObjectTypePair(null, declaredTypeOfField, false, false); - Pair, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers); - if (pair == null) { - return false; - } - Object value = invokeCustomDeserializer(child, pair); - if (value != null || !isPrimitive) { - f.set(parent, value); - } - return true; - } catch (IllegalAccessException e) { - throw new RuntimeException(); - } - } - - @SuppressWarnings("unchecked") - public void visitPrimitive(Object primitive) { - if (!json.isJsonPrimitive()) { - throw new JsonParseException( - "Type information is unavailable, and the target object is not a primitive: " + json); - } - JsonPrimitive prim = json.getAsJsonPrimitive(); - target = (T) prim.getAsObject(); - } -} diff --git a/gson/src/main/java/com/google/gson/JsonSerializationContext.java b/gson/src/main/java/com/google/gson/JsonSerializationContext.java index 7198a1bf..e5fee93d 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationContext.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationContext.java @@ -27,24 +27,10 @@ import java.lang.reflect.Type; */ public class JsonSerializationContext { - private final ObjectNavigator objectNavigator; - private final FieldNamingStrategy2 fieldNamingPolicy; - private final ParameterizedTypeHandlerMap> serializers; - private final boolean serializeNulls; - private final MemoryRefStack ancestors; + private final Gson gson; - JsonSerializationContext(ObjectNavigator objectNavigator, - FieldNamingStrategy2 fieldNamingPolicy, boolean serializeNulls, - ParameterizedTypeHandlerMap> serializers) { - this.objectNavigator = objectNavigator; - this.fieldNamingPolicy = fieldNamingPolicy; - this.serializeNulls = serializeNulls; - this.serializers = serializers; - this.ancestors = new MemoryRefStack(); - } - - JsonSerializationContext() { - this(null, null, false, null); + JsonSerializationContext(Gson gson) { + this.gson = gson; } /** @@ -54,10 +40,7 @@ public class JsonSerializationContext { * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. */ public JsonElement serialize(Object src) { - if (src == null) { - return JsonNull.INSTANCE; - } - return serialize(src, src.getClass(), false, false); + return gson.toJsonTree(src); } /** @@ -71,21 +54,10 @@ public class JsonSerializationContext { * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. */ public JsonElement serialize(Object src, Type typeOfSrc) { - return serialize(src, typeOfSrc, true, false); + return gson.toJsonTree(src, typeOfSrc); } public JsonElement serializeDefault(Object src, Type typeOfSrc) { - return serialize(src, typeOfSrc, true, true); - } - - JsonElement serialize(Object src, Type typeOfSrc, boolean preserveType, boolean defaultOnly) { - if (src == null) { - return JsonNull.INSTANCE; - } - JsonSerializationVisitor visitor = new JsonSerializationVisitor( - objectNavigator, fieldNamingPolicy, serializeNulls, serializers, this, ancestors); - ObjectTypePair objTypePair = new ObjectTypePair(src, typeOfSrc, preserveType, defaultOnly); - objectNavigator.accept(objTypePair, visitor); - return visitor.getJsonElement(); + throw new UnsupportedOperationException(); } } \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java deleted file mode 100644 index d29e6904..00000000 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Types; -import com.google.gson.internal.$Gson$Preconditions; - -import java.lang.reflect.Array; -import java.lang.reflect.Type; - -/** - * 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> serializers; - private final boolean serializeNulls; - private final JsonSerializationContext context; - private final MemoryRefStack ancestors; - private JsonElement root; - - JsonSerializationVisitor(ObjectNavigator objectNavigator, FieldNamingStrategy2 fieldNamingPolicy, - boolean serializeNulls, ParameterizedTypeHandlerMap> 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,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; - } -} diff --git a/gson/src/main/java/com/google/gson/JsonTreeNavigator.java b/gson/src/main/java/com/google/gson/JsonTreeNavigator.java deleted file mode 100644 index 992de504..00000000 --- a/gson/src/main/java/com/google/gson/JsonTreeNavigator.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.google.gson; - -import java.io.IOException; -import java.util.Map; - -/** - * A navigator to navigate a tree of JsonElement nodes in Depth-first order - * - * @author Inderjeet Singh - */ -final class JsonTreeNavigator { - private final JsonElementVisitor visitor; - private final boolean visitNulls; - - JsonTreeNavigator(JsonElementVisitor visitor, boolean visitNulls) { - this.visitor = visitor; - this.visitNulls = visitNulls; - } - - public void navigate(JsonElement element) throws IOException { - if (element.isJsonNull()) { - visitor.visitNull(); - } else if (element.isJsonArray()) { - JsonArray array = element.getAsJsonArray(); - visitor.startArray(array); - boolean isFirst = true; - for (JsonElement child : array) { - visitChild(array, child, isFirst); - if (isFirst) { - isFirst = false; - } - } - visitor.endArray(array); - } else if (element.isJsonObject()) { - JsonObject object = element.getAsJsonObject(); - visitor.startObject(object); - boolean isFirst = true; - for (Map.Entry member : object.entrySet()) { - boolean visited = visitChild(object, member.getKey(), member.getValue(), isFirst); - if (visited && isFirst) { - isFirst = false; - } - } - visitor.endObject(object); - } else { // must be JsonPrimitive - visitor.visitPrimitive(element.getAsJsonPrimitive()); - } - } - - /** - * Returns true if the child was visited, false if it was skipped. - */ - private boolean visitChild(JsonObject parent, String childName, JsonElement child, - boolean isFirst) throws IOException { - if (child.isJsonNull()) { - if (visitNulls) { - visitor.visitNullObjectMember(parent, childName, isFirst); - navigate(child.getAsJsonNull()); - } else { // Null value is being skipped. - return false; - } - } else if (child.isJsonArray()) { - JsonArray childAsArray = child.getAsJsonArray(); - visitor.visitObjectMember(parent, childName, childAsArray, isFirst); - navigate(childAsArray); - } else if (child.isJsonObject()) { - JsonObject childAsObject = child.getAsJsonObject(); - visitor.visitObjectMember(parent, childName, childAsObject, isFirst); - navigate(childAsObject); - } else { // is a JsonPrimitive - visitor.visitObjectMember(parent, childName, child.getAsJsonPrimitive(), isFirst); - } - return true; - } - - /** - * Returns true if the child was visited, false if it was skipped. - */ - private void visitChild(JsonArray parent, JsonElement child, boolean isFirst) throws IOException { - if (child.isJsonNull()) { - visitor.visitNullArrayMember(parent, isFirst); - navigate(child); - } else if (child.isJsonArray()) { - JsonArray childAsArray = child.getAsJsonArray(); - visitor.visitArrayMember(parent, childAsArray, isFirst); - navigate(childAsArray); - } else if (child.isJsonObject()) { - JsonObject childAsObject = child.getAsJsonObject(); - visitor.visitArrayMember(parent, childAsObject, isFirst); - navigate(childAsObject); - } else { // is a JsonPrimitive - visitor.visitArrayMember(parent, child.getAsJsonPrimitive(), isFirst); - } - } -} diff --git a/gson/src/main/java/com/google/gson/MapAsArrayTypeAdapter.java b/gson/src/main/java/com/google/gson/MapAsArrayTypeAdapter.java index aba1f6a5..6d2a2511 100644 --- a/gson/src/main/java/com/google/gson/MapAsArrayTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/MapAsArrayTypeAdapter.java @@ -16,10 +16,7 @@ package com.google.gson; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; import java.util.Map; /** @@ -90,79 +87,16 @@ import java.util.Map; * object. */ final class MapAsArrayTypeAdapter - extends BaseMapTypeAdapter implements JsonSerializer>, JsonDeserializer> { public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - Map result = constructMapType(typeOfT, context); - Type[] keyAndValueType = typeToTypeArguments(typeOfT); - if (json.isJsonArray()) { - JsonArray array = json.getAsJsonArray(); - for (int i = 0; i < array.size(); i++) { - JsonArray entryArray = array.get(i).getAsJsonArray(); - Object k = context.deserialize(entryArray.get(0), keyAndValueType[0]); - Object v = context.deserialize(entryArray.get(1), keyAndValueType[1]); - result.put(k, v); - } - checkSize(array, array.size(), result, result.size()); - } else { - JsonObject object = json.getAsJsonObject(); - for (Map.Entry entry : object.entrySet()) { - Object k = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueType[0]); - Object v = context.deserialize(entry.getValue(), keyAndValueType[1]); - result.put(k, v); - } - checkSize(object, object.entrySet().size(), result, result.size()); - } - return result; + // TODO + throw new UnsupportedOperationException(); } public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) { - Type[] keyAndValueType = typeToTypeArguments(typeOfSrc); - boolean serializeAsArray = false; - List keysAndValues = new ArrayList(); - for (Map.Entry entry : src.entrySet()) { - JsonElement key = serialize(context, entry.getKey(), keyAndValueType[0]); - serializeAsArray |= key.isJsonObject() || key.isJsonArray(); - keysAndValues.add(key); - keysAndValues.add(serialize(context, entry.getValue(), keyAndValueType[1])); - } - - if (serializeAsArray) { - JsonArray result = new JsonArray(); - for (int i = 0; i < keysAndValues.size(); i+=2) { - JsonArray entryArray = new JsonArray(); - entryArray.add(keysAndValues.get(i)); - entryArray.add(keysAndValues.get(i + 1)); - result.add(entryArray); - } - return result; - } else { - JsonObject result = new JsonObject(); - for (int i = 0; i < keysAndValues.size(); i+=2) { - result.add(keysAndValues.get(i).getAsString(), keysAndValues.get(i + 1)); - } - checkSize(src, src.size(), result, result.entrySet().size()); - return result; - } - } - - private Type[] typeToTypeArguments(Type typeOfT) { - if (typeOfT instanceof ParameterizedType) { - Type[] typeArguments = ((ParameterizedType) typeOfT).getActualTypeArguments(); - if (typeArguments.length != 2) { - throw new IllegalArgumentException("MapAsArrayTypeAdapter cannot handle " + typeOfT); - } - return typeArguments; - } - return new Type[] { Object.class, Object.class }; - } - - private void checkSize(Object input, int inputSize, Object output, int outputSize) { - if (inputSize != outputSize) { - throw new JsonSyntaxException("Input size " + inputSize + " != output size " + outputSize - + " for input " + input + " and output " + output); - } + // TODO + throw new UnsupportedOperationException(); } } diff --git a/gson/src/main/java/com/google/gson/MapTypeAdapter.java b/gson/src/main/java/com/google/gson/MapTypeAdapter.java deleted file mode 100644 index 7f95ef5b..00000000 --- a/gson/src/main/java/com/google/gson/MapTypeAdapter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 com.google.gson.internal.$Gson$Types; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Map; -import java.util.Set; - -/** - * 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. - * - * @author Joel Leitch - */ -@SuppressWarnings("unchecked") -final class MapTypeAdapter 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.INSTANCE; - } 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]); - Object value = context.deserialize(entry.getValue(), keyAndValueTypes[1]); - map.put(key, value); - } - return map; - } - - @Override - public String toString() { - return MapTypeAdapter.class.getSimpleName(); - } -} \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/MappedObjectConstructor.java b/gson/src/main/java/com/google/gson/MappedObjectConstructor.java deleted file mode 100644 index cc2f75bd..00000000 --- a/gson/src/main/java/com/google/gson/MappedObjectConstructor.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Types; - -import com.google.gson.internal.UnsafeAllocator; -import java.lang.reflect.Array; -import java.lang.reflect.Type; - -/** - * This class contains a mapping of all the application specific - * {@link InstanceCreator} instances. Registering an {@link InstanceCreator} - * with this class will override the default object creation that is defined - * by the ObjectConstructor that this class is wrapping. Using this class - * with the JSON framework provides the application with "pluggable" modules - * to customize framework to suit the application's needs. - * - * @author Joel Leitch - */ -final class MappedObjectConstructor implements ObjectConstructor { - private static final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); - private static final DefaultConstructorAllocator defaultConstructorAllocator = - new DefaultConstructorAllocator(500); - - private final ParameterizedTypeHandlerMap> instanceCreatorMap; - - public MappedObjectConstructor( - ParameterizedTypeHandlerMap> instanceCreators) { - instanceCreatorMap = instanceCreators; - } - - @SuppressWarnings("unchecked") - public T construct(Type typeOfT) { - InstanceCreator creator = (InstanceCreator) instanceCreatorMap.getHandlerFor(typeOfT, false); - if (creator != null) { - return creator.createInstance(typeOfT); - } - return (T) constructWithAllocators(typeOfT); - } - - public Object constructArray(Type type, int length) { - return Array.newInstance($Gson$Types.getRawType(type), length); - } - - @SuppressWarnings({"unchecked", "cast"}) - private T constructWithAllocators(Type typeOfT) { - try { - Class clazz = (Class) $Gson$Types.getRawType(typeOfT); - T obj = defaultConstructorAllocator.newInstance(clazz); - return (obj == null) - ? unsafeAllocator.newInstance(clazz) - : obj; - } catch (Exception e) { - throw new RuntimeException(("Unable to invoke no-args constructor for " + typeOfT + ". " - + "Register an InstanceCreator with Gson for this type may fix this problem."), e); - } - } - - @Override - public String toString() { - return instanceCreatorMap.toString(); - } -} diff --git a/gson/src/main/java/com/google/gson/MemoryRefStack.java b/gson/src/main/java/com/google/gson/MemoryRefStack.java deleted file mode 100644 index 3fbd8463..00000000 --- a/gson/src/main/java/com/google/gson/MemoryRefStack.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Preconditions; - -import java.util.Stack; - -/** - * A stack data structure that only does a memory reference comparison - * when looking for a particular item in the stack. This stack does - * not allow {@code null} values to be added. - * - * @author Joel Leitch - */ -final class MemoryRefStack { - private final Stack stack = new Stack(); - - /** - * Adds a new element to the top of the stack. - * - * @param obj the object to add to the stack - * @return the object that was added - */ - public ObjectTypePair push(ObjectTypePair obj) { - $Gson$Preconditions.checkNotNull(obj); - return stack.push(obj); - } - - /** - * Removes the top element from the stack. - * - * @return the element being removed from the stack - * @throws java.util.EmptyStackException thrown if the stack is empty - */ - public ObjectTypePair pop() { - return stack.pop(); - } - - public boolean isEmpty() { - return stack.isEmpty(); - } - - /** - * Retrieves the item from the top of the stack, but does not remove it. - * - * @return the item from the top of the stack - * @throws java.util.EmptyStackException thrown if the stack is empty - */ - public ObjectTypePair peek() { - return stack.peek(); - } - - /** - * Performs a memory reference check to see it the {@code obj} exists in - * the stack. - * - * @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(ObjectTypePair obj) { - if (obj == null) { - return false; - } - - for (ObjectTypePair stackObject : stack) { - if (stackObject.getObject() == obj.getObject() - && stackObject.type.equals(obj.type) ) { - return true; - } - } - return false; - } -} diff --git a/gson/src/main/java/com/google/gson/NullExclusionStrategy.java b/gson/src/main/java/com/google/gson/NullExclusionStrategy.java deleted file mode 100644 index b1de1253..00000000 --- a/gson/src/main/java/com/google/gson/NullExclusionStrategy.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.google.gson; - -/** - * This acts as a "Null Object" pattern for the {@link ExclusionStrategy}. - * Passing an instance of this class into the {@link ObjectNavigator} will - * make the {@link ObjectNavigator} parse/visit every field of the object - * being navigated. - * - * @author Joel Leitch - */ -final class NullExclusionStrategy implements ExclusionStrategy { - - public boolean shouldSkipField(FieldAttributes f) { - return false; - } - - public boolean shouldSkipClass(Class clazz) { - return false; - } -} diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java deleted file mode 100644 index 860cc25e..00000000 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.google.gson; - -import com.google.gson.internal.$Gson$Types; - -import java.lang.reflect.Type; - -/** - * 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(); - } -} diff --git a/gson/src/main/java/com/google/gson/ObjectTypePair.java b/gson/src/main/java/com/google/gson/ObjectTypePair.java deleted file mode 100644 index 3e85eae7..00000000 --- a/gson/src/main/java/com/google/gson/ObjectTypePair.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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; - -/** - * A holder class for an object and its type - * - * @author Inderjeet Singh - */ -final class ObjectTypePair { - private Object obj; - final Type type; - private final boolean preserveType; - private final boolean systemOnly; - - ObjectTypePair(Object obj, Type type, boolean preserveType, boolean systemOnly) { - this.obj = obj; - this.type = type; - this.preserveType = preserveType; - this.systemOnly = systemOnly; - } - - Object getObject() { - return obj; - } - - void setObject(Object obj) { - this.obj = obj; - } - - Type getType() { - return type; - } - - @Override - public String toString() { - return String.format("preserveType: %b, type: %s, obj: %s", preserveType, type, obj); - } - - Pair getMatchingHandler( - ParameterizedTypeHandlerMap handlers) { - HANDLER handler = null; - if (!preserveType && obj != null) { - // First try looking up the handler for the actual type - ObjectTypePair moreSpecificType = toMoreSpecificType(); - handler = handlers.getHandlerFor(moreSpecificType.type, systemOnly); - if (handler != null) { - return new Pair(handler, moreSpecificType); - } - } - // Try the specified type - handler = handlers.getHandlerFor(type, systemOnly); - return handler == null ? null : new Pair(handler, this); - } - - ObjectTypePair toMoreSpecificType() { - if (preserveType || obj == null) { - return this; - } - Type actualType = getActualTypeIfMoreSpecific(type, obj.getClass()); - if (actualType == type) { - return this; - } - return new ObjectTypePair(obj, actualType, true, systemOnly); - } - - Type getMoreSpecificType() { - if (preserveType || obj == null) { - return type; - } - return getActualTypeIfMoreSpecific(type, obj.getClass()); - } - - // This takes care of situations where the field was declared as an Object, but the - // actual value contains something more specific. See Issue 54. - // TODO (inder): This solution will not work if the field is of a generic type, but - // the actual object is of a raw type (which is a sub-class of the generic type). - static Type getActualTypeIfMoreSpecific(Type type, Class actualClass) { - if (type instanceof Class) { - Class typeAsClass = (Class) type; - if (typeAsClass.isAssignableFrom(actualClass)) { - type = actualClass; - } - if (type == Object.class) { - type = actualClass; - } - } - return type; - } - - @Override - public int hashCode() { - // 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) ? 31 : obj.hashCode()); - } - - @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 != other.obj) { // Checking for reference equality - return false; - } - if (type == null) { - if (other.type != null) { - return false; - } - } else if (!type.equals(other.type)) { - return false; - } - return preserveType == other.preserveType && systemOnly == other.systemOnly; - } - - public boolean isPreserveType() { - return preserveType; - } - - public boolean isSystemOnly() { - return systemOnly; - } -} diff --git a/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java b/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java deleted file mode 100644 index 863fc790..00000000 --- a/gson/src/main/java/com/google/gson/ReflectingFieldNavigator.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.ObjectNavigator.Visitor; -import com.google.gson.internal.$Gson$Preconditions; -import com.google.gson.internal.$Gson$Types; - -/** - * Visits each of the fields of the specified class using reflection - * - * @author Inderjeet Singh - * @author Joel Leitch - * @author Jesse Wilson - */ -final class ReflectingFieldNavigator { - - private static final Cache> fieldsCache = - new LruCache>(500); - - private final ExclusionStrategy exclusionStrategy; - - /** - * @param exclusionStrategy the concrete strategy object to be used to filter out fields of an - * object. - */ - ReflectingFieldNavigator(ExclusionStrategy exclusionStrategy) { - this.exclusionStrategy = $Gson$Preconditions.checkNotNull(exclusionStrategy); - } - - /** - * @param objTypePair The object,type (fully genericized) being navigated - * @param visitor the visitor to visit each field with - */ - void visitFieldsReflectively(ObjectTypePair objTypePair, Visitor visitor) { - Type moreSpecificType = objTypePair.getMoreSpecificType(); - Object obj = objTypePair.getObject(); - for (FieldAttributes fieldAttributes : getAllFields(moreSpecificType, objTypePair.getType())) { - if (exclusionStrategy.shouldSkipField(fieldAttributes) - || exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) { - continue; // skip - } - Type resolvedTypeOfField = getMoreSpecificType(fieldAttributes.getResolvedType(), obj, fieldAttributes); - boolean visitedWithCustomHandler = - visitor.visitFieldUsingCustomHandler(fieldAttributes, resolvedTypeOfField, obj); - if (!visitedWithCustomHandler) { - if ($Gson$Types.isArray(resolvedTypeOfField)) { - visitor.visitArrayField(fieldAttributes, resolvedTypeOfField, obj); - } else { - visitor.visitObjectField(fieldAttributes, resolvedTypeOfField, obj); - } - } - } - } - - @SuppressWarnings("unchecked") - private static Type getMoreSpecificType(Type type, Object obj, FieldAttributes fieldAttributes) { - try { - if (obj != null && (Object.class == type || type instanceof TypeVariable)) { - Object fieldValue = fieldAttributes.get(obj); - if (fieldValue != null) { - type = fieldValue.getClass(); - } - } - } catch (IllegalAccessException e) { - } - return type; - } - - private List getAllFields(Type type, Type declaredType) { - List fields = fieldsCache.getElement(type); - if (fields == null) { - fields = new ArrayList(); - for (Class curr : getInheritanceHierarchy(type)) { - Field[] currentClazzFields = curr.getDeclaredFields(); - AccessibleObject.setAccessible(currentClazzFields, true); - Field[] classFields = currentClazzFields; - for (Field f : classFields) { - fields.add(new FieldAttributes(curr, f, declaredType)); - } - } - fieldsCache.addElement(type, fields); - } - return fields; - } - - /** - * Returns a list of classes corresponding to the inheritance of specified type - */ - private List> getInheritanceHierarchy(Type type) { - List> classes = new ArrayList>(); - Class topLevelClass = $Gson$Types.getRawType(type); - for (Class curr = topLevelClass; curr != null && !curr.equals(Object.class); curr = - curr.getSuperclass()) { - if (!curr.isSynthetic()) { - classes.add(curr); - } - } - return classes; - } -} diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java new file mode 100644 index 00000000..b3c6db33 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java @@ -0,0 +1,173 @@ +/* + * 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.internal; + +import com.google.gson.InstanceCreator; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Returns a function that can construct an instance of a requested type. + */ +public final class ConstructorConstructor { + private final ParameterizedTypeHandlerMap> instanceCreators; + + public ConstructorConstructor(ParameterizedTypeHandlerMap> instanceCreators) { + this.instanceCreators = instanceCreators; + } + + public ConstructorConstructor() { + this(new ParameterizedTypeHandlerMap>()); + } + + public ObjectConstructor getConstructor(TypeToken typeToken) { + final Type type = typeToken.getType(); + final Class rawType = typeToken.getRawType(); + + // first try an instance creator + + @SuppressWarnings("unchecked") // types must agree + final InstanceCreator creator + = (InstanceCreator) instanceCreators.getHandlerFor(type, false); + if (creator != null) { + return new ObjectConstructor() { + @Override public T construct() { + return creator.createInstance(type); + } + }; + } + + ObjectConstructor defaultConstructor = newDefaultConstructor(rawType); + if (defaultConstructor != null) { + return defaultConstructor; + } + + ObjectConstructor defaultImplementation = newDefaultImplementationConstructor(rawType); + if (defaultImplementation != null) { + return defaultImplementation; + } + + // finally try unsafe + return newUnsafeAllocator(type, rawType); + } + + private ObjectConstructor newDefaultConstructor(Class rawType) { + try { + final Constructor constructor = rawType.getDeclaredConstructor(); + if (!constructor.isAccessible()) { + constructor.setAccessible(true); + } + return new ObjectConstructor() { + @SuppressWarnings("unchecked") // T is the same raw type as is requested + @Override public T construct() { + try { + Object[] args = null; + return (T) constructor.newInstance(args); + } catch (InstantiationException e) { + // TODO: JsonParseException ? + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + // TODO: don't wrap if cause is unchecked! + // TODO: JsonParseException ? + throw new RuntimeException(e.getTargetException()); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + }; + } catch (NoSuchMethodException e) { + return null; + } + } + + /** + * Constructors for common interface types like Map and List and their + * subytpes. + */ + @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is + private ObjectConstructor newDefaultImplementationConstructor(Class rawType) { + if (Collection.class.isAssignableFrom(rawType)) { + if (SortedSet.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + @Override public T construct() { + return (T) new TreeSet(); + } + }; + } else if (Set.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + @Override public T construct() { + return (T) new LinkedHashSet(); + } + }; + } else if (Queue.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + @Override public T construct() { + return (T) new LinkedList(); + } + }; + } else { + return new ObjectConstructor() { + @Override public T construct() { + return (T) new ArrayList(); + } + }; + } + } + + if (Map.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + @Override public T construct() { + return (T) new LinkedHashMap(); + } + }; + // TODO: SortedMap ? + } + + return null; + } + + private ObjectConstructor newUnsafeAllocator( + final Type type, final Class rawType) { + return new ObjectConstructor() { + private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); + @Override public T construct() { + try { + return (T) unsafeAllocator.newInstance(rawType); + } catch (Exception e) { + throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". " + + "Register an InstanceCreator with Gson for this type may fix this problem."), e); + } + } + }; + } + + @Override public String toString() { + return instanceCreators.toString(); + } +} diff --git a/gson/src/main/java/com/google/gson/ObjectConstructor.java b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java similarity index 60% rename from gson/src/main/java/com/google/gson/ObjectConstructor.java rename to gson/src/main/java/com/google/gson/internal/ObjectConstructor.java index 67973e9f..6ef20607 100644 --- a/gson/src/main/java/com/google/gson/ObjectConstructor.java +++ b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.gson; - -import java.lang.reflect.Type; +package com.google.gson.internal; /** * Defines a generic object construction factory. The purpose of this class @@ -26,22 +24,10 @@ import java.lang.reflect.Type; * @author Inderjeet Singh * @author Joel Leitch */ -interface ObjectConstructor { +public interface ObjectConstructor { /** - * Creates a new instance of the given type. - * - * @param typeOfT the class type that should be instantiated - * @return a default instance of the provided class. + * Returns a new instance. */ - public T construct(Type typeOfT); - - /** - * Constructs an array type of the provided length. - * - * @param typeOfArrayElements type of objects in the array - * @param length size of the array - * @return new array of size length - */ - public Object constructArray(Type typeOfArrayElements, int length); + public T construct(); } \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/Pair.java b/gson/src/main/java/com/google/gson/internal/Pair.java similarity index 95% rename from gson/src/main/java/com/google/gson/Pair.java rename to gson/src/main/java/com/google/gson/internal/Pair.java index ce5496f1..205ae295 100644 --- a/gson/src/main/java/com/google/gson/Pair.java +++ b/gson/src/main/java/com/google/gson/internal/Pair.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gson; +package com.google.gson.internal; /** * A simple object that holds onto a pair of object references, first and second. @@ -25,7 +25,7 @@ package com.google.gson; * @param * @param */ -final class Pair { +public final class Pair { public final FIRST first; public final SECOND second; diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/internal/ParameterizedTypeHandlerMap.java similarity index 98% rename from gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java rename to gson/src/main/java/com/google/gson/internal/ParameterizedTypeHandlerMap.java index 7add6ad4..0bb27447 100644 --- a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java +++ b/gson/src/main/java/com/google/gson/internal/ParameterizedTypeHandlerMap.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.gson; - -import com.google.gson.internal.$Gson$Types; +package com.google.gson.internal; import java.lang.reflect.Type; import java.util.ArrayList; @@ -35,7 +33,7 @@ import java.util.logging.Logger; * * @param The handler that will be looked up by type */ -final class ParameterizedTypeHandlerMap { +public final class ParameterizedTypeHandlerMap { private static final Logger logger = Logger.getLogger(ParameterizedTypeHandlerMap.class.getName()); /** @@ -222,7 +220,7 @@ final class ParameterizedTypeHandlerMap { ParameterizedTypeHandlerMap copy = new ParameterizedTypeHandlerMap(); // Instead of individually registering entries in the map, make an efficient copy // of the list and map - + // TODO (inder): Performance optimization. We can probably just share the // systemMap and systemTypeHierarchyList instead of making copies copy.systemMap.putAll(systemMap); diff --git a/gson/src/main/java/com/google/gson/Primitives.java b/gson/src/main/java/com/google/gson/internal/Primitives.java similarity index 98% rename from gson/src/main/java/com/google/gson/Primitives.java rename to gson/src/main/java/com/google/gson/internal/Primitives.java index 446890d4..c9285470 100644 --- a/gson/src/main/java/com/google/gson/Primitives.java +++ b/gson/src/main/java/com/google/gson/internal/Primitives.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gson; +package com.google.gson.internal; import com.google.gson.internal.$Gson$Preconditions; @@ -29,7 +29,7 @@ import java.util.Map; * * @author Kevin Bourrillion */ -final class Primitives { +public final class Primitives { private Primitives() {} /** A map from primitive types to their corresponding wrapper types. */ diff --git a/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java index 30ea9ea7..8d6fb881 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java @@ -28,7 +28,7 @@ import java.math.BigDecimal; * * @author Joel Leitch */ -public class BigDecimalTypeAdapter extends TypeAdapter { +public final class BigDecimalTypeAdapter extends TypeAdapter { @Override public BigDecimal read(JsonReader reader) throws IOException { diff --git a/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java index 9e0bfd98..f103ae8a 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java @@ -28,7 +28,7 @@ import java.math.BigInteger; * * @author Joel Leitch */ -public class BigIntegerTypeAdapter extends TypeAdapter { +public final class BigIntegerTypeAdapter extends TypeAdapter { @Override public BigInteger read(JsonReader reader) throws IOException { diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapter.java deleted file mode 100644 index 01747b51..00000000 --- a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapter.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.internal.bind; - -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import com.google.gson.internal.$Gson$Types; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * Adapt a homogeneous collection of objects. - */ -public final class CollectionTypeAdapter extends TypeAdapter> { - public static final Factory FACTORY = new Factory() { - public TypeAdapter create(MiniGson context, TypeToken typeToken) { - Type type = typeToken.getType(); - - Class rawType = typeToken.getRawType(); - if (!Collection.class.isAssignableFrom(rawType)) { - return null; - } - - Type elementType = $Gson$Types.getCollectionElementType(type, rawType); - TypeAdapter elementTypeAdapter = context.getAdapter(TypeToken.get(elementType)); - - Class constructorType; - - if (rawType == List.class || rawType == Collection.class) { - constructorType = ArrayList.class; - } else if (rawType == Set.class) { - constructorType = LinkedHashSet.class; - } else if (rawType == Queue.class) { - constructorType = LinkedList.class; - } else if (rawType == SortedSet.class) { - constructorType = TreeSet.class; - } else { - constructorType = rawType; - } - - Constructor constructor = null; - try { - constructor = constructorType.getConstructor(); - } catch (NoSuchMethodException ignored) { - } - - @SuppressWarnings("unchecked") // create() doesn't define a type parameter - TypeAdapter result = new CollectionTypeAdapter(context, elementType, elementTypeAdapter, constructor); - return result; - } - }; - - private final TypeAdapter elementTypeAdapter; - private final Constructor> constructor; - - public CollectionTypeAdapter(MiniGson context, Type elementType, TypeAdapter elementTypeAdapter, - Constructor> constructor) { - this.elementTypeAdapter = - new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType); - this.constructor = constructor; - } - - public Collection read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); // TODO: does this belong here? - return null; - } - - if (constructor == null) { - throw new UnsupportedOperationException("TODO: use unsafeAllocator.newInstance"); - } - Collection collection = Reflection.newInstance(constructor); - reader.beginArray(); - while (reader.hasNext()) { - E instance = elementTypeAdapter.read(reader); - collection.add(instance); - } - reader.endArray(); - return collection; - } - - public void write(JsonWriter writer, Collection collection) throws IOException { - if (collection == null) { - writer.nullValue(); // TODO: better policy here? - return; - } - - writer.beginArray(); - for (E element : collection) { - elementTypeAdapter.write(writer, element); - } - writer.endArray(); - } -} diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java new file mode 100644 index 00000000..84871cf6 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java @@ -0,0 +1,98 @@ +/* + * 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.internal.bind; + +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Collection; + +/** + * Adapt a homogeneous collection of objects. + */ +public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { + private final ConstructorConstructor constructorConstructor; + + public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + this.constructorConstructor = constructorConstructor; + } + + public TypeAdapter create(MiniGson context, TypeToken typeToken) { + Type type = typeToken.getType(); + + Class rawType = typeToken.getRawType(); + if (!Collection.class.isAssignableFrom(rawType)) { + return null; + } + + Type elementType = $Gson$Types.getCollectionElementType(type, rawType); + TypeAdapter elementTypeAdapter = context.getAdapter(TypeToken.get(elementType)); + ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); + + @SuppressWarnings("unchecked") // create() doesn't define a type parameter + TypeAdapter result = new Adapter(context, elementType, elementTypeAdapter, constructor); + return result; + } + + private final class Adapter extends TypeAdapter> { + private final TypeAdapter elementTypeAdapter; + private final ObjectConstructor> constructor; + + public Adapter(MiniGson context, Type elementType, + TypeAdapter elementTypeAdapter, + ObjectConstructor> constructor) { + this.elementTypeAdapter = + new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType); + this.constructor = constructor; + } + + public Collection read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } + + Collection collection = constructor.construct(); + reader.beginArray(); + while (reader.hasNext()) { + E instance = elementTypeAdapter.read(reader); + collection.add(instance); + } + reader.endArray(); + return collection; + } + + public void write(JsonWriter writer, Collection collection) throws IOException { + if (collection == null) { + writer.nullValue(); // TODO: better policy here? + return; + } + + writer.beginArray(); + for (E element : collection) { + elementTypeAdapter.write(writer, element); + } + writer.endArray(); + } + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java new file mode 100644 index 00000000..1e253556 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java @@ -0,0 +1,79 @@ +/* + * 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.internal.bind; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; + +/** + * This type adapter skips values using an exclusion strategy. It may delegate + * to another type adapter if only one direction is excluded. + */ +public final class ExcludedTypeAdapterFactory implements TypeAdapter.Factory { + private final ExclusionStrategy serializationExclusionStrategy; + private final ExclusionStrategy deserializationExclusionStrategy; + + public ExcludedTypeAdapterFactory(ExclusionStrategy serializationExclusionStrategy, + ExclusionStrategy deserializationExclusionStrategy) { + this.serializationExclusionStrategy = serializationExclusionStrategy; + this.deserializationExclusionStrategy = deserializationExclusionStrategy; + } + + public TypeAdapter create(final MiniGson context, final TypeToken type) { + Class rawType = type.getRawType(); + final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType); + final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType); + + if (!skipSerialize && !skipDeserialize) { + return null; + } + + return new TypeAdapter() { + /** + * The delegate is lazily created because it may not be needed, and + * creating it may fail. + */ + private TypeAdapter delegate; + + @Override public T read(JsonReader reader) throws IOException { + if (skipDeserialize) { + reader.skipValue(); + return null; + } + return delegate().read(reader); + } + + @Override public void write(JsonWriter writer, T value) throws IOException { + if (skipSerialize) { + writer.nullValue(); + return; + } + delegate().write(writer, value); + } + + private TypeAdapter delegate() { + TypeAdapter d = delegate; + return d != null + ? d + : (delegate = context.getNextAdapter(ExcludedTypeAdapterFactory.this, type)); + } + }; + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/GsonCompatibleMapTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/GsonCompatibleMapTypeAdapter.java deleted file mode 100644 index f0567380..00000000 --- a/gson/src/main/java/com/google/gson/internal/bind/GsonCompatibleMapTypeAdapter.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.internal.bind; - -import com.google.gson.internal.$Gson$Types; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Adapt a map whose keys are any type. - */ -public final class GsonCompatibleMapTypeAdapter extends TypeAdapter> { - public static final Factory FACTORY = new Factory() { - public TypeAdapter create(MiniGson context, TypeToken typeToken) { - Type type = typeToken.getType(); - - Class rawType = typeToken.getRawType(); - if (!Map.class.isAssignableFrom(rawType)) { - return null; - } - - Type childGenericType = Object.class; - if (type instanceof ParameterizedType) { - Class rawTypeOfSrc = $Gson$Types.getRawType(type); - childGenericType = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc)[1]; - } - TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(childGenericType)); - - Constructor constructor; - try { - Class constructorType = (rawType == Map.class) ? LinkedHashMap.class : rawType; - constructor = constructorType.getConstructor(); - } catch (NoSuchMethodException e) { - return null; - } - - @SuppressWarnings("unchecked") // we don't define a type parameter for the key or value types - TypeAdapter result = new GsonCompatibleMapTypeAdapter(valueAdapter, constructor); - return result; - } - }; - - private final TypeAdapter valueTypeAdapter; - private final Constructor> constructor; - - public GsonCompatibleMapTypeAdapter(TypeAdapter valueTypeAdapter, - Constructor> constructor) { - this.valueTypeAdapter = valueTypeAdapter; - this.constructor = constructor; - } - - public Map read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); // TODO: does this belong here? - return null; - } - - Map map = Reflection.newInstance(constructor); - reader.beginObject(); - while (reader.hasNext()) { - String key = reader.nextName(); - V value = valueTypeAdapter.read(reader); - map.put(key, value); // TODO: convert to the map's key type? - } - reader.endObject(); - return map; - } - - public void write(JsonWriter writer, Map map) throws IOException { - if (map == null) { - writer.nullValue(); // TODO: better policy here? - return; - } - - writer.beginObject(); - for (Map.Entry entry : map.entrySet()) { - String key = String.valueOf(entry.getKey()); - writer.name(key); - valueTypeAdapter.write(writer, entry.getValue()); - } - writer.endObject(); - } -} diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java new file mode 100644 index 00000000..c8e6c664 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java @@ -0,0 +1,101 @@ +/* + * 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.internal.bind; + +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * Adapt a map whose keys are any type. + */ +public final class MapTypeAdapterFactory implements TypeAdapter.Factory { + private final ConstructorConstructor constructorConstructor; + + public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + this.constructorConstructor = constructorConstructor; + } + + public TypeAdapter create(MiniGson context, TypeToken typeToken) { + Type type = typeToken.getType(); + + Class rawType = typeToken.getRawType(); + if (!Map.class.isAssignableFrom(rawType)) { + return null; + } + + Class rawTypeOfSrc = $Gson$Types.getRawType(type); + Type childGenericType = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc)[1]; + TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(childGenericType)); + ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); + + @SuppressWarnings("unchecked") // we don't define a type parameter for the key or value types + TypeAdapter result = new Adapter(valueAdapter, constructor); + return result; + } + + private final class Adapter extends TypeAdapter> { + private final TypeAdapter valueTypeAdapter; + private final ObjectConstructor> constructor; + + public Adapter(TypeAdapter valueTypeAdapter, + ObjectConstructor> constructor) { + this.valueTypeAdapter = valueTypeAdapter; + this.constructor = constructor; + } + + public Map read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } + + Map map = constructor.construct(); + + reader.beginObject(); + while (reader.hasNext()) { + String key = reader.nextName(); + V value = valueTypeAdapter.read(reader); + map.put(key, value); // TODO: convert to the map's key type? + } + reader.endObject(); + return map; + } + + public void write(JsonWriter writer, Map map) throws IOException { + if (map == null) { + writer.nullValue(); // TODO: better policy here? + return; + } + + writer.beginObject(); + for (Map.Entry entry : map.entrySet()) { + String key = String.valueOf(entry.getKey()); + writer.name(key); + valueTypeAdapter.write(writer, entry.getValue()); + } + writer.endObject(); + } + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java b/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java index 79f7a01b..bb73efc9 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java +++ b/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java @@ -16,6 +16,10 @@ package com.google.gson.internal.bind; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -23,10 +27,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - /** * A basic binding between JSON and Java objects. */ @@ -48,6 +48,7 @@ public final class MiniGson { private final List factories; private MiniGson(Builder builder) { + ConstructorConstructor constructorConstructor = new ConstructorConstructor(); List factories = new ArrayList(); if (builder.addDefaultFactories) { factories.add(TypeAdapters.BOOLEAN_FACTORY); @@ -59,11 +60,11 @@ public final class MiniGson { } factories.addAll(builder.factories); if (builder.addDefaultFactories) { - factories.add(CollectionTypeAdapter.FACTORY); - factories.add(StringToValueMapTypeAdapter.FACTORY); + factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); + factories.add(new StringToValueMapTypeAdapterFactory(constructorConstructor)); factories.add(ArrayTypeAdapter.FACTORY); factories.add(ObjectTypeAdapter.FACTORY); - factories.add(ReflectiveTypeAdapter.FACTORY); + factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor)); } this.factories = Collections.unmodifiableList(factories); } @@ -100,6 +101,33 @@ public final class MiniGson { } } + /** + * Returns a type adapter for {@code} type that isn't {@code skipPast}. This + * can be used for type adapters to compose other, simpler type adapters. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public TypeAdapter getNextAdapter(TypeAdapter.Factory skipPast, TypeToken type) { + boolean skipPastFound = false; + + for (TypeAdapter.Factory factory : factories) { + if (!skipPastFound) { + if (factory == skipPast) { + skipPastFound = true; + } + continue; + } + + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + return candidate; + } + } + + throw new IllegalArgumentException("This MiniGSON cannot serialize " + type); + } + static class FutureTypeAdapter extends TypeAdapter { private TypeAdapter delegate; diff --git a/gson/src/main/java/com/google/gson/internal/bind/Reflection.java b/gson/src/main/java/com/google/gson/internal/bind/Reflection.java index 77b66eb9..a901ad11 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/Reflection.java +++ b/gson/src/main/java/com/google/gson/internal/bind/Reflection.java @@ -15,8 +15,6 @@ */ package com.google.gson.internal.bind; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -27,29 +25,8 @@ final class Reflection { public static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { if (value != null && (type == Object.class || type instanceof TypeVariable || type instanceof Class)) { - type = (Class) value.getClass(); + type = value.getClass(); } return type; } - - // TODO: this should use Joel's unsafe constructor stuff - public static T newInstance(Constructor constructor) { - if (!constructor.isAccessible()) { - constructor.setAccessible(true); - } - try { - Object[] args = null; - return constructor.newInstance(args); - } catch (InstantiationException e) { - // TODO: JsonParseException ? - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - // TODO: don't wrap if cause is unchecked! - // TODO: JsonParseException ? - throw new RuntimeException(e.getTargetException()); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - } diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapter.java deleted file mode 100644 index 117c8773..00000000 --- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapter.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.internal.bind; - -import com.google.gson.JsonSyntaxException; -import java.io.IOException; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.google.gson.internal.$Gson$Types; -import com.google.gson.internal.UnsafeAllocator; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * Adapts the fields of an object to the properties of a JSON object. - */ -public final class ReflectiveTypeAdapter extends TypeAdapter { - public static final Factory FACTORY = new FactoryImpl(); - - private static final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); - private final Class rawType; - private final Constructor constructor; - private final Map map; - private final BoundField[] boundFields; - - ReflectiveTypeAdapter(Class rawType, Constructor constructor, Map map) { - this.rawType = rawType; - this.constructor = constructor; - this.map = map; - this.boundFields = map.values().toArray(new BoundField[map.size()]); - } - - @SuppressWarnings("unchecked") // the '? super T' is a raw T (the only kind we can construct) - public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); // TODO: does this belong here? - return null; - } - - T instance; - if (constructor != null) { - instance = (T) Reflection.newInstance(constructor); - } else { - try { - instance = (T) unsafeAllocator.newInstance(rawType); - } catch (Exception e) { - throw new RuntimeException(("Unable to invoke no-args constructor for " + rawType.getName() - + ". Register an InstanceCreator with Gson for this type may fix this problem."), e); - } - } - - // TODO: null out the other fields? - - try { - reader.beginObject(); - while (reader.hasNext()) { - String name = reader.nextName(); - BoundField field = map.get(name); - if (field == null || !field.deserialized) { - // TODO: define a better policy - reader.skipValue(); - } else { - field.read(reader, instance); - } - } - } catch (IllegalStateException e) { - throw new JsonSyntaxException(e); - } catch (IllegalAccessException e) { - throw new AssertionError(); - } - reader.endObject(); - return instance; - } - - public void write(JsonWriter writer, T value) throws IOException { - if (value == null) { - writer.nullValue(); // TODO: better policy here? - return; - } - - writer.beginObject(); - try { - for (BoundField boundField : boundFields) { - if (boundField.serialized) { - writer.name(boundField.name); - boundField.write(writer, value); - } - } - } catch (IllegalAccessException e) { - throw new AssertionError(); - } - writer.endObject(); - } - - static BoundField createBoundField( - final MiniGson context, final Field field, final String name, - final TypeToken fieldType, boolean serialize, boolean deserialize) { - // special casing primitives here saves ~5% on Android... - return new BoundField(name, serialize, deserialize) { - final TypeAdapter typeAdapter = context.getAdapter(fieldType); - @SuppressWarnings("unchecked") // the type adapter and field type always agree - @Override void write(JsonWriter writer, Object value) - throws IOException, IllegalAccessException { - Object fieldValue = field.get(value); - TypeAdapter t = - new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); - t.write(writer, fieldValue); - } - @Override void read(JsonReader reader, Object value) - throws IOException, IllegalAccessException { - Object fieldValue = typeAdapter.read(reader); - field.set(value, fieldValue); - } - }; - } - - public static class FactoryImpl implements Factory { - public boolean serializeField(Class declaringClazz, Field f, Type declaredType) { - return !f.isSynthetic(); - } - public boolean deserializeField(Class declaringClazz, Field f, Type declaredType) { - return !f.isSynthetic(); - } - - public String getFieldName(Class declaringClazz, Field f, Type declaredType) { - return f.getName(); - } - - public TypeAdapter create(MiniGson context, TypeToken type) { - Class raw = type.getRawType(); - - if (!Object.class.isAssignableFrom(raw)) { - return null; // it's a primitive! - } - - Constructor constructor = null; - try { - constructor = raw.getDeclaredConstructor(); - } catch (NoSuchMethodException ignored) { - } - - return new ReflectiveTypeAdapter(raw, constructor, getBoundFields(context, type, raw)); - } - - private Map getBoundFields( - MiniGson context, TypeToken type, Class raw) { - Map result = new LinkedHashMap(); - if (raw.isInterface()) { - return result; - } - - Type declaredType = type.getType(); - while (raw != Object.class) { - Field[] fields = raw.getDeclaredFields(); - AccessibleObject.setAccessible(fields, true); - for (Field field : fields) { - boolean serialize = serializeField(raw, field, declaredType); - boolean deserialize = deserializeField(raw, field, declaredType); - if (serialize || deserialize) { - Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); - BoundField boundField = createBoundField(context, field, getFieldName(raw, field, declaredType), - TypeToken.get(fieldType), serialize, deserialize); - BoundField previous = result.put(boundField.name, boundField); - if (previous != null) { - throw new IllegalArgumentException(declaredType - + " declares multiple JSON fields named " + previous.name); - } - } - } - type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); - raw = type.getRawType(); - } - return result; - } - } - - static abstract class BoundField { - final String name; - final boolean serialized; - final boolean deserialized; - - protected BoundField(String name, boolean serialized, boolean deserialized) { - this.name = name; - this.serialized = serialized; - this.deserialized = deserialized; - } - - abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException; - abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException; - } -} diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java new file mode 100644 index 00000000..05813aea --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -0,0 +1,219 @@ +/* + * 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.internal.bind; + +import com.google.gson.JsonSyntaxException; +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.internal.Primitives; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Type adapter that reflects over the fields and methods of a class. + */ +public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { + private final ConstructorConstructor constructorConstructor; + + public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + this.constructorConstructor = constructorConstructor; + } + + protected boolean serializeField(Class declaringClazz, Field f, Type declaredType) { + return !f.isSynthetic(); + } + + protected boolean deserializeField(Class declaringClazz, Field f, Type declaredType) { + return !f.isSynthetic(); + } + + protected String getFieldName(Class declaringClazz, Field f, Type declaredType) { + return f.getName(); + } + + public TypeAdapter create(MiniGson context, final TypeToken type) { + Class raw = type.getRawType(); + + if (!Object.class.isAssignableFrom(raw)) { + return null; // it's a primitive! + } + + ObjectConstructor constructor = constructorConstructor.getConstructor(type); + + return new Adapter(context, constructor, type, + getBoundFields(context, type, raw)); + } + + private ReflectiveTypeAdapterFactory.BoundField createBoundField( + final MiniGson context, final Field field, final String name, + final TypeToken fieldType, boolean serialize, boolean deserialize) { + final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); + + // special casing primitives here saves ~5% on Android... + return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { + final TypeAdapter typeAdapter = context.getAdapter(fieldType); + @SuppressWarnings("unchecked") // the type adapter and field type always agree + @Override void write(JsonWriter writer, Object value) + throws IOException, IllegalAccessException { + Object fieldValue = field.get(value); + TypeAdapter t = + new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); + t.write(writer, fieldValue); + } + @Override void read(JsonReader reader, Object value) + throws IOException, IllegalAccessException { + Object fieldValue = typeAdapter.read(reader); + if (fieldValue != null || !isPrimitive) { + field.set(value, fieldValue); + } + } + }; + } + + private Map getBoundFields( + MiniGson context, TypeToken type, Class raw) { + Map result = new LinkedHashMap(); + if (raw.isInterface()) { + return result; + } + + Type declaredType = type.getType(); + while (raw != Object.class) { + Field[] fields = raw.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field field : fields) { + boolean serialize = serializeField(raw, field, declaredType); + boolean deserialize = deserializeField(raw, field, declaredType); + if (!serialize && !deserialize) { + continue; + } + Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); + BoundField boundField = createBoundField(context, field, getFieldName(raw, field, declaredType), + TypeToken.get(fieldType), serialize, deserialize); + BoundField previous = result.put(boundField.name, boundField); + if (previous != null) { + throw new IllegalArgumentException(declaredType + + " declares multiple JSON fields named " + previous.name); + } + } + type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); + raw = type.getRawType(); + } + return result; + } + + static abstract class BoundField { + final String name; + final boolean serialized; + final boolean deserialized; + + protected BoundField(String name, boolean serialized, boolean deserialized) { + this.name = name; + this.serialized = serialized; + this.deserialized = deserialized; + } + + abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException; + abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException; + } + + public final class Adapter extends TypeAdapter { + private final MiniGson context; + private final ObjectConstructor constructor; + private final TypeToken type; + private final Map boundFields; + + private Adapter(MiniGson context, ObjectConstructor constructor, + TypeToken type, Map boundFields) { + this.context = context; + this.constructor = constructor; + this.type = type; + this.boundFields = boundFields; + } + + @SuppressWarnings("unchecked") // the '? super T' is a raw T (the only kind we can construct) + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } + + T instance = constructor.construct(); + + // TODO: null out the other fields? + + try { + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + BoundField field = boundFields.get(name); + if (field == null || !field.deserialized) { + // TODO: define a better policy + reader.skipValue(); + } else { + field.read(reader, instance); + } + } + } catch (IllegalStateException e) { + throw new JsonSyntaxException(e); + } catch (IllegalAccessException e) { + throw new AssertionError(); + } + reader.endObject(); + return instance; + } + + public void write(JsonWriter writer, T value) throws IOException { + if (value == null) { + writer.nullValue(); // TODO: better policy here? + return; + } + + // TODO: GSON includes subclass fields during serialization + if (false) { + Class runtimeType = value.getClass(); + if (runtimeType != type.getRawType()) { + TypeAdapter adapter = context.getAdapter(runtimeType); + ((TypeAdapter) adapter).write(writer, value); + return; + } + } + + writer.beginObject(); + try { + for (BoundField boundField : boundFields.values()) { + if (boundField.serialized) { + writer.name(boundField.name); + boundField.write(writer, value); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(); + } + writer.endObject(); + } + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapter.java deleted file mode 100644 index 9fbd0cbd..00000000 --- a/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.internal.bind; - -import com.google.gson.internal.$Gson$Types; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Adapt a map whose keys are strings. - */ -public final class StringToValueMapTypeAdapter extends TypeAdapter> { - public static final Factory FACTORY = new Factory() { - public TypeAdapter create(MiniGson context, TypeToken typeToken) { - Type type = typeToken.getType(); - if (!(type instanceof ParameterizedType)) { - return null; - } - - Class rawType = typeToken.getRawType(); - if (!Map.class.isAssignableFrom(rawType)) { - return null; - } - - Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawType); - if (keyAndValueTypes[0] != String.class) { - return null; - } - TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1])); - - Constructor constructor; - try { - Class constructorType = (rawType == Map.class) ? LinkedHashMap.class : rawType; - constructor = constructorType.getConstructor(); - } catch (NoSuchMethodException e) { - return null; - } - - @SuppressWarnings("unchecked") // we don't define a type parameter for the key or value types - TypeAdapter result = new StringToValueMapTypeAdapter(valueAdapter, constructor); - return result; - } - }; - - private final TypeAdapter valueTypeAdapter; - private final Constructor> constructor; - - public StringToValueMapTypeAdapter(TypeAdapter valueTypeAdapter, - Constructor> constructor) { - this.valueTypeAdapter = valueTypeAdapter; - this.constructor = constructor; - } - - public Map read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); // TODO: does this belong here? - return null; - } - - Map map = Reflection.newInstance(constructor); - reader.beginObject(); - while (reader.hasNext()) { - String key = reader.nextName(); - V value = valueTypeAdapter.read(reader); - map.put(key, value); - } - reader.endObject(); - return map; - } - - public void write(JsonWriter writer, Map map) throws IOException { - if (map == null) { - writer.nullValue(); // TODO: better policy here? - return; - } - - writer.beginObject(); - for (Map.Entry entry : map.entrySet()) { - writer.name(entry.getKey()); - valueTypeAdapter.write(writer, entry.getValue()); - } - writer.endObject(); - } -} diff --git a/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java new file mode 100644 index 00000000..02c4c143 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java @@ -0,0 +1,106 @@ +/* + * 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.internal.bind; + +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * Adapt a map whose keys are strings. + */ +public final class StringToValueMapTypeAdapterFactory implements TypeAdapter.Factory { + private final ConstructorConstructor constructorConstructor; + + public StringToValueMapTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + this.constructorConstructor = constructorConstructor; + } + + public TypeAdapter create(MiniGson context, TypeToken typeToken) { + Type type = typeToken.getType(); + if (!(type instanceof ParameterizedType)) { + return null; + } + + Class rawType = typeToken.getRawType(); + if (!Map.class.isAssignableFrom(rawType)) { + return null; + } + + Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawType); + if (keyAndValueTypes[0] != String.class) { + return null; + } + TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1])); + + ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); + + @SuppressWarnings("unchecked") // we don't define a type parameter for the key or value types + TypeAdapter result = new Adapter(valueAdapter, constructor); + return result; + } + + private final class Adapter extends TypeAdapter> { + private final TypeAdapter valueTypeAdapter; + private final ObjectConstructor> constructor; + + public Adapter(TypeAdapter valueTypeAdapter, + ObjectConstructor> constructor) { + this.valueTypeAdapter = valueTypeAdapter; + this.constructor = constructor; + } + + public Map read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); // TODO: does this belong here? + return null; + } + + Map map = constructor.construct(); + reader.beginObject(); + while (reader.hasNext()) { + String key = reader.nextName(); + V value = valueTypeAdapter.read(reader); + map.put(key, value); + } + reader.endObject(); + return map; + } + + public void write(JsonWriter writer, Map map) throws IOException { + if (map == null) { + writer.nullValue(); // TODO: better policy here? + return; + } + + writer.beginObject(); + for (Map.Entry entry : map.entrySet()) { + writer.name(entry.getKey()); + valueTypeAdapter.write(writer, entry.getValue()); + } + writer.endObject(); + } + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java index b005fdaa..f118a484 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java @@ -15,12 +15,11 @@ */ package com.google.gson.internal.bind; -import java.io.IOException; -import java.lang.reflect.Type; - import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; final class TypeAdapterRuntimeTypeWrapper extends TypeAdapter { @@ -52,10 +51,10 @@ final class TypeAdapterRuntimeTypeWrapper extends TypeAdapter { Type runtimeType = Reflection.getRuntimeTypeIfMoreSpecific(type, value); if (runtimeType != type) { TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType)); - if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapter)) { + if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) { // The user registered a type adapter for the runtime type, so we will use that chosen = runtimeTypeAdapter; - } else if (!(delegate instanceof ReflectiveTypeAdapter)) { + } else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) { // The user registered a type adapter for Base class, so we prefer it over the // reflective type adapter for the runtime type chosen = delegate; diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java index 46cbc5c4..d5abfaad 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java @@ -167,10 +167,15 @@ public final class TypeAdapters { public static final TypeAdapter STRING = new TypeAdapter() { public String read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { + JsonToken peek = reader.peek(); + if (peek == JsonToken.NULL) { reader.nextNull(); // TODO: does this belong here? return null; } + /* coerce booleans to strings for backwards compatibility */ + if (peek == JsonToken.BOOLEAN) { + return Boolean.toString(reader.nextBoolean()); + } return reader.nextString(); } public void write(JsonWriter writer, String value) throws IOException { @@ -314,16 +319,6 @@ public final class TypeAdapters { public static final TypeAdapter.Factory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); - public static final TypeAdapter EXCLUDED_TYPE_ADAPTER = new TypeAdapter() { - @Override public Object read(JsonReader reader) throws IOException { - reader.skipValue(); - return null; - } - @Override public void write(JsonWriter writer, Object value) throws IOException { - writer.nullValue(); - } - }; - public static TypeAdapter.Factory newFactory( final TypeToken type, final TypeAdapter typeAdapter) { return new TypeAdapter.Factory() { diff --git a/gson/src/test/java/com/google/gson/DefaultConstructorAllocatorTest.java b/gson/src/test/java/com/google/gson/DefaultConstructorAllocatorTest.java deleted file mode 100644 index c20443b8..00000000 --- a/gson/src/test/java/com/google/gson/DefaultConstructorAllocatorTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * Unit tests for the default constructor allocator class. - * - * @author Joel Leitch - */ -public class DefaultConstructorAllocatorTest extends TestCase { - private DefaultConstructorAllocator allocator; - - @Override - protected void setUp() throws Exception { - super.setUp(); - allocator = new DefaultConstructorAllocator(); - } - - @SuppressWarnings("unchecked") - public void testObjectConstructor() throws Exception { - ArrayList arrayList = allocator.newInstance(ArrayList.class); - assertTrue(arrayList.isEmpty()); - assertTrue(allocator.isInCache(ArrayList.class)); - - LinkedList linkedList = allocator.newInstance(LinkedList.class); - assertTrue(linkedList.isEmpty()); - assertTrue(allocator.isInCache(LinkedList.class)); - } - - public void testMissingDefaultConstructor() throws Exception { - assertNull(allocator.newInstance(NoDefaultConstructor.class)); - assertTrue(allocator.isInCache(NoDefaultConstructor.class)); - } - - private static class NoDefaultConstructor { - @SuppressWarnings("unused") - public NoDefaultConstructor(int i) { - // do nothing - } - } -} diff --git a/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java b/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java index 7210da6f..5c061953 100644 --- a/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java +++ b/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java @@ -17,12 +17,10 @@ package com.google.gson; import com.google.gson.reflect.TypeToken; - -import junit.framework.TestCase; - import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; +import junit.framework.TestCase; /** * Unit test for the default JSON map serialization object located in the @@ -31,17 +29,11 @@ import java.util.Map; * @author Joel Leitch */ public class DefaultMapJsonSerializerTest extends TestCase { - private JsonSerializer> mapSerializer; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mapSerializer = new MapTypeAdapter(); - } + private Gson gson = new Gson(); public void testEmptyMapNoTypeSerialization() { Map emptyMap = new HashMap(); - JsonElement element = mapSerializer.serialize(emptyMap, emptyMap.getClass(), null); + JsonElement element = gson.toJsonTree(emptyMap, emptyMap.getClass()); assertTrue(element instanceof JsonObject); JsonObject emptyMapJsonObject = (JsonObject) element; assertTrue(emptyMapJsonObject.entrySet().isEmpty()); @@ -50,13 +42,13 @@ public class DefaultMapJsonSerializerTest extends TestCase { public void testEmptyMapSerialization() { Type mapType = new TypeToken>() { }.getType(); Map emptyMap = new HashMap(); - JsonElement element = mapSerializer.serialize(emptyMap, mapType, null); + JsonElement element = gson.toJsonTree(emptyMap, mapType); assertTrue(element instanceof JsonObject); JsonObject emptyMapJsonObject = (JsonObject) element; assertTrue(emptyMapJsonObject.entrySet().isEmpty()); } - + public void testNonEmptyMapSerialization() { Type mapType = new TypeToken>() { }.getType(); Map myMap = new HashMap(); diff --git a/gson/src/test/java/com/google/gson/FunctionWithInternalDependenciesTest.java b/gson/src/test/java/com/google/gson/FunctionWithInternalDependenciesTest.java index 0b386e47..6c197eb4 100644 --- a/gson/src/test/java/com/google/gson/FunctionWithInternalDependenciesTest.java +++ b/gson/src/test/java/com/google/gson/FunctionWithInternalDependenciesTest.java @@ -16,13 +16,11 @@ package com.google.gson; +import com.google.gson.common.TestTypes.ClassWithNoFields; import java.lang.reflect.Modifier; import java.util.LinkedList; - import junit.framework.TestCase; -import com.google.gson.common.TestTypes.ClassWithNoFields; - /** * Functional tests for Gson that depend on some internal package-protected elements of * com.google.gson package and hence must be placed in the same package. We should make every @@ -39,7 +37,7 @@ public class FunctionWithInternalDependenciesTest extends TestCase { strategies.add(new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC)); ExclusionStrategy exclusionStrategy = new DisjunctionExclusionStrategy(strategies); Gson gson = new Gson(exclusionStrategy, exclusionStrategy, Gson.DEFAULT_NAMING_POLICY, - new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()), + DefaultTypeAdapters.getDefaultInstanceCreators(), false, DefaultTypeAdapters.getDefaultSerializers(), DefaultTypeAdapters.getDefaultDeserializers(), Gson.DEFAULT_JSON_NON_EXECUTABLE, true, false, false, LongSerializationPolicy.DEFAULT); diff --git a/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java b/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java deleted file mode 100644 index da36ef02..00000000 --- a/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 junit.framework.TestCase; - -import java.lang.reflect.Type; - -/** - * Unit tests for the {@link MappedObjectConstructor} class. - * - * @author Joel Leitch - */ -public class MappedObjectConstructorTest extends TestCase { - private ParameterizedTypeHandlerMap> creatorMap; - private MappedObjectConstructor constructor; - - @Override - protected void setUp() throws Exception { - super.setUp(); - creatorMap = new ParameterizedTypeHandlerMap>(); - constructor = new MappedObjectConstructor(creatorMap); - } - - public void testInstanceCreatorTakesTopPrecedence() throws Exception { - creatorMap.register(ObjectWithDefaultConstructor.class, new MyInstanceCreator(), false); - ObjectWithDefaultConstructor obj = - constructor.construct(ObjectWithDefaultConstructor.class); - assertEquals("instanceCreator", obj.stringValue); - assertEquals(10, obj.intValue); - } - - public void testNoInstanceCreatorInvokesDefaultConstructor() throws Exception { - ObjectWithDefaultConstructor expected = new ObjectWithDefaultConstructor(); - ObjectWithDefaultConstructor obj = - constructor.construct(ObjectWithDefaultConstructor.class); - assertEquals(expected.stringValue, obj.stringValue); - assertEquals(expected.intValue, obj.intValue); - } - - public void testNoDefaultConstructor() throws Exception { - ObjectNoDefaultConstructor obj = constructor.construct(ObjectNoDefaultConstructor.class); - assertNull(obj.stringValue); - assertEquals(0, obj.intValue); - } - - private static class MyInstanceCreator - implements InstanceCreator { - public ObjectWithDefaultConstructor createInstance(Type type) { - return new ObjectWithDefaultConstructor("instanceCreator", 10); - } - } - - private static class ObjectWithDefaultConstructor { - public final String stringValue; - public final int intValue; - - private ObjectWithDefaultConstructor() { - this("default", 5); - } - - public ObjectWithDefaultConstructor(String stringValue, int intValue) { - this.stringValue = stringValue; - this.intValue = intValue; - } - } - - private static class ObjectNoDefaultConstructor extends ObjectWithDefaultConstructor { - @SuppressWarnings("unused") - public ObjectNoDefaultConstructor(String stringValue, int intValue) { - super(stringValue, intValue); - } - } -} diff --git a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java b/gson/src/test/java/com/google/gson/MemoryRefStackTest.java deleted file mode 100644 index 788c03fe..00000000 --- a/gson/src/test/java/com/google/gson/MemoryRefStackTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.google.gson; - - -import junit.framework.TestCase; - -import java.util.EmptyStackException; - -/** - * Unit tests for the {@link MemoryRefStack} class. - * - * @author Joel Leitch - */ -public class MemoryRefStackTest extends TestCase { - private MemoryRefStack stack; - - @Override - protected void setUp() throws Exception { - super.setUp(); - stack = new MemoryRefStack(); - } - - public void testPeekEmptyStack() throws Exception { - try { - stack.peek(); - } catch (EmptyStackException expected) { } - } - - public void testPushPeekAndPop() throws Exception { - ObjectTypePair obj = new ObjectTypePair(this, getClass(), true, false); - - assertEquals(obj, stack.push(obj)); - assertEquals(obj, stack.peek()); - assertEquals(obj, stack.pop()); - } - - public void testPopTooMany() throws Exception { - ObjectTypePair obj = new ObjectTypePair(this, getClass(), true, false); - stack.push(obj); - assertEquals(obj, stack.pop()); - - try { - stack.pop(); - } catch (EmptyStackException expected) { } - } - - public void testContains() throws Exception { - MockObject objA = new MockObject(); - MockObject objB = new MockObject(); - assertEquals(objA, objB); - stack.push(new ObjectTypePair(objA, MockObject.class, true, false)); - assertTrue(stack.contains(new ObjectTypePair(objA, MockObject.class, true, false))); - assertFalse(stack.contains(new ObjectTypePair(objB, MockObject.class, true, false))); - } - - private static class MockObject { - private final int value = 1; - - @Override - public boolean equals(Object obj) { - return obj instanceof MockObject && value == ((MockObject) obj).value; - } - - @Override - public int hashCode() { - return value; - } - } -} diff --git a/gson/src/test/java/com/google/gson/MixedStreamTest.java b/gson/src/test/java/com/google/gson/MixedStreamTest.java index 4cf00b0a..dbd86792 100644 --- a/gson/src/test/java/com/google/gson/MixedStreamTest.java +++ b/gson/src/test/java/com/google/gson/MixedStreamTest.java @@ -73,7 +73,7 @@ public final class MixedStreamTest extends TestCase { assertEquals(RED_MIATA, gson.fromJson(jsonReader, Car.class)); jsonReader.endArray(); } - + public void testReaderDoesNotMutateState() throws IOException { Gson gson = new Gson(); JsonReader jsonReader = new JsonReader(new StringReader(CARS_JSON)); @@ -114,7 +114,7 @@ public final class MixedStreamTest extends TestCase { try { gson.fromJson(jsonReader, String.class); fail(); - } catch (IllegalArgumentException expected) { + } catch (IllegalStateException expected) { } } @@ -152,7 +152,7 @@ public final class MixedStreamTest extends TestCase { } catch (IllegalStateException expected) { } } - + public void testWriteNulls() { Gson gson = new Gson(); try { @@ -165,7 +165,7 @@ public final class MixedStreamTest extends TestCase { gson.toJson(null, new JsonWriter(stringWriter)); assertEquals("", stringWriter.toString()); } - + public void testReadNulls() { Gson gson = new Gson(); try { @@ -176,7 +176,7 @@ public final class MixedStreamTest extends TestCase { try { gson.fromJson(new JsonReader(new StringReader("true")), null); fail(); - } catch (IllegalArgumentException expected) { + } catch (NullPointerException expected) { } } @@ -195,7 +195,7 @@ public final class MixedStreamTest extends TestCase { assertEquals("[\"<\",\">\",\"&\",\"=\",\"'\"]", writer.toString()); } - + public void testWriteLenient() { List doubles = Arrays.asList(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, -0.0d, 0.5d, 0.0d); diff --git a/gson/src/test/java/com/google/gson/NullExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/NullExclusionStrategyTest.java deleted file mode 100644 index c0d486cf..00000000 --- a/gson/src/test/java/com/google/gson/NullExclusionStrategyTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.google.gson; - -import junit.framework.TestCase; - -/** - * Unit test for the {@link NullExclusionStrategy} class. - * - * @author Joel Leitch - */ -public class NullExclusionStrategyTest extends TestCase { - private NullExclusionStrategy strategy; - - @Override - protected void setUp() throws Exception { - super.setUp(); - strategy = new NullExclusionStrategy(); - } - - public void testNeverSkipsClass() throws Exception { - assertFalse(strategy.shouldSkipClass(String.class)); - } - - public void testNeverSkipsField() throws Exception { - assertFalse(strategy.shouldSkipField( - new FieldAttributes(String.class, String.class.getFields()[0], String.class))); - } -} diff --git a/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java b/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java index 96062fae..6eff2ee3 100644 --- a/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java +++ b/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java @@ -18,6 +18,7 @@ package com.google.gson; import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.Primitives; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; diff --git a/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java index 801697c2..fb38687b 100644 --- a/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java +++ b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java @@ -16,6 +16,7 @@ package com.google.gson; +import com.google.gson.internal.Primitives; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java index e5a1b858..f2a4eae3 100644 --- a/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java +++ b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java @@ -53,7 +53,7 @@ public class ExclusionStrategyFunctionalTest extends TestCase { assertFalse(json.contains("\"annotatedField\"")); assertTrue(json.contains("\"longField\"")); } - + public void testExclusionStrategyDeserialization() throws Exception { Gson gson = createGson(new MyExclusionStrategy(String.class), false); JsonObject json = new JsonObject(); @@ -68,13 +68,13 @@ public class ExclusionStrategyFunctionalTest extends TestCase { assertEquals(src.annotatedField, target.annotatedField); assertEquals(src.stringField, target.stringField); } - + public void testExclusionStrategyWithMode() throws Exception { SampleObjectForTest testObj = new SampleObjectForTest( src.annotatedField + 5, src.stringField + "blah,blah", src.longField + 655L); - Gson gson = createGson(new MyExclusionStrategy(String.class), false); + Gson gson = createGson(new MyExclusionStrategy(String.class), false); JsonObject json = gson.toJsonTree(testObj).getAsJsonObject(); assertEquals(testObj.annotatedField, json.get("annotatedField").getAsInt()); assertEquals(testObj.stringField, json.get("stringField").getAsString()); @@ -99,7 +99,7 @@ public class ExclusionStrategyFunctionalTest extends TestCase { .serializeNulls() .create(); } - + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) private static @interface Foo { @@ -115,7 +115,7 @@ public class ExclusionStrategyFunctionalTest extends TestCase { public SampleObjectForTest() { this(5, "someDefaultValue", 12345L); } - + public SampleObjectForTest(int annotatedField, String stringField, long longField) { this.annotatedField = annotatedField; this.stringField = stringField; diff --git a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java index 374b51d3..a2d4bdaf 100644 --- a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java @@ -16,17 +16,15 @@ package com.google.gson.functional; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import junit.framework.TestCase; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import junit.framework.TestCase; public class MapAsArrayTypeAdapterTest extends TestCase { @@ -82,7 +80,7 @@ public class MapAsArrayTypeAdapterTest extends TestCase { } catch (JsonSyntaxException expected) { } } - + public void testMultipleEnableComplexKeyRegistrationHasNoEffect() throws Exception { Type type = new TypeToken>() {}.getType(); Gson gson = new GsonBuilder() diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java b/gson/src/test/java/com/google/gson/internal/ParameterizedTypeHandlerMapTest.java similarity index 96% rename from gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java rename to gson/src/test/java/com/google/gson/internal/ParameterizedTypeHandlerMapTest.java index 5e8212e8..1b7b4f76 100644 --- a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java +++ b/gson/src/test/java/com/google/gson/internal/ParameterizedTypeHandlerMapTest.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.google.gson; +package com.google.gson.internal; import com.google.gson.common.TestTypes.Base; import com.google.gson.common.TestTypes.Sub; +import com.google.gson.internal.ParameterizedTypeHandlerMap; import com.google.gson.reflect.TypeToken; import junit.framework.TestCase; @@ -26,7 +27,7 @@ import java.lang.reflect.Type; import java.util.List; /** - * Unit tests for the {@link ParameterizedTypeHandlerMap} class. + * Unit tests for the {@link com.google.gson.internal.ParameterizedTypeHandlerMap} class. * * @author Joel Leitch */