package com.todoroo.andlib.service; import java.lang.reflect.Field; import java.util.Arrays; import android.util.Log; import com.todoroo.astrid.utility.Constants; /** * Simple Dependency Injection Service for Android. *

* Add dependency injectors to the injector chain, then invoke this method * against classes you wish to perform dependency injection for. *

* All errors encountered are handled as warnings, so if dependency injection * seems to be failing, check the logs for more information. * * @author Tim Su * */ public class DependencyInjectionService { private static final String QUALIFIED_PACKAGE = "com.t"; //$NON-NLS-1$ /** * Dependency injectors. Use getters and setters to modify this list */ private AbstractDependencyInjector[] injectors = {}; /** * Perform dependency injection in the caller object * * @param caller * object to perform DI on */ @SuppressWarnings("nls") public void inject(Object caller) { if(Constants.DEBUG) { Log.d("INJECTOR", "Invoked Injector on " + caller, new Throwable()); } // Traverse through class and all parent classes, looking for // fields declared with the @Autowired annotation and using // dependency injection to set them as appropriate Class cls = caller.getClass(); while(cls != null) { String packageName = cls.getPackage().getName(); if(!packageName.startsWith(QUALIFIED_PACKAGE)) break; for(Field field : cls.getDeclaredFields()) { if(field.getAnnotation(Autowired.class) != null) { field.setAccessible(true); try { handleField(caller, field); } catch (IllegalStateException e) { throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'", field.getName(), field.getType()), e); } catch (IllegalArgumentException e) { throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'", field.getName(), field.getType()), e); } catch (IllegalAccessException e) { throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'", field.getName(), field.getType()), e); } } } cls = cls.getSuperclass(); } } /** * This method returns the appropriate dependency object based on the type * that this autowired field accepts * * @param caller * calling object * @param field * field to inject */ @SuppressWarnings("nls") private void handleField(Object caller, Field field) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { if(field.getType().isPrimitive()) throw new IllegalStateException(String.format( "Tried to dependency-inject primative field '%s' of type '%s'", field.getName(), field.getType())); // field has already been processed, ignore if (field.get(caller) != null) { return; } for (AbstractDependencyInjector injector : injectors) { Object injection = injector.getInjection(caller, field); if (injection != null) { if(Constants.DEBUG) Log.e("INJECTOR", injector + ":" + caller + "." + field.getName() + " => " + injection); field.set(caller, injection); return; } } throw new IllegalStateException( String.format("No dependency injector found for autowired " + "field '%s' in class '%s'. Injectors: %s", field.getName(), caller.getClass().getName(), Arrays.asList(getInjectors()))); } // --- static methods private static DependencyInjectionService instance = null; DependencyInjectionService() { // prevent instantiation } /** * Gets the singleton instance of the dependency injection service. * @return */ public synchronized static DependencyInjectionService getInstance() { if(instance == null) instance = new DependencyInjectionService(); return instance; } /** * Gets the array of installed injectors * @return */ public synchronized AbstractDependencyInjector[] getInjectors() { return injectors; } /** * Sets the array of installed injectors * @param injectors */ public synchronized void setInjectors(AbstractDependencyInjector[] injectors) { this.injectors = injectors; } }