make it possible to deserialize into ctors that have immutable collection parameters

This commit is contained in:
adriancole 2013-03-13 11:56:58 -07:00
parent 7596205774
commit ef2718629f
5 changed files with 325 additions and 269 deletions

View File

@ -26,6 +26,7 @@ import javax.inject.Inject;
import org.jclouds.date.DateService;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapter;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
import com.google.common.base.Splitter;
@ -84,10 +85,10 @@ public class CloudStackParserModule extends AbstractModule {
public static final class Adapter<E> extends TypeAdapter<Iterable<E>> {
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
private final IterableTypeAdapter<E> delegate;
public Adapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
this.delegate = new IterableTypeAdapter<E>(elementAdapter);
nullSafe();
}

View File

@ -25,6 +25,7 @@ import java.util.Set;
import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapter;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
import com.google.common.base.Objects;
@ -63,10 +64,10 @@ public class KeystoneParserModule extends AbstractModule {
public static final class Adapter<E> extends TypeAdapter<Set<E>> {
private final SetTypeAdapterFactory.SetTypeAdapter<E> delegate;
private final SetTypeAdapter<E> delegate;
public Adapter(TypeAdapter<E> elementAdapter) {
this.delegate = new SetTypeAdapterFactory.SetTypeAdapter<E>(elementAdapter);
this.delegate = new SetTypeAdapter<E>(elementAdapter);
nullSafe();
}

View File

@ -46,7 +46,10 @@ import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.CollectionTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.FluentIterableTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ImmutableListTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ImmutableSetTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ListTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MultimapTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
@ -88,9 +91,10 @@ public class GsonModule extends AbstractModule {
@Singleton
Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings,
OptionalTypeAdapterFactory optional, SetTypeAdapterFactory set, MapTypeAdapterFactory map,
MultimapTypeAdapterFactory multimap, IterableTypeAdapterFactory iterable,
CollectionTypeAdapterFactory collection, FluentIterableTypeAdapterFactory fluentIterable,
OptionalTypeAdapterFactory optional, SetTypeAdapterFactory set, ImmutableSetTypeAdapterFactory immutableSet,
MapTypeAdapterFactory map, MultimapTypeAdapterFactory multimap, IterableTypeAdapterFactory iterable,
CollectionTypeAdapterFactory collection, ListTypeAdapterFactory list,
ImmutableListTypeAdapterFactory immutableList, FluentIterableTypeAdapterFactory fluentIterable,
DefaultExclusionStrategy exclusionStrategy) {
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
@ -109,7 +113,10 @@ public class GsonModule extends AbstractModule {
builder.registerTypeAdapterFactory(optional);
builder.registerTypeAdapterFactory(iterable);
builder.registerTypeAdapterFactory(collection);
builder.registerTypeAdapterFactory(list);
builder.registerTypeAdapterFactory(immutableList);
builder.registerTypeAdapterFactory(set);
builder.registerTypeAdapterFactory(immutableSet);
builder.registerTypeAdapterFactory(map);
builder.registerTypeAdapterFactory(multimap);
builder.registerTypeAdapterFactory(fluentIterable);

View File

@ -25,6 +25,7 @@ import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ -61,33 +62,6 @@ public class NullFilteringTypeAdapterFactories {
param));
}
public static class IterableTypeAdapterFactory implements TypeAdapterFactory {
protected final Class<?> declaring;
public IterableTypeAdapterFactory() {
this(Iterable.class);
}
protected IterableTypeAdapterFactory(Class<?> declaring) {
this.declaring = declaring;
}
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
Type type = ownerType.getType();
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
return null;
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) newAdapter(elementAdapter);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new IterableTypeAdapter<E>(elementAdapter);
}
public static final class IterableTypeAdapter<E> extends TypeAdapter<Iterable<E>> {
private final TypeAdapter<E> elementAdapter;
@ -145,73 +119,60 @@ public class NullFilteringTypeAdapterFactories {
return toStringHelper(this).add("elementAdapter", elementAdapter).toString();
}
}
public static class ImmutableListTypeAdapterFactory implements TypeAdapterFactory {
protected final Class<?> declaring;
public ImmutableListTypeAdapterFactory() {
this(ImmutableList.class);
}
public static class CollectionTypeAdapterFactory extends IterableTypeAdapterFactory {
protected ImmutableListTypeAdapterFactory(Class<?> declaring) {
this.declaring = declaring;
}
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
Type type = ownerType.getType();
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
return null;
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) newAdapter(elementAdapter);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new IterableTypeAdapter<E>(elementAdapter);
}
}
public static class IterableTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public IterableTypeAdapterFactory() {
super(Iterable.class);
}
}
public static class CollectionTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public CollectionTypeAdapterFactory() {
super(Collection.class);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new CollectionTypeAdapter<E>(elementAdapter);
}
public static final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
public CollectionTypeAdapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
nullSafe();
public static class ListTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public ListTypeAdapterFactory() {
super(List.class);
}
public void write(JsonWriter out, Collection<E> value) throws IOException {
this.delegate.write(out, value);
}
public Collection<E> read(JsonReader in) throws IOException {
return delegate.readAndBuild(in, ImmutableList.<E> builder());
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
CollectionTypeAdapter<?> that = CollectionTypeAdapter.class.cast(obj);
return equal(this.delegate, that.delegate);
}
@Override
public String toString() {
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
}
}
}
public static class SetTypeAdapterFactory extends IterableTypeAdapterFactory {
public SetTypeAdapterFactory() {
super(Set.class);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new SetTypeAdapter<E>(elementAdapter);
}
public static final class SetTypeAdapter<E> extends TypeAdapter<Set<E>> {
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
private final IterableTypeAdapter<E> delegate;
public SetTypeAdapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
this.delegate = new IterableTypeAdapter<E>(elementAdapter);
nullSafe();
}
@ -243,24 +204,34 @@ public class NullFilteringTypeAdapterFactories {
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
}
}
public static class ImmutableSetTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public ImmutableSetTypeAdapterFactory() {
this(ImmutableSet.class);
}
public static class FluentIterableTypeAdapterFactory extends IterableTypeAdapterFactory {
public FluentIterableTypeAdapterFactory() {
super(FluentIterable.class);
protected ImmutableSetTypeAdapterFactory(Class<?> declaring) {
super(declaring);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new FluentIterableTypeAdapter<E>(elementAdapter);
return (TypeAdapter<I>) new SetTypeAdapter<E>(elementAdapter);
}
}
public static final class FluentIterableTypeAdapter<E> extends TypeAdapter<FluentIterable<E>> {
public static class SetTypeAdapterFactory extends ImmutableSetTypeAdapterFactory {
public SetTypeAdapterFactory() {
super(Set.class);
}
}
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
private static final class FluentIterableTypeAdapter<E> extends TypeAdapter<FluentIterable<E>> {
private final IterableTypeAdapter<E> delegate;
public FluentIterableTypeAdapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
this.delegate = new IterableTypeAdapter<E>(elementAdapter);
nullSafe();
}
@ -292,37 +263,19 @@ public class NullFilteringTypeAdapterFactories {
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
}
}
}
public static class MapTypeAdapterFactory implements TypeAdapterFactory {
protected final Class<?> declaring;
public MapTypeAdapterFactory() {
this(Map.class);
}
protected MapTypeAdapterFactory(Class<?> declaring) {
this.declaring = declaring;
}
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
Type type = ownerType.getType();
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
return null;
Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
return newAdapter(keyAdapter, valueAdapter);
public static class FluentIterableTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public FluentIterableTypeAdapterFactory() {
super(FluentIterable.class);
}
@SuppressWarnings("unchecked")
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
return (TypeAdapter<T>) new MapTypeAdapter<K, V>(keyAdapter, valueAdapter);
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new FluentIterableTypeAdapter<E>(elementAdapter);
}
}
public static final class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
private static final class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
protected final TypeAdapter<K> keyAdapter;
protected final TypeAdapter<V> valueAdapter;
@ -380,37 +333,54 @@ public class NullFilteringTypeAdapterFactories {
return toStringHelper(this).add("keyAdapter", keyAdapter).add("valueAdapter", valueAdapter).toString();
}
}
public static class MapTypeAdapterFactory implements TypeAdapterFactory {
protected final Class<?> declaring;
public MapTypeAdapterFactory() {
this(Map.class);
}
public static class MultimapTypeAdapterFactory extends MapTypeAdapterFactory {
protected MapTypeAdapterFactory(Class<?> declaring) {
this.declaring = declaring;
}
public MultimapTypeAdapterFactory() {
super(Multimap.class);
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
Type type = ownerType.getType();
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
return null;
Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
return newAdapter(keyAdapter, valueAdapter);
}
@SuppressWarnings("unchecked")
@Override
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
return (TypeAdapter<T>) new MultimapTypeAdapter<K, V>(keyAdapter, valueAdapter);
return (TypeAdapter<T>) new MapTypeAdapter<K, V>(keyAdapter, valueAdapter);
}
}
public static final class MultimapTypeAdapter<K, V> extends TypeAdapter<Multimap<K, V>> {
private static final class MultimapTypeAdapter<K, V> extends TypeAdapter<Multimap<K, V>> {
private final MapTypeAdapterFactory.MapTypeAdapter<K, Collection<V>> delegate;
private final MapTypeAdapter<K, Iterable<V>> delegate;
public MultimapTypeAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
this.delegate = new MapTypeAdapterFactory.MapTypeAdapter<K, Collection<V>>(keyAdapter,
new CollectionTypeAdapterFactory.CollectionTypeAdapter<V>(valueAdapter));
this.delegate = new MapTypeAdapter<K, Iterable<V>>(keyAdapter,
new IterableTypeAdapter<V>(valueAdapter));
nullSafe();
}
@SuppressWarnings("unchecked")
public void write(JsonWriter out, Multimap<K, V> value) throws IOException {
this.delegate.write(out, value.asMap());
this.delegate.write(out, Map.class.cast(value.asMap()));
}
public Multimap<K, V> read(JsonReader in) throws IOException {
ImmutableMultimap.Builder<K, V> builder = ImmutableMultimap.<K, V> builder();
for (Entry<K, Collection<V>> entry : delegate.read(in).entrySet())
for (Entry<K, Iterable<V>> entry : delegate.read(in).entrySet())
builder.putAll(entry.getKey(), entry.getValue());
return builder.build();
}
@ -436,5 +406,18 @@ public class NullFilteringTypeAdapterFactories {
.add("valueAdapter", delegate.valueAdapter).toString();
}
}
public static class MultimapTypeAdapterFactory extends MapTypeAdapterFactory {
public MultimapTypeAdapterFactory() {
super(Multimap.class);
}
@SuppressWarnings("unchecked")
@Override
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
return (TypeAdapter<T>) new MultimapTypeAdapter<K, V>(keyAdapter, valueAdapter);
}
}
}

