/*
* Copyright 2009 Michael Burton
*
* 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 roboguice.inject;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Set;
import com.google.inject.Guice;
import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.HierarchyTraversalFilter;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import android.app.Application;
import android.content.Context;
import android.preference.PreferenceActivity;
/**
*
* @author Mike Burton
*/
public class PreferenceListener implements TypeListener {
protected ArrayList<PreferenceMembersInjector<?>> preferencesForInjection = new ArrayList<PreferenceMembersInjector<?>>();
protected Provider<Context> contextProvider;
protected Application application;
private HierarchyTraversalFilter filter;
public PreferenceListener(Provider<Context> contextProvider, Application application) {
this.contextProvider = contextProvider;
this.application = application;
}
public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
if (filter == null) {
filter = Guice.createHierarchyTraversalFilter();
} else {
filter.reset();
}
Class<?> c = typeLiteral.getRawType();
while (isWorthScanning(c)) {
final Set<Field> allFields;
allFields = filter.getAllFields(InjectPreference.class.getName(), c);
if (allFields != null) {
for (Field field : allFields) {
if (field.isAnnotationPresent(InjectPreference.class))
if (Modifier.isStatic(field.getModifiers()))
throw new UnsupportedOperationException("Preferences may not be statically injected");
else
typeEncounter
.register(new PreferenceMembersInjector<I>(field, contextProvider, field.getAnnotation(InjectPreference.class)));
}
}
c = c.getSuperclass();
}
}
private boolean isWorthScanning(Class<?> c) {
return filter.isWorthScanningForFields(InjectPreference.class.getName(), c);
}
public void registerPreferenceForInjection(PreferenceMembersInjector<?> injector) {
preferencesForInjection.add(injector);
}
public void injectPreferenceViews() {
for (int i = preferencesForInjection.size() - 1; i >= 0; --i)
preferencesForInjection.remove(i).reallyInjectMembers();
}
class PreferenceMembersInjector<T> implements MembersInjector<T> {
protected Field field;
protected Provider<Context> contextProvider;
protected InjectPreference annotation;
protected WeakReference<T> instanceRef;
public PreferenceMembersInjector(Field field, Provider<Context> contextProvider, InjectPreference annotation) {
this.field = field;
this.annotation = annotation;
this.contextProvider = contextProvider;
}
public void injectMembers(T instance) {
// Mark instance for injection during setContentView
this.instanceRef = new WeakReference<T>(instance);
registerPreferenceForInjection(this);
}
@SuppressWarnings("deprecation")
public void reallyInjectMembers() {
final T instance = instanceRef.get();
if (instance == null)
return;
Object value = null;
try {
value = ((PreferenceActivity) contextProvider.get()).findPreference(annotation.value());
if (value == null && Nullable.notNullable(field))
throw new NullPointerException(String.format("Can't inject null value into %s.%s when field is not @Nullable", field.getDeclaringClass(),
field.getName()));
field.setAccessible(true);
field.set(instance, value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException f) {
throw new IllegalArgumentException(String.format("Can't assign %s value %s to %s field %s", value != null ? value.getClass() : "(null)", value,
field.getType(), field.getName()));
}
}
}
}