mirror of https://github.com/apache/jclouds.git
Adding Multimap TypeAdapterFactory; as well as Set and Map TypeAdapterFactories that filter out nulls
This commit is contained in:
parent
9a68d6b115
commit
a23048cab2
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue