com.nikedlab.android.inject.DirectApplicationContext Maven / Gradle / Ivy
package com.nikedlab.android.inject;
import android.app.Activity;
import android.content.Context;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("TypeParameterExplicitlyExtendsObject")
public class DirectApplicationContext implements ApplicationContext {
private Map, Class extends Object>> binding;
private Map> bindingByName;
private Map, Object> singletons;
private Map singletonByName;
private Map, BindingFactory> factories;
private List beans;
public DirectApplicationContext() {
binding = new HashMap, Class extends Object>>();
bindingByName = new HashMap>();
singletons = new HashMap, Object>();
singletonByName = new HashMap();
factories = new HashMap, BindingFactory>();
}
public void bind(Class extends Object> interfaceClass, Class extends Object> implementationClass) {
binding.put(interfaceClass, implementationClass);
}
public void bind(Class extends Object> implementationClass) {
Component component = implementationClass.getAnnotation(Component.class);
if (component != null) {
if (component.value() != null) {
bindingByName.put(component.value(), implementationClass);
}
}
binding.put(implementationClass, implementationClass);
}
public void bindInstance(Class extends Object> interfaceClass, Object singleton) {
singletons.put(interfaceClass, singleton);
}
public void bindFactory(Class extends Object> interfaceClass, BindingFactory factory) {
factories.put(interfaceClass, factory);
}
@Override
public void setConfig(List beans) {
this.beans = beans;
}
public void inject(Object target) {
Class extends Object> targetClass = target.getClass();
Method[] methods = targetClass.getMethods();
for (Method method : methods) {
if (method.getName().startsWith("set")) {
Autowired inject = method.getAnnotation(Autowired.class);
if (inject != null) {
injectParameters(target, method);
}
}
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T getBean(Class interfaceClass) {
return (T) getSingleton(interfaceClass);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T getBean(String name) {
return (T) getSingleton(name);
}
/**
* {@inheritDoc}
*/
public boolean containsType(Class interfaceClass) {
return (singletons.containsKey(interfaceClass) || binding.containsKey(interfaceClass) || factories.containsKey(interfaceClass));
}
private void injectParameters(Object target, Method method) {
Class extends Object>[] parameterTypes = method.getParameterTypes();
Object[] parameters = new Object[parameterTypes.length];
int parameterIndex = 0;
boolean allParametersInstantiated = true;
for (Class extends Object> parameterType : parameterTypes) {
if (singletons.containsKey(parameterType) || binding.containsKey(parameterType) || factories.containsKey(parameterType)) {
parameters[parameterIndex] = getSingleton(parameterType);
} else {
allParametersInstantiated = false;
}
parameterIndex++;
}
if (allParametersInstantiated) {
try {
method.invoke(target, parameters);
} catch (IllegalArgumentException e) {
throw new InjectException(e);
} catch (IllegalAccessException e) {
throw new InjectException(e);
} catch (InvocationTargetException e) {
throw new InjectException(e);
}
}
}
private synchronized Object getSingleton(Class extends Object> parameterType) {
Object singleton;
if (singletons.containsKey(parameterType)) {
singleton = singletons.get(parameterType);
} else {
if (factories.containsKey(parameterType)) {
BindingFactory factory = factories.get(parameterType);
singleton = factory.getBinding();
} else if (binding.containsKey(parameterType)) {
Class extends Object> implementationClass = binding.get(parameterType);
if (implementationClass == null) {
throw new BeanNotFoundException("no binding found for type: " + parameterType.toString());
}
singleton = createSingleton(implementationClass);
} else {
throw new InjectException("Failed to find binding for type: " + parameterType.getCanonicalName());
}
singletons.put(parameterType, singleton);
}
return singleton;
}
private synchronized Object getSingleton(String name) {
Object singleton;
if (singletonByName.containsKey(name)) {
singleton = singletonByName.get(name);
} else {
Class extends Object> implementationClass = bindingByName.get(name);
if (implementationClass == null) {
throw new BeanNotFoundException("no binding found for name: " + name);
}
singleton = createSingleton(implementationClass);
singletonByName.put(name, singleton);
}
return singleton;
}
private Object createSingleton(Class extends Object> implementationClass) {
Object singleton = null;
try {
Bean bean = _getCurrentBean(implementationClass);
if (bean.isSystemService()) {
singleton = _getSystemService(bean.getClazz());
} else if (bean.getArgs().size() > 0) {
singleton = _composeInstanceWithConstructor(implementationClass, bean);
} else if (bean.isSingleton()) {
boolean isReallySingleton = bean.isSingleton();
if (!isReallySingleton) {
singleton = implementationClass.newInstance();
} else {
Method method = implementationClass.getMethod("getInstance", new Class[0]);
singleton = method.invoke(implementationClass);
}
} else if (SimpleInjector.class.isAssignableFrom(implementationClass)){
singleton = implementationClass.newInstance();
} else {
singleton = implementationClass.newInstance();
}
Component component = implementationClass.getAnnotation(Component.class);
if (component != null) {
inject(singleton);
}
} catch (InstantiationException e) {
throw new BeanInstantiationException(e);
} catch (IllegalAccessException e) {
throw new BeanInstantiationException(e);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return singleton;
}
private Object _getSystemService(String clazz) {
Context context = getBean(Context.class);
Object result = null;
if ("android.app.admin.DevicePolicyManager".equals(clazz)) {
result = context.getSystemService(Context.DEVICE_POLICY_SERVICE);
} else if ("android.telephony.TelephonyManager".equals(clazz)) {
result = context.getSystemService(Context.TELEPHONY_SERVICE);
} else if ("android.media.AudioManager".equals(clazz)) {
result = context.getSystemService(Context.AUDIO_SERVICE);
} else if ("android.os.PowerManager".equals(clazz)) {
result = context.getSystemService(Context.POWER_SERVICE);
} else if ("android.net.ConnectivityManager".equals(clazz)) {
result = context.getSystemService(Context.CONNECTIVITY_SERVICE);
} else if ("android.os.Vibrator".equals(clazz)) {
result = context.getSystemService(Context.VIBRATOR_SERVICE);
} else if ("android.net.wifi.WifiManager".equals(clazz)) {
result = context.getSystemService(Context.WIFI_SERVICE);
} else if ("android.location.LocationManager".equals(clazz)) {
result = context.getSystemService(Context.LOCATION_SERVICE);
} else if ("android.app.NotificationManager".equals(clazz)) {
result = context.getSystemService(Context.NOTIFICATION_SERVICE);
} else if ("android.app.ActivityManager".equals(clazz)) {
result = context.getSystemService( Activity.ACTIVITY_SERVICE );
} else if ("android.view.WindowManager".equals(clazz)) {
result = context.getSystemService( Context.WINDOW_SERVICE );
}
return result;
}
private Object _composeInstanceWithConstructor(Class> implementationClass, Bean bean) {
List args = bean.getArgs();
Constructor>[] constructors = implementationClass.getDeclaredConstructors();
for (Constructor> item : constructors) {
if (item.getParameterTypes().length == args.size()) {
boolean coincidence = true;
for (int i = 0; i < item.getParameterTypes().length; i++) {
Class>[] parameters = item.getParameterTypes();
if (!parameters[i].getName().equals(args.get(i).getType())
&& !parameters[i].getName().equals(args.get(i).getReferral())
) {
coincidence = false;
break;
}
}
boolean isSingleton = bean.isSingleton();
if (coincidence) {
Object[] objParams = new Object[args.size()];
int i = 0;
for (Bean.Args arg : args) {
if (arg.getType().equals("int") || arg.getType().equals("java.lang.Integer")) {
objParams[i] = Integer.parseInt(arg.getValue());
} else if (arg.getType().equals("long") || arg.getType().equals("java.lang.Long")) {
objParams[i] = Long.parseLong(arg.getValue());
} else if (arg.getType().equals("java.lang.String")) {
objParams[i] = arg.getValue();
} else if (arg.getReferral() != null) {
try {
objParams[i] = getBean(Class.forName(arg.getReferral()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else if (bean.isSingleton()) {
break;
} else if (arg.getType().equals("android.content.Context")) {
objParams[i] = getBean(Context.class);
}
i++;
}
try {
if (!isSingleton) {
return item.newInstance(objParams);
} else {
Class>[] classes = new Class[args.size()];
Object[] objects = new Object[args.size()];
int j = 0;
for (Bean.Args arg : args) {
Class> aClass = Class.forName(arg.getType());
classes[j] = aClass;
if ("".equals(arg.getValue()) && null == arg.getReferral()) {
objects[j] = getBean(aClass);
} else if ("".equals(arg.getValue()) && null != arg.getReferral()) {
objects[j] = getBean(aClass);
} else if (!"".equals(arg.getValue())) {
if (arg.getType().equals("int") || arg.getType().equals("java.lang.Integer")) {
objects[j] = Integer.parseInt(arg.getValue());
} else if (arg.getType().equals("long") || arg.getType().equals("java.lang.Long")) {
objects[j] = Long.parseLong(arg.getValue());
} else if (arg.getType().equals("java.lang.String")) {
objects[j] = arg.getValue();
}
}
j++;
}
Method method = implementationClass.getMethod("getInstance", classes);
return method.invoke(implementationClass, objects);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
return null;
}
/**
* Getter for current handled bean
* @param implementationClass Class for injecting
* @return Bean object {@link Bean}
*/
private Bean _getCurrentBean(Class extends Object> implementationClass) {
for (Bean item : beans) {
if (item.getClazz().equals(implementationClass.getName())) {
return item;
}
}
return null;
}
}