Adding Multimap TypeAdapterFactory; as well as Set and Map TypeAdapterFactories that filter out nulls

This commit is contained in:
Adam Lowe 2012-07-03 12:35:00 +01:00
parent 9a68d6b115
commit a23048cab2
1 changed files with 164 additions and 9 deletions

View File

@ -19,6 +19,8 @@
package org.jclouds.openstack.nova.v2_0.config; package org.jclouds.openstack.nova.v2_0.config;
import java.beans.ConstructorProperties; import java.beans.ConstructorProperties;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -29,22 +31,31 @@ import javax.inject.Singleton;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.openstack.nova.v2_0.domain.*; import org.jclouds.openstack.nova.v2_0.domain.Address;
import org.jclouds.openstack.nova.v2_0.domain.HostResourceUsage;
import org.jclouds.openstack.nova.v2_0.domain.Server;
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedAttributes;
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedStatus;
import org.jclouds.openstack.nova.v2_0.domain.ServerWithSecurityGroups;
import org.jclouds.openstack.v2_0.domain.Link; import org.jclouds.openstack.v2_0.domain.Link;
import org.jclouds.openstack.v2_0.domain.Resource; import org.jclouds.openstack.v2_0.domain.Resource;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gson.JsonArray; import com.google.gson.*;
import com.google.gson.JsonDeserializationContext; import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.JsonDeserializer; import com.google.gson.reflect.TypeToken;
import com.google.gson.JsonElement; import com.google.gson.stream.JsonReader;
import com.google.gson.JsonParseException; import com.google.gson.stream.JsonWriter;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -65,6 +76,8 @@ public class NovaParserModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
bind(new TypeLiteral<Set<TypeAdapterFactory>>() {
}).toInstance(ImmutableSet.<TypeAdapterFactory>of(new SetTypeAdapterFactory(), new MapTypeAdapterFactory(), new MultimapTypeAdapterFactory()));
} }
@Singleton @Singleton
@ -146,10 +159,152 @@ public class NovaParserModule extends AbstractModule {
protected ServerInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId, protected ServerInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4, String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
@Nullable String accessIPv6, Server.Status status, Resource image, Resource flavor, @Nullable String keyName, @Nullable String accessIPv6, Server.Status status, Resource image, Resource flavor, @Nullable String keyName,
@Nullable String configDrive, Map<String, Set<Address>> addresses, Map<String, String> metadata, @Nullable String configDrive, Multimap<String, Address> addresses, Map<String, String> metadata,
@Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes, @Nullable String diskConfig) { @Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes, @Nullable String diskConfig) {
super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, image, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig); super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, image, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig);
} }
} }
} }
/**
* Eliminates nulls from within a set
* <p/>
* Treats [null] as the empty set; [A, null] as [A]; etc.
*/
public static class SetTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Set.class || !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) newSetAdapter(elementAdapter);
}
private <E> TypeAdapter<Set<E>> newSetAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Set<E>>() {
public void write(JsonWriter out, Set<E> value) throws IOException {
out.beginArray();
for (E element : value) {
elementAdapter.write(out, element);
}
out.endArray();
}
public Set<E> read(JsonReader in) throws IOException {
Set<E> result = Sets.newLinkedHashSet();
in.beginArray();
while (in.hasNext()) {
E element = elementAdapter.read(in);
if (element != null) result.add(element);
}
in.endArray();
return result;
}
}.nullSafe();
}
}
/**
* Eliminates null values from incoming maps
* <p/>
* Treats ["a":null] as the empty map; ["a":1, "b":null] as ["a":1]; etc.
*/
public static class MapTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Map.class || !(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 (TypeAdapter<T>) newMapAdapter(keyAdapter, valueAdapter);
}
private <K,V> TypeAdapter<Map<K, V>> newMapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
return new TypeAdapter<Map<K, V>>() {
public void write(JsonWriter out, Map<K, V> value) throws IOException {
out.beginObject();
for (Map.Entry<K, V> element : value.entrySet()) {
out.name(keyAdapter.toJson(element.getKey()));
valueAdapter.write(out, element.getValue());
}
out.endObject();
}
public Map<K, V> read(JsonReader in) throws IOException {
Map<K, V> result = Maps.newLinkedHashMap();
in.beginObject();
while (in.hasNext()) {
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
K name = keyAdapter.read(in);
V value = valueAdapter.read(in);
if (value != null) result.put(name, value);
}
in.endObject();
return result;
}
}.nullSafe();
}
}
/**
* Parses Multi-maps to/from json
*/
public static class MultimapTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if ((typeToken.getRawType() != Multimap.class) || !(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 (TypeAdapter<T>) newMapAdapter(keyAdapter, valueAdapter);
}
private <K,V> TypeAdapter<Multimap<K, V>> newMapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
return new TypeAdapter<Multimap<K, V>>() {
public void write(JsonWriter out, Multimap<K, V> map) throws IOException {
out.beginObject();
for (K key : map.keySet()) {
out.name(keyAdapter.toJson(key));
out.beginArray();
for (V value : map.get(key)) {
valueAdapter.write(out, value);
}
out.endArray();
}
out.endObject();
}
public Multimap<K, V> read(JsonReader in) throws IOException {
ImmutableMultimap.Builder<K, V> result = ImmutableListMultimap.builder();
in.beginObject();
while (in.hasNext()) {
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
K name = keyAdapter.read(in);
in.beginArray();
while (in.hasNext()) {
V value = valueAdapter.read(in);
if (value != null) result.put(name, value);
}
in.endArray();
}
in.endObject();
return result.build();
}
}.nullSafe();
}
}
} }