/** * * 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 java.lang.reflect.Field; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingStrategy; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.Excluder; import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; import com.google.gson.reflect.TypeToken; public class ConfigurableTypeAdapterFactory implements TypeAdapterFactory { private ReflectiveTypeAdapterFactory delegate; public ConfigurableTypeAdapterFactory() { delegate = new ReflectiveTypeAdapterFactory( new ConstructorConstructor(), new ConfigurableFieldNamingStrategy(), new Excluder().withExclusionStrategy(new ConfigurableExclusionStrategy(), true, true)); } public class ConfigurableExclusionStrategy implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(Configurable.class) == null; } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } } public class ConfigurableFieldNamingStrategy implements FieldNamingStrategy { @Override public String translateName(Field f) { Configurable configAnnotation = f.getAnnotation(Configurable.class); if (configAnnotation == null || "".equals(configAnnotation.name())) { return FieldNamingPolicy.IDENTITY.translateName(f); } else { return configAnnotation.name(); } } } @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { return delegate.create(gson, type); } } //public class ConfigurableTypeAdapterFactory<E> { // private final Context context; // private final Class<E> baseClass; // private final Class<? extends E> defaultClass; // // /** // * Use the base class as the default class. // * @param context // * @param baseClass // */ // public ConfigurableTypeAdapterFactory(Context context, Class<E> baseClass) { // this(context, baseClass, null); // } // // /** // * @param context // * @param baseClass // * @param defaultClass Setting this to null will cause a ParseException if the runtime type information is incorrect or unavailable. // */ // public ConfigurableTypeAdapterFactory(Context context, Class<E> baseClass, Class<? extends E> defaultClass) { // assert context != null && baseClass != null; // if (defaultClass != null && !DefaultRuntimeTypeAdapterFactory.isInstantiable(defaultClass)) { // throw new RuntimeException("Default class does not have a default contructor."); // } // this.context = context; // this.baseClass = baseClass; // this.defaultClass = defaultClass; // } // // @Override // public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) { // if (baseClass.isAssignableFrom(type.getRawType())) { // // TODO: create caching data structures // return new TypeAdapter<T>() { // @Override // public void write(JsonWriter out, T value) throws IOException { // // TODO: need to handle null // JsonObject jsonObject = new JsonObject(); // jsonObject.addProperty(RuntimeTypeAdapterFactory.TYPE, value.getClass().getName()); // List<Field> configurableFields = new ArrayList<Field>(); // AnnotationUtil.getAllFieldsWithAnnotation(configurableFields, value.getClass(), Configurable.class); // for (Field field : configurableFields) { // String fieldJsonName = field.getAnnotation(Configurable.class).name(); // if ("".equals(fieldJsonName)) { // fieldJsonName = field.getName(); // } // boolean currentAccessibility = field.isAccessible(); // try { // field.setAccessible(true); // jsonObject.add(fieldJsonName, gson.toJsonTree(field.get(value))); // } catch (IllegalArgumentException e) { // Log.e(LogUtil.TAG, "Bad access of configurable fields!!", e); // } catch (IllegalAccessException e) { // Log.e(LogUtil.TAG, "Bad access of configurable fields!!", e); // } finally { // field.setAccessible(currentAccessibility); // } // } // Streams.write(jsonObject, out); // } // // @Override // public T read(JsonReader in) throws IOException { // // TODO: need to handle null // JsonElement el = Streams.parse(in); // Class<? extends T> runtimeType = DefaultRuntimeTypeAdapterFactory.getRuntimeType(el, type); // if (runtimeType == null) { // throw new ParseException("RuntimeTypeAdapter: Unable to parse runtime type."); // } // // T object = null; // try { // object = runtimeType.newInstance(); // } catch (IllegalAccessException e) { // throw new RuntimeException("RuntimeTypeAdapter: Runtime class '" + runtimeType.getName() + "' does not have a visible default contructor."); // } catch (InstantiationException e) { // throw new RuntimeException("RuntimeTypeAdapter: Runtime class '" + runtimeType.getName() + "' does not have a default contructor."); // } // // // Inject Configuration // if (el.isJsonObject()) { // JsonObject jsonObject = el.getAsJsonObject(); // // Loop over configurable fields for customization // List<Field> configurableFields = new ArrayList<Field>(); // AnnotationUtil.getAllFieldsWithAnnotation(configurableFields, runtimeType, Configurable.class); // for (Field field : configurableFields) { // // TODO: check that object doesn't have field of it's own type (to prevent infinite recursion) // String fieldJsonName = field.getAnnotation(Configurable.class).name(); // if ("".equals(fieldJsonName)) { // fieldJsonName = field.getName(); // } // boolean currentAccessibility = field.isAccessible(); // try { // field.setAccessible(true); // if (jsonObject.has(fieldJsonName)) { // field.set(object, gson.fromJson(jsonObject.get(fieldJsonName), field.getGenericType())); // } // } catch (IllegalArgumentException e) { // Log.e(LogUtil.TAG, "Bad access of configurable fields!!", e); // } catch (IllegalAccessException e) { // Log.e(LogUtil.TAG, "Bad access of configurable fields!!", e); // } finally { // field.setAccessible(currentAccessibility); // } // } // } // // // Inject Context // List<Field> contextFields = new ArrayList<Field>(); // AnnotationUtil.getAllFieldsOfType(contextFields, runtimeType, Context.class); // for (Field field : contextFields) { // boolean currentAccessibility = field.isAccessible(); // field.setAccessible(true); // try { // field.set(object, context); // } catch (IllegalArgumentException e) { // Log.e(LogUtil.TAG, "Bad access of Context fields!!", e); // } catch (IllegalAccessException e) { // Log.e(LogUtil.TAG, "Bad access of Context fields!!", e); // } // field.setAccessible(currentAccessibility); // } // // return object; // } // // }; // } // return null; // } // // //}