OLD | NEW |
(Empty) | |
| 1 library di.base_injector; |
| 2 |
| 3 import 'provider.dart'; |
| 4 import 'error_helper.dart'; |
| 5 |
| 6 import 'package:collection/collection.dart'; |
| 7 import 'package:di/di.dart'; |
| 8 |
| 9 List<Key> _PRIMITIVE_TYPES = new UnmodifiableListView(<Key>[ |
| 10 new Key(num), new Key(int), new Key(double), new Key(String), |
| 11 new Key(bool) |
| 12 ]); |
| 13 |
| 14 bool _defaultVisibility(_, __) => true; |
| 15 |
| 16 const ResolutionContext _ZERO_DEPTH_RESOLVING = |
| 17 const ResolutionContext(0, null, null); |
| 18 |
| 19 abstract class BaseInjector implements Injector, ObjectFactory { |
| 20 |
| 21 @override |
| 22 final String name; |
| 23 |
| 24 @override |
| 25 final BaseInjector parent; |
| 26 |
| 27 Injector _root; |
| 28 |
| 29 List<Provider> _providers; |
| 30 int _providersLen = 0; |
| 31 |
| 32 final Map<Key, Object> _instances = <Key, Object>{}; |
| 33 |
| 34 @override |
| 35 final bool allowImplicitInjection; |
| 36 |
| 37 Iterable<Type> _typesCache; |
| 38 |
| 39 Iterable<Type> get _types { |
| 40 if (_providers == null) return []; |
| 41 |
| 42 if (_typesCache == null) { |
| 43 _typesCache = _providers |
| 44 .where((p) => p != null) |
| 45 .map((p) => p.type); |
| 46 } |
| 47 return _typesCache; |
| 48 } |
| 49 |
| 50 BaseInjector({List<Module> modules, String name, |
| 51 bool allowImplicitInjection: false}) |
| 52 : this.fromParent(modules, null, |
| 53 name: name, allowImplicitInjection: allowImplicitInjection); |
| 54 |
| 55 BaseInjector.fromParent(List<Module> modules, |
| 56 BaseInjector this.parent, {this.name, this.allowImplicitInjection}) { |
| 57 _root = parent == null ? this : parent._root; |
| 58 var injectorId = new Key(Injector).id; |
| 59 _providers = new List(lastKeyId + 1); |
| 60 _providersLen = lastKeyId + 1; |
| 61 if (modules != null) { |
| 62 modules.forEach((module) { |
| 63 module.bindings.forEach((k, v) { |
| 64 _providers[k] = v; |
| 65 }); |
| 66 }); |
| 67 } |
| 68 _providers[injectorId] = new ValueProvider(Injector, this); |
| 69 } |
| 70 |
| 71 @override |
| 72 Injector get root => _root; |
| 73 |
| 74 @override |
| 75 Set<Type> get types { |
| 76 var types = new Set.from(_types); |
| 77 var parent = this.parent; |
| 78 while (parent != null) { |
| 79 types.addAll(parent._types); |
| 80 parent = parent.parent; |
| 81 } |
| 82 return types; |
| 83 } |
| 84 |
| 85 Object getInstanceByKey(Key key, Injector requester, ResolutionContext resolvi
ng) { |
| 86 assert(_checkKeyConditions(key, resolving)); |
| 87 |
| 88 // Do not bother checking the array until we are fairly deep. |
| 89 if (resolving.depth > 30 && resolvedTypes(resolving).contains(key)) { |
| 90 throw new CircularDependencyError( |
| 91 error(resolving, 'Cannot resolve a circular dependency!', key)); |
| 92 } |
| 93 |
| 94 var providerWithInjector = _getProviderWithInjectorForKey(key, resolving); |
| 95 var provider = providerWithInjector.provider; |
| 96 var injector = providerWithInjector.injector; |
| 97 var visible = provider.visibility != null ? |
| 98 provider.visibility(requester, injector) : |
| 99 _defaultVisibility(requester, injector); |
| 100 |
| 101 if (visible && _instances.containsKey(key)) return _instances[key]; |
| 102 |
| 103 if (providerWithInjector.injector != this || !visible) { |
| 104 if (!visible) { |
| 105 if (injector.parent == null) { |
| 106 throw new NoProviderError( |
| 107 error(resolving, 'No provider found for ${key}!', key)); |
| 108 } |
| 109 injector = injector.parent |
| 110 ._getProviderWithInjectorForKey(key, resolving).injector; |
| 111 } |
| 112 return injector.getInstanceByKey(key, requester, resolving); |
| 113 } |
| 114 |
| 115 resolving = new ResolutionContext(resolving.depth + 1, key, resolving); |
| 116 var value = provider.get(this, requester, this, resolving); |
| 117 |
| 118 // cache the value. |
| 119 providerWithInjector.injector._instances[key] = value; |
| 120 return value; |
| 121 } |
| 122 |
| 123 /// Returns a pair for provider and the injector where it's defined. |
| 124 _ProviderWithDefiningInjector _getProviderWithInjectorForKey( |
| 125 Key key, ResolutionContext resolving) { |
| 126 if (key.id < _providersLen) { |
| 127 var provider = _providers[key.id]; |
| 128 if (provider != null) { |
| 129 return new _ProviderWithDefiningInjector(provider, this); |
| 130 } |
| 131 } |
| 132 |
| 133 if (parent != null) { |
| 134 return parent._getProviderWithInjectorForKey(key, resolving); |
| 135 } |
| 136 |
| 137 if (allowImplicitInjection) { |
| 138 return new _ProviderWithDefiningInjector( |
| 139 new TypeProvider(key.type), this); |
| 140 } |
| 141 |
| 142 throw new NoProviderError( |
| 143 error(resolving, 'No provider found for ${key}!', key)); |
| 144 } |
| 145 |
| 146 bool _checkKeyConditions(Key key, ResolutionContext resolving) { |
| 147 if (_PRIMITIVE_TYPES.contains(key)) { |
| 148 throw new NoProviderError( |
| 149 error(resolving, |
| 150 'Cannot inject a primitive type of ${key.type}!', key)); |
| 151 } |
| 152 return true; |
| 153 } |
| 154 |
| 155 @override |
| 156 dynamic get(Type type, [Type annotation]) => |
| 157 getInstanceByKey(new Key(type, annotation), this, _ZERO_DEPTH_RESOLVING); |
| 158 |
| 159 @override |
| 160 dynamic getByKey(Key key) => |
| 161 getInstanceByKey(key, this, _ZERO_DEPTH_RESOLVING); |
| 162 |
| 163 @override |
| 164 Injector createChild(List<Module> modules, |
| 165 {List forceNewInstances, String name}) => |
| 166 createChildWithResolvingHistory(modules, _ZERO_DEPTH_RESOLVING, |
| 167 forceNewInstances: forceNewInstances, |
| 168 name: name); |
| 169 |
| 170 Injector createChildWithResolvingHistory( |
| 171 List<Module> modules, |
| 172 resolving, |
| 173 {List forceNewInstances, String name}) { |
| 174 if (forceNewInstances != null) { |
| 175 Module forceNew = new Module(); |
| 176 forceNewInstances.forEach((key) { |
| 177 if (key is Type) { |
| 178 key = new Key(key); |
| 179 } else if (key is! Key) { |
| 180 throw 'forceNewInstances must be List<Key|Type>'; |
| 181 } |
| 182 assert(key is Key); |
| 183 var providerWithInjector = |
| 184 _getProviderWithInjectorForKey(key, resolving); |
| 185 var provider = providerWithInjector.provider; |
| 186 forceNew.factoryByKey(key, (Injector inj) => provider.get(this, |
| 187 inj, inj as ObjectFactory, resolving), |
| 188 visibility: provider.visibility); |
| 189 }); |
| 190 |
| 191 modules = modules.toList(); // clone |
| 192 modules.add(forceNew); |
| 193 } |
| 194 |
| 195 return newFromParent(modules, name); |
| 196 } |
| 197 |
| 198 newFromParent(List<Module> modules, String name); |
| 199 |
| 200 Object newInstanceOf(Type type, ObjectFactory factory, Injector requestor, |
| 201 resolving); |
| 202 } |
| 203 |
| 204 class _ProviderWithDefiningInjector { |
| 205 final Provider provider; |
| 206 final BaseInjector injector; |
| 207 _ProviderWithDefiningInjector(this.provider, this.injector); |
| 208 } |
| 209 |
| 210 /** |
| 211 * Information about the context in which the [key] is being resolved, including |
| 212 * dependency tree [depth] at which the key is being resolved, as well as |
| 213 * [parent] context (used to determine circular dependencies). |
| 214 */ |
| 215 class ResolutionContext { |
| 216 final int depth; |
| 217 final Key key; |
| 218 final ResolutionContext parent; |
| 219 |
| 220 const ResolutionContext(this.depth, this.key, this.parent); |
| 221 } |
OLD | NEW |