001package com.gigya.android.sdk.containers; 002 003import com.gigya.android.sdk.GigyaLogger; 004 005import java.lang.reflect.Constructor; 006import java.lang.reflect.InvocationTargetException; 007import java.lang.reflect.Modifier; 008import java.util.ArrayList; 009import java.util.HashMap; 010import java.util.List; 011import java.util.Map; 012import java.util.MissingResourceException; 013 014@SuppressWarnings("unchecked") 015public class IoCContainer { 016 017 private static final String LOG_TAG = "IoCContainer"; 018 019 public static class BindInfo<T> { 020 Class<T> concrete; 021 boolean asSingleton; 022 public T instance; 023 024 BindInfo(Class<T> c, boolean s) { 025 concrete = c; 026 asSingleton = s; 027 } 028 029 BindInfo(T i) { 030 concrete = (Class<T>) i.getClass(); 031 asSingleton = true; 032 instance = i; 033 } 034 } 035 036 private Map<Class<?>, BindInfo> _bindings = new HashMap<>(); 037 038 public <I, C extends I> IoCContainer bind(Class<I> contractClazz, final Class<C> concreteClazz, final boolean asSingleton) { 039 GigyaLogger.ioc(LOG_TAG, "Binding " + contractClazz.getCanonicalName() + " to " + concreteClazz.getCanonicalName() + " as " + (asSingleton ? "singleton" : "factory")); 040 _bindings.put(contractClazz, new BindInfo<>(concreteClazz, asSingleton)); 041 return this; 042 } 043 044 public <I, C extends I> IoCContainer bind(Class<I> contractClazz, C concreteInstance) { 045 GigyaLogger.ioc(LOG_TAG, "binding " + contractClazz.getCanonicalName() + " to instance (of type " + concreteInstance.getClass().getCanonicalName() + ")"); 046 _bindings.put(contractClazz, new BindInfo<>(concreteInstance)); 047 return this; 048 } 049 050 public <T> T get(Class<T> contractClazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 051 GigyaLogger.ioc(LOG_TAG, "Trying to get: " + contractClazz.getCanonicalName()); 052 053 if (!isBound(contractClazz)) { 054 GigyaLogger.ioc(LOG_TAG, "Contract was not registered"); 055 return null; 056 } 057 058 BindInfo<T> bindingInfo = _bindings.get(contractClazz); 059 if (bindingInfo == null) { 060 GigyaLogger.ioc(LOG_TAG, "Contract was not registered = null"); 061 return null; 062 } 063 064 if (bindingInfo.instance != null) { 065 return bindingInfo.instance; 066 } 067 T instance = createInstance(bindingInfo.concrete); 068 if (bindingInfo.asSingleton) { 069 bindingInfo.instance = instance; 070 } 071 return instance; 072 } 073 074 public <T> T createInstance(Class<T> concreteClazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 075 return createInstance(concreteClazz, false); 076 } 077 078 public <T> T createInstance(Class<T> concreteClazz, boolean forceCreation) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 079 GigyaLogger.ioc(LOG_TAG, "Trying to create new instance for: " + concreteClazz.getCanonicalName()); 080 Constructor<?>[] constructors = concreteClazz.getDeclaredConstructors(); 081 if (constructors.length == 0) { 082 GigyaLogger.ioc(LOG_TAG, "Default constructor - creating instance"); 083 return concreteClazz.newInstance(); 084 } 085 086 for (Constructor<?> ctor : constructors) { 087 Class<?>[] ctorParams = ctor.getParameterTypes(); 088 GigyaLogger.ioc(LOG_TAG, "For constructor with params #: " + ctorParams.length); 089 090 List<Object> params = new ArrayList<>(); 091 for (Class paramContract : ctorParams) { 092 GigyaLogger.ioc(LOG_TAG, "Getting required param: " + paramContract.getCanonicalName()); 093 094 Object paramInstance = get(paramContract); 095 096 if (paramInstance == null && forceCreation) { 097 paramInstance = createInstance(paramContract); 098 } 099 100 if (paramInstance != null) { 101 params.add(paramInstance); 102 } else { 103 // missing param, so keep searching 104 break; 105 } 106 } 107 108 if (params.size() == ctorParams.length) { 109 GigyaLogger.ioc(LOG_TAG, "Creating new instance for " + concreteClazz.getCanonicalName()); 110 if (Modifier.isProtected(ctor.getModifiers())) { 111 GigyaLogger.ioc(LOG_TAG, "Constructor is protected"); 112 ctor.setAccessible(true); 113 } 114 return (T) ctor.newInstance(params.toArray()); 115 } else { 116 GigyaLogger.ioc(LOG_TAG, "Constructor wasn't suitable"); 117 } 118 } 119 120 throw new MissingResourceException("Concrete class missing dependencies", concreteClazz.getName(), "iocContainer"); 121 } 122 123 public boolean isBound(Class contractClazz) { 124 return _bindings.containsKey(contractClazz); 125 } 126 127 public IoCContainer clone() { 128 IoCContainer clone = new IoCContainer(); 129 clone._bindings = new HashMap<>(this._bindings); 130 return clone; 131 } 132 133 public void dispose() { 134 _bindings.clear(); 135 _bindings = null; 136 } 137}