| /* |
| * 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); |
| } |
| |
| } |
| } |