Mercurial > pub > ImplabJs
comparison src/implab/di/ServiceDescriptor.js @ 34:27e8e9e38e07 default tip
Слияние
author | nickolay |
---|---|
date | Wed, 05 Jun 2019 20:44:15 +0300 |
parents | 8af8e840dd49 1dc2fd263b90 |
children |
comparison
equal
deleted
inserted
replaced
33:8af8e840dd49 | 34:27e8e9e38e07 |
---|---|
1 define( | |
2 [ | |
3 "../declare", | |
4 "../safe", | |
5 "./Descriptor", | |
6 "./ValueDescriptor" | |
7 ], | |
8 | |
9 function (declare, safe, Descriptor, Value) { | |
10 var SINGLETON_ACTIVATION = 1, | |
11 CONTAINER_ACTIVATION = 2, | |
12 CONTEXT_ACTIVATION = 3, | |
13 CALL_ACTIVATION = 4, | |
14 HIERARCHY_ACTIVATION = 5; | |
15 | |
16 var injectMethod = function (target, method, context, args) { | |
17 var m = target[method]; | |
18 if (!m) | |
19 throw new Error("Method '" + method + "' not found"); | |
20 | |
21 if (args instanceof Array) | |
22 m.apply(target, context.parse(args, "." + method)); | |
23 else | |
24 m.call(target, context.parse(args, "." + method)); | |
25 }; | |
26 | |
27 var makeClenupCallback = function (target, method) { | |
28 if (typeof (method) === "string") { | |
29 return function () { | |
30 target[method](); | |
31 }; | |
32 } else { | |
33 return function () { | |
34 method(target); | |
35 }; | |
36 } | |
37 }; | |
38 | |
39 var cacheId = 0; | |
40 | |
41 var cls = declare( | |
42 Descriptor, { | |
43 _instance: null, | |
44 _hasInstance: false, | |
45 _activationType: CALL_ACTIVATION, | |
46 _services: null, | |
47 _type: null, | |
48 _typeMap: null, | |
49 _factory: null, | |
50 _params: undefined, | |
51 _inject: null, | |
52 _cleanup: null, | |
53 _cacheId: null, | |
54 _owner: null, | |
55 | |
56 constructor: function (opts) { | |
57 safe.argumentNotNull(opts, "opts"); | |
58 safe.argumentNotNull(opts.owner, "opts.owner"); | |
59 | |
60 this._owner = opts.owner; | |
61 | |
62 if (!(opts.type || opts.factory)) | |
63 throw new Error( | |
64 "Either a type or a factory must be specified"); | |
65 | |
66 if (typeof (opts.type) === "string" && !opts.typeMap) | |
67 throw new Error( | |
68 "The typeMap is required when the type is specified by its name"); | |
69 | |
70 if (opts.activation) | |
71 this._activationType = opts.activation; | |
72 if (opts.type) | |
73 this._type = opts.type; | |
74 if (opts.params) | |
75 this._params = opts.params; | |
76 if (opts.inject) | |
77 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject]; | |
78 if (opts.services) | |
79 this._services = opts.services; | |
80 if (opts.factory) | |
81 this._factory = opts.factory; | |
82 if (opts.typeMap) | |
83 this._typeMap = opts.typeMap; | |
84 if (opts.cleanup) { | |
85 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function)) | |
86 throw new Error( | |
87 "The cleanup parameter must be either a function or a function name"); | |
88 | |
89 this._cleanup = opts.cleanup; | |
90 } | |
91 | |
92 this._cacheId = ++cacheId; | |
93 }, | |
94 | |
95 activate: function (context, name) { | |
96 | |
97 // if we have a local service records, register them first | |
98 | |
99 var instance; | |
100 | |
101 switch (this._activationType) { | |
102 case 1: // SINGLETON | |
103 // if the value is cached return it | |
104 if (this._hasInstance) | |
105 return this._instance; | |
106 | |
107 var tof = this._type || this._factory; | |
108 | |
109 // create the persistent cache identifier for the type | |
110 if (safe.isPrimitive(tof)) | |
111 this._cacheId = this._type; | |
112 else | |
113 this._cacheId = safe.oid(tof); | |
114 | |
115 // singletons are bound to the root container | |
116 var container = context.container.getRootContainer(); | |
117 | |
118 if (container.has(this._cacheId)) { | |
119 instance = container.get(this._cacheId); | |
120 } else { | |
121 instance = this._create(context, name); | |
122 container.store(this._cacheId, instance); | |
123 if (this._cleanup) | |
124 container.onDispose( | |
125 makeClenupCallback(instance, this._cleanup)); | |
126 } | |
127 | |
128 this._hasInstance = true; | |
129 return (this._instance = instance); | |
130 | |
131 case 2: // CONTAINER | |
132 //return a cached value | |
133 if (this._hasInstance) | |
134 return this._instance; | |
135 | |
136 // create an instance | |
137 instance = this._create(context, name); | |
138 | |
139 // the instance is bound to the container | |
140 if (this._cleanup) | |
141 this._owner.onDispose( | |
142 makeClenupCallback(instance, this._cleanup)); | |
143 | |
144 // cache and return the instance | |
145 this._hasInstance = true; | |
146 return (this._instance = instance); | |
147 case 3: // CONTEXT | |
148 //return a cached value if one exists | |
149 if (context.has(this._cacheId)) | |
150 return context.get(this._cacheId); | |
151 // context context activated instances are controlled by callers | |
152 return context.store(this._cacheId, this._create( | |
153 context, | |
154 name)); | |
155 case 4: // CALL | |
156 // per-call created instances are controlled by callers | |
157 return this._create(context, name); | |
158 case 5: // HIERARCHY | |
159 // hierarchy activated instances are behave much like container activated | |
160 // except they are created and bound to the child container | |
161 | |
162 // return a cached value | |
163 if (context.container.has(this._cacheId)) | |
164 return context.container.get(this._cacheId); | |
165 | |
166 instance = this._create(context, name); | |
167 | |
168 if (this._cleanup) | |
169 context.container.onDispose(makeClenupCallback( | |
170 instance, | |
171 this._cleanup)); | |
172 | |
173 return context.container.store(this._cacheId, instance); | |
174 default: | |
175 throw "Invalid activation type: " + this._activationType; | |
176 } | |
177 }, | |
178 | |
179 isInstanceCreated: function () { | |
180 return this._hasInstance; | |
181 }, | |
182 | |
183 getInstance: function () { | |
184 return this._instance; | |
185 }, | |
186 | |
187 _create: function (context, name) { | |
188 context.enter(name, this, Boolean(this._services)); | |
189 | |
190 if (this._activationType != CALL_ACTIVATION && | |
191 context.visit(this._cacheId) > 0) | |
192 throw new Error("Recursion detected"); | |
193 | |
194 if (this._services) { | |
195 for (var p in this._services) { | |
196 var sv = this._services[p]; | |
197 context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false)); | |
198 } | |
199 } | |
200 | |
201 var instance; | |
202 | |
203 if (!this._factory) { | |
204 var ctor, type = this._type; | |
205 | |
206 if (typeof (type) === "string") { | |
207 ctor = this._typeMap[type]; | |
208 if (!ctor) | |
209 throw new Error("Failed to resolve the type '" + | |
210 type + "'"); | |
211 } else { | |
212 ctor = type; | |
213 } | |
214 | |
215 if (this._params === undefined) { | |
216 this._factory = function () { | |
217 return new ctor(); | |
218 }; | |
219 } else if (this._params instanceof Array) { | |
220 this._factory = function () { | |
221 var inst = Object.create(ctor.prototype); | |
222 var ret = ctor.apply(inst, arguments); | |
223 return typeof (ret) === "object" ? ret : inst; | |
224 }; | |
225 } else { | |
226 this._factory = function (param) { | |
227 return new ctor(param); | |
228 }; | |
229 } | |
230 } | |
231 | |
232 if (this._params === undefined) { | |
233 instance = this._factory(); | |
234 } else if (this._params instanceof Array) { | |
235 instance = this._factory.apply(this, context.parse( | |
236 this._params, | |
237 ".params")); | |
238 } else { | |
239 instance = this._factory(context.parse( | |
240 this._params, | |
241 ".params")); | |
242 } | |
243 | |
244 if (this._inject) { | |
245 this._inject.forEach(function (spec) { | |
246 for (var m in spec) | |
247 injectMethod(instance, m, context, spec[m]); | |
248 }); | |
249 } | |
250 | |
251 context.leave(); | |
252 | |
253 return instance; | |
254 }, | |
255 | |
256 // @constructor {singleton} foo/bar/Baz | |
257 // @factory {singleton} | |
258 toString: function () { | |
259 var parts = []; | |
260 | |
261 parts.push(this._type ? "@constructor" : "@factory"); | |
262 | |
263 parts.push(activationNames[this._activationType]); | |
264 | |
265 if (typeof (this._type) === "string") | |
266 parts.push(this._type); | |
267 | |
268 return parts.join(" "); | |
269 } | |
270 | |
271 }); | |
272 | |
273 cls.SINGLETON = SINGLETON_ACTIVATION; | |
274 cls.CONTAINER = CONTAINER_ACTIVATION; | |
275 cls.CONTEXT = CONTEXT_ACTIVATION; | |
276 cls.CALL = CALL_ACTIVATION; | |
277 cls.HIERARCHY = HIERARCHY_ACTIVATION; | |
278 | |
279 var activationNames = [ | |
280 "", | |
281 "{singleton}", | |
282 "{container}", | |
283 "{context}", | |
284 "{call}", | |
285 "{hierarchy}" | |
286 ]; | |
287 | |
288 return cls; | |
289 }); |