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 |