/**
*
* Funf: Open Sensing Framework
* Copyright (C) 2010-2011 Nadav Aharony, Wei Pan, Alex Pentland.
* Acknowledgments: Alan Gardner
* Contact: nadav@media.mit.edu
*
* This file is part of Funf.
*
* Funf is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Funf is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Funf. If not, see <http://www.gnu.org/licenses/>.
*
*/
package edu.mit.media.funf.config;
import static edu.mit.media.funf.json.JsonUtils.immutable;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.internal.bind.JsonTreeReader;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
* Same configuration should return the same object.
* A cache of each object created is kept to ensure this.
*
* TODO: should probably have weak refs, so that we can garbage collect unused items.
*
* @author alangardner
*
*/
public class SingletonTypeAdapterFactory implements TypeAdapterFactory {
private RuntimeTypeAdapterFactory delegate;
private Map<String,Object> cache;
public SingletonTypeAdapterFactory(RuntimeTypeAdapterFactory delegate) {
this.delegate = delegate;
this.cache = new HashMap<String,Object>();
}
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
TypeAdapter<T> adapter = delegate.create(gson, type);
return adapter == null ? null : new SingletonTypeAdapter<T>(adapter, type);
}
public Collection<Object> getCached() {
return this.cache.values();
}
public void clearCache(Object o) {
synchronized (cache) {
Set<String> toRemove = new HashSet<String>();
for (Map.Entry<String, Object> entry : cache.entrySet()) {
if (o == entry.getValue()) {
toRemove.add(entry.getKey());
}
}
for (String key : toRemove) {
cache.remove(key);
}
}
}
public void clearCache() {
synchronized (cache) {
cache.clear();
}
}
public class SingletonTypeAdapter<E> extends TypeAdapter<E> {
private TypeAdapter<E> typeAdapter;
private TypeToken<E> type;
public SingletonTypeAdapter(TypeAdapter<E> typeAdapter, TypeToken<E> type) {
this.typeAdapter = typeAdapter;
this.type = type;
}
@Override
public void write(JsonWriter out, E value) throws IOException {
typeAdapter.write(out, value);
}
@Override
public E read(JsonReader in) throws IOException {
JsonElement el = Streams.parse(in);
Class<? extends E> runtimeType = delegate.getRuntimeType(el, type);
String configString = runtimeType.toString() + immutable(el).toString();
// TODO: surround this in a try catch class cast exception
@SuppressWarnings("unchecked")
E object = (E)cache.get(configString);
if (object == null) {
object = typeAdapter.read(new JsonTreeReader(el));
cache.put(configString, object);
}
return object;
}
public void clearCache() {
cache.clear();
}
}
}