| OLD | NEW |
| (Empty) |
| 1 part of di; | |
| 2 | |
| 3 class Injector { | |
| 4 | |
| 5 /** | |
| 6 * Name of the injector or null of none is given. | |
| 7 */ | |
| 8 final String name; | |
| 9 | |
| 10 static const List<Type> _PRIMITIVE_TYPES = const <Type>[ | |
| 11 num, int, double, String, bool | |
| 12 ]; | |
| 13 | |
| 14 /** | |
| 15 * Returns the parent injector or null if root. | |
| 16 */ | |
| 17 final Injector parent; | |
| 18 | |
| 19 Injector _root; | |
| 20 | |
| 21 Map<Type, _Provider> _providers = <Type, _Provider>{}; | |
| 22 | |
| 23 final Map<Type, Object> instances = <Type, Object>{}; | |
| 24 | |
| 25 final List<Type> resolving = <Type>[]; | |
| 26 | |
| 27 final bool allowImplicitInjection; | |
| 28 | |
| 29 Iterable<Type> _typesCache; | |
| 30 | |
| 31 /** | |
| 32 * List of all types which the injector can return | |
| 33 */ | |
| 34 Iterable<Type> get _types { | |
| 35 if (_typesCache == null) { | |
| 36 _typesCache = _providers.keys; | |
| 37 } | |
| 38 return _typesCache; | |
| 39 } | |
| 40 | |
| 41 Injector({List<Module> modules, String name, | |
| 42 bool allowImplicitInjection: false}) | |
| 43 : this.fromParent(modules, null, | |
| 44 name: name, allowImplicitInjection: allowImplicitInjection); | |
| 45 | |
| 46 Injector.fromParent(List<Module> modules, | |
| 47 Injector this.parent, {this.name, this.allowImplicitInjection}) { | |
| 48 _root = parent == null ? this : parent._root; | |
| 49 if (modules != null) { | |
| 50 modules.forEach((module) { | |
| 51 _providers.addAll(module._bindings); | |
| 52 }); | |
| 53 } | |
| 54 _providers[Injector] = new _ValueProvider(this); | |
| 55 } | |
| 56 | |
| 57 Injector get root => _root; | |
| 58 | |
| 59 Set<Type> get types { | |
| 60 var types = new Set.from(_types); | |
| 61 var parent = this.parent; | |
| 62 while (parent != null) { | |
| 63 types.addAll(parent._types); | |
| 64 parent = parent.parent; | |
| 65 } | |
| 66 return types; | |
| 67 } | |
| 68 | |
| 69 String _error(message, [appendDependency]) { | |
| 70 if (appendDependency != null) { | |
| 71 resolving.add(appendDependency); | |
| 72 } | |
| 73 | |
| 74 String graph = resolving.join(' -> '); | |
| 75 | |
| 76 resolving.clear(); | |
| 77 | |
| 78 return '$message (resolving $graph)'; | |
| 79 } | |
| 80 | |
| 81 dynamic _getInstanceByType(Type typeName, Injector requester) { | |
| 82 _checkTypeConditions(typeName); | |
| 83 | |
| 84 if (resolving.contains(typeName)) { | |
| 85 throw new CircularDependencyError( | |
| 86 _error('Cannot resolve a circular dependency!', typeName)); | |
| 87 } | |
| 88 | |
| 89 var providerWithInjector = _getProviderWithInjectorForType(typeName); | |
| 90 var provider = providerWithInjector.provider; | |
| 91 var injector = providerWithInjector.injector; | |
| 92 var visible = provider.visibility != null ? | |
| 93 provider.visibility(requester, injector) : | |
| 94 _defaultVisibility(requester, injector); | |
| 95 | |
| 96 if (visible && instances.containsKey(typeName)) { | |
| 97 return instances[typeName]; | |
| 98 } | |
| 99 | |
| 100 if (providerWithInjector.injector != this || !visible) { | |
| 101 if (!visible) { | |
| 102 if (injector.parent == null) { | |
| 103 throw new NoProviderError( | |
| 104 _error('No provider found for ${typeName}!', typeName)); | |
| 105 } | |
| 106 injector = | |
| 107 injector.parent._getProviderWithInjectorForType(typeName).injector; | |
| 108 } | |
| 109 return injector._getInstanceByType(typeName, requester); | |
| 110 } | |
| 111 | |
| 112 var value; | |
| 113 try { | |
| 114 var strategy = provider.creationStrategy != null ? | |
| 115 provider.creationStrategy : _defaultCreationStrategy; | |
| 116 value = strategy(requester, injector, () { | |
| 117 resolving.add(typeName); | |
| 118 var val = provider.get(this, requester, _getInstanceByType, _error); | |
| 119 resolving.removeLast(); | |
| 120 return val; | |
| 121 }); | |
| 122 } catch(e) { | |
| 123 resolving.clear(); | |
| 124 rethrow; | |
| 125 } | |
| 126 | |
| 127 // cache the value. | |
| 128 providerWithInjector.injector.instances[typeName] = value; | |
| 129 return value; | |
| 130 } | |
| 131 | |
| 132 /// Returns a pair for provider and the injector where it's defined. | |
| 133 _ProviderWithDefiningInjector _getProviderWithInjectorForType(Type typeName) { | |
| 134 if (_providers.containsKey(typeName)) { | |
| 135 return new _ProviderWithDefiningInjector(_providers[typeName], this); | |
| 136 } | |
| 137 | |
| 138 if (parent != null) { | |
| 139 return parent._getProviderWithInjectorForType(typeName); | |
| 140 } | |
| 141 | |
| 142 if (allowImplicitInjection) { | |
| 143 return new _ProviderWithDefiningInjector( | |
| 144 new _TypeProvider(typeName), this); | |
| 145 } | |
| 146 | |
| 147 throw new NoProviderError(_error('No provider found for ' | |
| 148 '${typeName}!', typeName)); | |
| 149 } | |
| 150 | |
| 151 void _checkTypeConditions(Type typeName) { | |
| 152 if (_PRIMITIVE_TYPES.contains(typeName)) { | |
| 153 throw new NoProviderError(_error('Cannot inject a primitive type ' | |
| 154 'of $typeName!', typeName)); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 | |
| 159 // PUBLIC API | |
| 160 | |
| 161 /** | |
| 162 * Get an instance for given token ([Type]). | |
| 163 * | |
| 164 * If the injector already has an instance for this token, it returns this | |
| 165 * instance. Otherwise, injector resolves all its dependencies, instantiate | |
| 166 * new instance and returns this instance. | |
| 167 * | |
| 168 * If there is no binding for given token, injector asks parent injector. | |
| 169 * | |
| 170 * If there is no parent injector, an implicit binding is used. That is, | |
| 171 * the token ([Type]) is instantiated. | |
| 172 */ | |
| 173 dynamic get(Type type) => _getInstanceByType(type, this); | |
| 174 | |
| 175 /** | |
| 176 * Create a child injector. | |
| 177 * | |
| 178 * Child injector can override any bindings by adding additional modules. | |
| 179 * | |
| 180 * It also accepts a list of tokens that a new instance should be forced. | |
| 181 * That means, even if some parent injector already has an instance for this | |
| 182 * token, there will be a new instance created in the child injector. | |
| 183 */ | |
| 184 Injector createChild(List<Module> modules, | |
| 185 {List<Type> forceNewInstances, String name}) { | |
| 186 if (forceNewInstances != null) { | |
| 187 Module forceNew = new Module(); | |
| 188 forceNewInstances.forEach((type) { | |
| 189 var providerWithInjector = _getProviderWithInjectorForType(type); | |
| 190 var provider = providerWithInjector.provider; | |
| 191 forceNew.factory(type, | |
| 192 (Injector inj) => provider.get(this, inj, inj._getInstanceByType, | |
| 193 inj._error), | |
| 194 creation: provider.creationStrategy, | |
| 195 visibility: provider.visibility); | |
| 196 }); | |
| 197 | |
| 198 modules = modules.toList(); // clone | |
| 199 modules.add(forceNew); | |
| 200 } | |
| 201 | |
| 202 return newFromParent(modules, name); | |
| 203 } | |
| 204 | |
| 205 newFromParent(List<Module> modules, String name) { | |
| 206 throw new UnimplementedError('This method must be overriden.'); | |
| 207 } | |
| 208 | |
| 209 Object newInstanceOf(Type type, ObjectFactory factory, Injector requestor, | |
| 210 errorHandler(message, [appendDependency])) { | |
| 211 throw new UnimplementedError('This method must be overriden.'); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 class _ProviderWithDefiningInjector { | |
| 216 final _Provider provider; | |
| 217 final Injector injector; | |
| 218 _ProviderWithDefiningInjector(this.provider, this.injector); | |
| 219 } | |
| OLD | NEW |