View File

@ -23,12 +23,16 @@ import static org.testng.Assert.assertEquals;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.CollectionTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.FluentIterableTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ImmutableListTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ImmutableSetTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ListTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MultimapTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
@ -149,6 +153,46 @@ public class NullFilteringTypeAdapterFactoriesTest {
ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson list = new GsonBuilder().registerTypeAdapterFactory(new ListTypeAdapterFactory()).create();
private Type listType = new TypeToken<List<String>>() {
private static final long serialVersionUID = 1L;
}.getType();
private Type listResourceType = new TypeToken<List<Resource>>() {
private static final long serialVersionUID = 1L;
}.getType();
public void testList() {
Iterable<String> noNulls = list.fromJson("[\"value\",\"a test string!\"]", listType);
assertEquals(noNulls, ImmutableList.of("value", "a test string!"));
Iterable<String> withNull = list.fromJson("[null,\"a test string!\"]", listType);
assertEquals(withNull, ImmutableList.of("a test string!"));
Iterable<String> withDupes = list.fromJson("[\"value\",\"value\"]", listType);
assertEquals(withDupes, ImmutableList.of("value", "value"));
Iterable<Resource> resources = list.fromJson(
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", listResourceType);
assertEquals(resources, ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson immutableList = new GsonBuilder().registerTypeAdapterFactory(new ImmutableListTypeAdapterFactory()).create();
private Type immutableListType = new TypeToken<ImmutableList<String>>() {
private static final long serialVersionUID = 1L;
}.getType();
private Type immutableListResourceType = new TypeToken<ImmutableList<Resource>>() {
private static final long serialVersionUID = 1L;
}.getType();
public void testImmutableList() {
Iterable<String> noNulls = immutableList.fromJson("[\"value\",\"a test string!\"]", immutableListType);
assertEquals(noNulls, ImmutableList.of("value", "a test string!"));
Iterable<String> withNull = immutableList.fromJson("[null,\"a test string!\"]", immutableListType);
assertEquals(withNull, ImmutableList.of("a test string!"));
Iterable<String> withDupes = immutableList.fromJson("[\"value\",\"value\"]", immutableListType);
assertEquals(withDupes, ImmutableList.of("value", "value"));
Iterable<Resource> resources = immutableList.fromJson(
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", immutableListResourceType);
assertEquals(resources, ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson set = new GsonBuilder().registerTypeAdapterFactory(new SetTypeAdapterFactory()).create();
private Type setType = new TypeToken<Set<String>>() {
private static final long serialVersionUID = 1L;
@ -169,6 +213,26 @@ public class NullFilteringTypeAdapterFactoriesTest {
assertEquals(resources, ImmutableSet.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson immutableSet = new GsonBuilder().registerTypeAdapterFactory(new ImmutableSetTypeAdapterFactory()).create();
private Type immutableSetType = new TypeToken<ImmutableSet<String>>() {
private static final long serialVersionUID = 1L;
}.getType();
private Type immutableSetResourceType = new TypeToken<ImmutableSet<Resource>>() {
private static final long serialVersionUID = 1L;
}.getType();
public void testImmutableSet() {
Iterable<String> noNulls = immutableSet.fromJson("[\"value\",\"a test string!\"]", immutableSetType);
assertEquals(noNulls, ImmutableSet.of("value", "a test string!"));
Iterable<String> withNull = immutableSet.fromJson("[null,\"a test string!\"]", immutableSetType);
assertEquals(withNull, ImmutableSet.of("a test string!"));
Iterable<String> withDupes = immutableSet.fromJson("[\"value\",\"value\"]", immutableSetType);
assertEquals(withDupes, ImmutableSet.of("value", "value"));
Iterable<Resource> resources = immutableSet.fromJson(
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", immutableSetResourceType);
assertEquals(resources, ImmutableSet.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson map = new GsonBuilder().registerTypeAdapterFactory(new MapTypeAdapterFactory()).create();
private Type mapType = new TypeToken<Map<String, String>>() {
private static final long serialVersionUID = 1L;