blob: cdef883b7ae80431b23288ae2d6a9ff37986dad4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.jclouds.json.internal;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
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;
import org.jclouds.json.gson.internal.JsonReaderInternalAccess;
import com.google.common.base.Objects;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
* Eliminates null values when deserializing Collections, Maps, and Multimaps
* <p/>
* Treats [null] as the empty set; [A, null] as [A]; etc.
*/
public class NullFilteringTypeAdapterFactories {
private NullFilteringTypeAdapterFactories() {
}
static <T> TypeToken<?> resolve(TypeToken<T> ownerType, Type param) {
return TypeToken.get(org.jclouds.json.gson.internal.$Gson$Types.resolve(ownerType.getType(), ownerType.getRawType(),
param));
}
public static final class IterableTypeAdapter<E> extends TypeAdapter<Iterable<E>> {
private final TypeAdapter<E> elementAdapter;
public IterableTypeAdapter(TypeAdapter<E> elementAdapter) {
this.elementAdapter = elementAdapter;
nullSafe();
}
public void write(JsonWriter out, Iterable<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : value)
elementAdapter.write(out, element);
out.endArray();
}
public Iterable<E> read(JsonReader in) throws IOException {
return readAndBuild(in, ImmutableList.<E> builder());
}
@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);
if (element != null)
builder.add(element);
}
in.endArray();
return (C) builder.build();
}
@Override
public int hashCode() {
return elementAdapter.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
IterableTypeAdapter<?> that = IterableTypeAdapter.class.cast(obj);
return equal(this.elementAdapter, that.elementAdapter);
}
@Override
public String toString() {
return toStringHelper(this).add("elementAdapter", elementAdapter).toString();
}
}
public static class ImmutableListTypeAdapterFactory implements TypeAdapterFactory {
protected final Class<?> declaring;
public ImmutableListTypeAdapterFactory() {
this(ImmutableList.class);
}
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);
}
}
public static class ListTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public ListTypeAdapterFactory() {
super(List.class);
}
}
public static final class SetTypeAdapter<E> extends TypeAdapter<Set<E>> {
private final IterableTypeAdapter<E> delegate;
public SetTypeAdapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapter<E>(elementAdapter);
nullSafe();
}
public void write(JsonWriter out, Set<E> value) throws IOException {
this.delegate.write(out, value);
}
public Set<E> read(JsonReader in) throws IOException {
return delegate.readAndBuild(in, ImmutableSet.<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;
SetTypeAdapter<?> that = SetTypeAdapter.class.cast(obj);
return equal(this.delegate, that.delegate);
}
@Override
public String toString() {
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
}
}
public static class ImmutableSetTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public ImmutableSetTypeAdapterFactory() {
this(ImmutableSet.class);
}
protected ImmutableSetTypeAdapterFactory(Class<?> declaring) {
super(declaring);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new SetTypeAdapter<E>(elementAdapter);
}
}
public static class SetTypeAdapterFactory extends ImmutableSetTypeAdapterFactory {
public SetTypeAdapterFactory() {
super(Set.class);
}
}
private static final class FluentIterableTypeAdapter<E> extends TypeAdapter<FluentIterable<E>> {
private final IterableTypeAdapter<E> delegate;
public FluentIterableTypeAdapter(TypeAdapter<E> elementAdapter) {
this.delegate = new IterableTypeAdapter<E>(elementAdapter);
nullSafe();
}
public void write(JsonWriter out, FluentIterable<E> value) throws IOException {
this.delegate.write(out, value.toList());
}
public FluentIterable<E> read(JsonReader in) throws IOException {
return FluentIterable.from(delegate.read(in));
}
@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;
FluentIterableTypeAdapter<?> that = FluentIterableTypeAdapter.class.cast(obj);
return equal(this.delegate, that.delegate);
}
@Override
public String toString() {
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
}
}
public static class FluentIterableTypeAdapterFactory extends ImmutableListTypeAdapterFactory {
public FluentIterableTypeAdapterFactory() {
super(FluentIterable.class);
}
@SuppressWarnings("unchecked")
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
return (TypeAdapter<I>) new FluentIterableTypeAdapter<E>(elementAdapter);
}
}
public static class ImmutableMapTypeAdapterFactory extends MapTypeAdapterFactory {
public ImmutableMapTypeAdapterFactory() {
super(ImmutableMap.class);
}
}
public static class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
protected final TypeAdapter<K> keyAdapter;
protected final TypeAdapter<V> valueAdapter;
protected MapTypeAdapter(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();
}
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()) {
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.build();
}
@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;
MapTypeAdapter<?, ?> that = MapTypeAdapter.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 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);
}
@SuppressWarnings("unchecked")
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
return (TypeAdapter<T>) new MapTypeAdapter<K, V>(keyAdapter, valueAdapter);
}
}
private static final class MultimapTypeAdapter<K, V> extends TypeAdapter<Multimap<K, V>> {
private final MapTypeAdapter<K, Iterable<V>> delegate;
public MultimapTypeAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<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, 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, Iterable<V>> entry : delegate.read(in).entrySet())
builder.putAll(entry.getKey(), entry.getValue());
return builder.build();
}
@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;
MultimapTypeAdapter<?, ?> that = MultimapTypeAdapter.class.cast(obj);
return equal(this.delegate, that.delegate);
}
@Override
public String toString() {
return toStringHelper(this).add("keyAdapter", delegate.keyAdapter)
.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);
}
}
}