JCLOUDS-1044 fix handling NULL JsonTokens in adapters under NullFilteringTypeAdapterFactories class

This commit is contained in:
Josef Cacek 2015-12-02 23:50:15 +01:00 committed by Ignasi Barrera
parent 4d899cacae
commit 0fb1b459a6
4 changed files with 67 additions and 48 deletions

View File

@ -16,8 +16,6 @@
*/
package org.jclouds.chef.config;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
@ -39,11 +37,11 @@ import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.Pems;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
@ -57,7 +55,7 @@ import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.JsonToken;
import com.google.inject.AbstractModule;
import com.google.inject.ImplementedBy;
import com.google.inject.Provides;
@ -205,33 +203,19 @@ public class ChefParserModule extends AbstractModule {
private String id;
}
// The NullFilteringTypeAdapterFactories.MapTypeAdapter class is final. Do
// the same logic here
private static final class KeepLastRepeatedKeyMapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
protected final TypeAdapter<K> keyAdapter;
protected final TypeAdapter<V> valueAdapter;
private static final class KeepLastRepeatedKeyMapTypeAdapter<K, V>
extends NullFilteringTypeAdapterFactories.MapTypeAdapter<K, V> {
protected KeepLastRepeatedKeyMapTypeAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
this.keyAdapter = keyAdapter;
this.valueAdapter = valueAdapter;
nullSafe();
}
public void write(JsonWriter out, Map<K, V> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
for (Map.Entry<K, V> element : value.entrySet()) {
out.name(String.valueOf(element.getKey()));
valueAdapter.write(out, element.getValue());
}
out.endObject();
super(keyAdapter, valueAdapter);
}
@Override
public Map<K, V> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Map<K, V> result = Maps.newHashMap();
in.beginObject();
while (in.hasNext()) {
@ -239,33 +223,14 @@ public class ChefParserModule extends AbstractModule {
K name = keyAdapter.read(in);
V value = valueAdapter.read(in);
if (value != null) {
// If there are repeated keys, overwrite them to only keep the last one
// If there are repeated keys, overwrite them to only keep the
// last one
result.put(name, value);
}
}
in.endObject();
return ImmutableMap.copyOf(result);
}
@Override
public int hashCode() {
return Objects.hashCode(keyAdapter, valueAdapter);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
KeepLastRepeatedKeyMapTypeAdapter<?, ?> that = KeepLastRepeatedKeyMapTypeAdapter.class.cast(obj);
return equal(this.keyAdapter, that.keyAdapter) && equal(this.valueAdapter, that.valueAdapter);
}
@Override
public String toString() {
return toStringHelper(this).add("keyAdapter", keyAdapter).add("valueAdapter", valueAdapter).toString();
}
}
public static class KeepLastRepeatedKeyMapTypeAdapterFactory extends MapTypeAdapterFactory {

View File

@ -20,12 +20,15 @@ import static com.google.common.base.Objects.equal;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import org.jclouds.chef.config.ChefParserModule.KeepLastRepeatedKeyMapTypeAdapterFactory;
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.ListTypeAdapterFactory;
import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
@ -90,4 +93,25 @@ public class ChefParserModuleTest {
assertEquals(duplicates,
ImmutableMap.of("i-foo", new KeyValue("i-foo", "foo2"), "i-bar", new KeyValue("i-bar", "bar2")));
}
private Gson listInMap = new GsonBuilder().registerTypeAdapterFactory(new KeepLastRepeatedKeyMapTypeAdapterFactory())
.registerTypeAdapterFactory(new ListTypeAdapterFactory()).create();
private Type listInMapType = new TypeToken<Map<String, List<Map<String, String>>>>() {
private static final long serialVersionUID = 1L;
}.getType();
public void testListInMap() {
Map<String, List<Map<String, String>>> notNull = listInMap
.fromJson("{\"value\":[{\"x\":\"y\",\"a\":\"b\"},{\"u\":\"v\"}]}", listInMapType);
assertEquals(notNull,
ImmutableMap.of("value", ImmutableList.of(ImmutableMap.of("x", "y", "a", "b"), ImmutableMap.of("u", "v"))));
Map<String, List<Map<String, String>>> innerMapValueNull = listInMap
.fromJson("{\"value\":[{\"x\":\"y\",\"a\":null},{\"u\":\"v\"}]}", listInMapType);
assertEquals(innerMapValueNull,
ImmutableMap.of("value", ImmutableList.of(ImmutableMap.of("x", "y"), ImmutableMap.of("u", "v"))));
Map<String, List<Map<String, String>>> withNullInList = listInMap.fromJson("{\"value\":[null]}", listInMapType);
assertEquals(withNullInList, ImmutableMap.of("value", ImmutableList.of()));
Map<String, List<Map<String, String>>> withNullAsList = listInMap.fromJson("{\"parent\":null}", listInMapType);
assertEquals(withNullAsList, ImmutableMap.of());
}
}

View File

@ -30,6 +30,7 @@ import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.JsonReaderInternalAccess;
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;
@ -85,6 +86,10 @@ public class NullFilteringTypeAdapterFactories {
@SuppressWarnings("unchecked")
protected <C extends Iterable<E>, B extends ImmutableCollection.Builder<E>> C readAndBuild(JsonReader in,
B builder) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginArray();
while (in.hasNext()) {
E element = elementAdapter.read(in);
@ -278,7 +283,7 @@ public class NullFilteringTypeAdapterFactories {
}
}
private static final class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
public static class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
protected final TypeAdapter<K> keyAdapter;
protected final TypeAdapter<V> valueAdapter;
@ -303,6 +308,10 @@ public class NullFilteringTypeAdapterFactories {
}
public Map<K, V> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
ImmutableMap.Builder<K, V> result = ImmutableMap.builder();
in.beginObject();
while (in.hasNext()) {

View File

@ -297,4 +297,25 @@ public class NullFilteringTypeAdapterFactoriesTest {
assertEquals(resourceDupes.get("i-foo"),
ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
}
private Gson listInMap = new GsonBuilder().registerTypeAdapterFactory(new MapTypeAdapterFactory())
.registerTypeAdapterFactory(new ListTypeAdapterFactory()).create();
private Type listInMapType = new TypeToken<Map<String, List<Map<String, String>>>>() {
private static final long serialVersionUID = 1L;
}.getType();
public void testListInMap() {
Map<String, List<Map<String, String>>> notNull = listInMap
.fromJson("{\"value\":[{\"x\":\"y\",\"a\":\"b\"},{\"u\":\"v\"}]}", listInMapType);
assertEquals(notNull,
ImmutableMap.of("value", ImmutableList.of(ImmutableMap.of("x", "y", "a", "b"), ImmutableMap.of("u", "v"))));
Map<String, List<Map<String, String>>> innerMapValueNull = listInMap
.fromJson("{\"value\":[{\"x\":\"y\",\"a\":null},{\"u\":\"v\"}]}", listInMapType);
assertEquals(innerMapValueNull,
ImmutableMap.of("value", ImmutableList.of(ImmutableMap.of("x", "y"), ImmutableMap.of("u", "v"))));
Map<String, List<Map<String, String>>> withNullInList = listInMap.fromJson("{\"value\":[null]}", listInMapType);
assertEquals(withNullInList, ImmutableMap.of("value", ImmutableList.of()));
Map<String, List<Map<String, String>>> withNullAsList = listInMap.fromJson("{\"parent\":null}", listInMapType);
assertEquals(withNullAsList, ImmutableMap.of());
}
}