blob: 5a6b5919006428fdee7d62d41f77d2933500f7d5 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed 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 com.google.enterprise.adaptor.secmgr.json;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* Static methods for some useful type adapters.
*/
public final class TypeAdapters {
private static final String JSON_KEY_CLASS = "typeName";
// Don't instantiate.
private TypeAdapters() {
throw new UnsupportedOperationException();
}
/**
* Gets a type adapter for an iterable type. The returned adapter does lazy
* deserialization.
*
* @return A type adapter for {@code Iterable<T>}.
*/
public static Object iterable() {
return ITERABLE;
}
private static final Object ITERABLE = new IterableTypeAdapter<Object>();
private static final class IterableTypeAdapter<T>
extends AbstractIterableTypeAdapter<T, Iterable<T>> {
IterableTypeAdapter() {
super(Iterable.class);
}
@Override
public Iterable<T> deserialize(JsonElement element, Type type,
final JsonDeserializationContext context) {
if (element.isJsonNull()) {
return null;
}
final JsonArray ja = element.getAsJsonArray();
final Type elementType = GsonTypes.getParameterTypes(type, rawType)[0];
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
final Iterator<JsonElement> jsonIterator = ja.iterator();
return new Iterator<T>() {
@Override
public boolean hasNext() {
return jsonIterator.hasNext();
}
@Override
public T next() {
JsonElement je = jsonIterator.next();
return (je == null || je.isJsonNull())
? null
: context.<T>deserialize(je, elementType);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
}
private abstract static class AbstractIterableTypeAdapter<T, I extends Iterable<T>>
implements JsonSerializer<I>, JsonDeserializer<I> {
protected static final JsonNull JSON_NULL = new JsonNull();
protected final Class<?> rawType;
protected AbstractIterableTypeAdapter(Class<?> rawType) {
Preconditions.checkNotNull(rawType);
this.rawType = rawType;
}
@Override
public JsonElement serialize(I iterable, Type typeOfI, JsonSerializationContext context) {
if (iterable == null) {
return JSON_NULL;
}
JsonArray ja = new JsonArray();
Type typeOfElement = GsonTypes.getParameterTypes(typeOfI, rawType)[0];
for (T element : iterable) {
ja.add((element == null)
? JSON_NULL
: context.serialize(element,
(typeOfElement == Object.class)
? element.getClass()
: typeOfElement));
}
return ja;
}
}
/**
* Gets a type adapter for an immutable list.
*
* @return A type adapter for {@code ImmutableList<Object>}.
*/
public static Object immutableList() {
return IMMUTABLE_LIST;
}
/**
* Gets a type adapter for an immutable set.
*
* @return A type adapter for {@code ImmutableSet<Object>}.
*/
public static Object immutableSet() {
return IMMUTABLE_SET;
}
/**
* Gets a type adapter for an immutable multiset.
*
* @return A type adapter for {@code ImmutableMultiset<Object>}.
*/
public static Object immutableMultiset() {
return IMMUTABLE_MULTISET;
}
private static final Object IMMUTABLE_LIST = new ImmutableListTypeAdapter<Object>();
private static final Object IMMUTABLE_SET = new ImmutableSetTypeAdapter<Object>();
private static final Object IMMUTABLE_MULTISET = new ImmutableMultisetTypeAdapter<Object>();
private static final class ImmutableListTypeAdapter<T>
extends AbstractCollectionTypeAdapter<T, ImmutableList<T>> {
ImmutableListTypeAdapter() {
super(ImmutableList.class);
}
@Override
protected ImmutableCollection.Builder<T> getBuilder() {
return ImmutableList.builder();
}
}
private static final class ImmutableSetTypeAdapter<T>
extends AbstractCollectionTypeAdapter<T, ImmutableSet<T>> {
ImmutableSetTypeAdapter() {
super(ImmutableSet.class);
}
@Override
protected ImmutableCollection.Builder<T> getBuilder() {
return ImmutableSet.builder();
}
}
private static final class ImmutableMultisetTypeAdapter<T>
extends AbstractCollectionTypeAdapter<T, ImmutableMultiset<T>> {
ImmutableMultisetTypeAdapter() {
super(ImmutableMultiset.class);
}
@Override
protected ImmutableCollection.Builder<T> getBuilder() {
return ImmutableMultiset.builder();
}
}
private abstract static class AbstractCollectionTypeAdapter<E, C extends Collection<E>>
extends AbstractIterableTypeAdapter<E, C> {
protected AbstractCollectionTypeAdapter(Class<?> rawType) {
super(rawType);
}
protected abstract ImmutableCollection.Builder<E> getBuilder();
@Override
public C deserialize(JsonElement src, Type typeOfSrc, JsonDeserializationContext context) {
if (src.isJsonNull()) {
return null;
}
JsonArray ja = src.getAsJsonArray();
Type typeOfElement = GsonTypes.getParameterTypes(typeOfSrc, rawType)[0];
ImmutableCollection.Builder<E> builder = getBuilder();
for (JsonElement je : ja) {
builder.add((je == null || je.isJsonNull())
? null
: context.<E>deserialize(je, typeOfElement));
}
@SuppressWarnings("unchecked")
C result = (C) builder.build();
return result;
}
}
/**
* Gets a type adapter for an immutable map.
*
* @return A type adapter for {@code ImmutableMap<?, ?>}.
*/
public static Object immutableMap() {
return IMMUTABLE_MAP;
}
private static final Object IMMUTABLE_MAP = new ImmutableMapTypeAdapter<Object, Object>();
private static final class ImmutableMapTypeAdapter<K, V>
implements JsonSerializer<ImmutableMap<K, V>>, JsonDeserializer<ImmutableMap<K, V>> {
@Override
public JsonElement serialize(ImmutableMap<K, V> map, Type typeOfMap,
JsonSerializationContext context) {
Type[] pts = GsonTypes.getParameterTypes(typeOfMap, ImmutableMap.class);
Type typeOfKey = pts[0];
Type typeOfValue = pts[1];
JsonArray ja = new JsonArray();
for (Map.Entry<K, V> entry : map.entrySet()) {
JsonArray je = new JsonArray();
je.add(context.serialize(entry.getKey(), typeOfKey));
je.add(context.serialize(entry.getValue(), typeOfValue));
ja.add(je);
}
return ja;
}
@Override
public ImmutableMap<K, V> deserialize(JsonElement src, Type typeOfSrc,
JsonDeserializationContext context) {
Type[] pts = GsonTypes.getParameterTypes(typeOfSrc, ImmutableMap.class);
Type typeOfKey = pts[0];
Type typeOfValue = pts[1];
JsonArray ja = src.getAsJsonArray();
ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
for (int i = 0; i < ja.size(); i++) {
JsonArray je = ja.get(i).getAsJsonArray();
K key = context.<K>deserialize(je.get(0), typeOfKey);
V value = context.<V>deserialize(je.get(1), typeOfValue);
builder.put(key, value);
}
return builder.build();
}
}
/**
* Gets a type adapter for a given class that dispatches on a given set of
* subclasses.
*
* @param <T> A given class type.
* @param classes Some subclasses of {@code <T>}.
* @return A dispatching type adapter for {@code <T>}.
*/
public static <T> Object dispatch(Iterable<Class<? extends T>> classes) {
return new DispatchTypeAdapter<T>(classes);
}
private static final class DispatchTypeAdapter<T>
implements JsonSerializer<T>, JsonDeserializer<T> {
final ImmutableList<Class<? extends T>> classes;
DispatchTypeAdapter(Iterable<Class<? extends T>> classes) {
this.classes = ImmutableList.copyOf(classes);
}
@Override
public JsonElement serialize(T instance, Type typeOfT, JsonSerializationContext context) {
for (Class<? extends T> subClass : classes) {
if (subClass.isInstance(instance)) {
JsonObject jo = context.serialize(instance, subClass).getAsJsonObject();
jo.addProperty(JSON_KEY_CLASS, subClass.getSimpleName());
return jo;
}
}
throw new JsonParseException("Unknown subclass: " + instance);
}
@Override
public T deserialize(JsonElement je, Type typeOfT, JsonDeserializationContext context) {
JsonObject jo = je.getAsJsonObject();
if (!jo.has(JSON_KEY_CLASS)) {
throw new JsonParseException("Missing " + JSON_KEY_CLASS + " binding: " + jo);
}
String typeName = jo.getAsJsonPrimitive(JSON_KEY_CLASS).getAsString();
for (Class<? extends T> subClass : classes) {
if (subClass.getSimpleName().equals(typeName)) {
return context.<T>deserialize(jo, subClass);
}
}
throw new JsonParseException("Unknown subclass: " + typeName);
}
}
}