package com.oanda.v20.primitives; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; 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; /** * Generic Nullable type GSON TypeAdapterFactory * * @param <C> the underlying type being held by the NullableType */ public class NullableTypeAdapterFactory<C> implements TypeAdapterFactory { private final Class<C> containedClass; public <T> NullableTypeAdapterFactory(Class<C> containedClass) { this.containedClass = containedClass; } public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { if (type.getRawType() != NullableType.class) { return null; } if (getBaseType(type.getType()) != this.containedClass) { return null; } final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, TypeToken.get(this.containedClass)); return new TypeAdapter<T>() { @SuppressWarnings("unchecked") @Override public void write(JsonWriter out, T value) throws IOException { if (value == null || ! ((NullableType<C>) value).isSet()) { out.nullValue(); } else if (((NullableType<C>) value).getValue() == null) { boolean oldSerializeNulls = out.getSerializeNulls(); out.setSerializeNulls(true); out.nullValue(); out.setSerializeNulls(oldSerializeNulls); } else { try { delegate.write(out, (C) (((NullableType<C>) value).getValue())); } catch (IllegalArgumentException e) { throw new IOException(e); } } } @SuppressWarnings("unchecked") @Override public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return (T) new NullableType<C>(); } else { return (T) new NullableType<C>(delegate.read(in)); } } }; } final private Type getBaseType(Type type) { if (! (type instanceof ParameterizedType)) { return type; } Type[] types = ((ParameterizedType) type).getActualTypeArguments(); if (types[0] instanceof ParameterizedType) { return ((ParameterizedType) types[0]).getRawType(); } return types[0]; } }