comparison core/src/js/di/Container.js @ 34:27e8e9e38e07 default tip

Слияние
author nickolay
date Wed, 05 Jun 2019 20:44:15 +0300
parents acdcdf1a8d21
children
comparison
equal deleted inserted replaced
33:8af8e840dd49 34:27e8e9e38e07
1 define([
2 "../declare",
3 "../safe",
4 "../Uuid",
5 "../Deferred",
6 "./ActivationContext",
7 "./Descriptor",
8 "./ValueDescriptor",
9 "./ReferenceDescriptor",
10 "./ServiceDescriptor",
11 "./ActivationError"
12 ], function (
13 declare,
14 safe,
15 Uuid,
16 Deferred,
17 ActivationContext,
18 Descriptor,
19 Value,
20 Reference,
21 Service,
22 ActivationError) {
23 var Container = declare(null, {
24 _services: null,
25 _cache: null,
26 _cleanup: null,
27 _root: null,
28 _parent: null,
29
30 constructor: function (parent) {
31 this._parent = parent;
32 this._services = parent ? Object.create(parent._services) : {};
33 this._cache = {};
34 this._cleanup = [];
35 this._root = parent ? parent.getRootContainer() : this;
36 this._services.container = new Value(this, true);
37 },
38
39 getRootContainer: function () {
40 return this._root;
41 },
42
43 getParent: function () {
44 return this._parent;
45 },
46
47 /**
48 *
49 */
50 getService: function (name, def) {
51 var d = this._services[name];
52 if (!d)
53 if (arguments.length > 1)
54 return def;
55 else
56 throw new Error("Service '" + name + "' isn't found");
57 if (d.isInstanceCreated())
58 return d.getInstance();
59
60 var context = new ActivationContext(this, this._services);
61
62 try {
63 return d.activate(context, name);
64 } catch (error) {
65 throw new ActivationError(name, context.getStack(), error);
66 }
67 },
68
69 register: function (name, service) {
70 if (arguments.length == 1) {
71 var data = name;
72 for (name in data)
73 this.register(name, data[name]);
74 } else {
75 if (!(service instanceof Descriptor))
76 service = new Value(service, true);
77 this._services[name] = service;
78 }
79 return this;
80 },
81
82 onDispose: function (callback) {
83 if (!(callback instanceof Function))
84 throw new Error("The callback must be a function");
85 this._cleanup.push(callback);
86 },
87
88 dispose: function () {
89 if (this._cleanup) {
90 for (var i = 0; i < this._cleanup.length; i++)
91 this._cleanup[i].call(null);
92 this._cleanup = null;
93 }
94 },
95
96 /**
97 * @param{String|Object} config
98 * The configuration of the contaier. Can be either a string or an object,
99 * if the configuration is an object it's treated as a collection of
100 * services which will be registed in the contaier.
101 *
102 * @param{Function} opts.contextRequire
103 * The function which will be used to load a configuration or types for services.
104 *
105 */
106 configure: function (config, opts) {
107 var p, me = this,
108 contextRequire = (opts && opts.contextRequire);
109
110 if (typeof (config) === "string") {
111 p = new Deferred();
112 if (!contextRequire) {
113 var shim = [config, new Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
114 define(shim, ["require", config], function (ctx, data) {
115 p.resolve([data, {
116 contextRequire: ctx
117 }]);
118 });
119 require([shim]);
120 } else {
121 // TODO how to get correct contextRequire for the relative config module?
122 contextRequire([config], function (data) {
123 p.resolve([data, {
124 contextRequire: contextRequire
125 }]);
126 });
127 }
128
129 return p.then(function (args) {
130 return me._configure.apply(me, args);
131 });
132 } else {
133 return me._configure(config, opts);
134 }
135 },
136
137 createChildContainer: function () {
138 return new Container(this);
139 },
140
141 has: function (id) {
142 return id in this._cache;
143 },
144
145 get: function (id) {
146 return this._cache[id];
147 },
148
149 store: function (id, value) {
150 return (this._cache[id] = value);
151 },
152
153 _configure: function (data, opts) {
154 var typemap = {},
155 d = new Deferred(),
156 me = this,
157 p,
158 contextRequire = (opts && opts.contextRequire) || require;
159
160 var services = {};
161
162 for (p in data) {
163 var service = me._parse(data[p], typemap);
164 if (!(service instanceof Descriptor))
165 service = new Value(service, false);
166 services[p] = service;
167 }
168
169 me.register(services);
170
171 var names = [];
172
173 for (p in typemap)
174 names.push(p);
175
176 if (names.length) {
177 contextRequire(names, function () {
178 for (var i = 0; i < names.length; i++)
179 typemap[names[i]] = arguments[i];
180 d.resolve(me);
181 });
182 } else {
183 d.resolve(me);
184 }
185 return d.promise;
186 },
187
188 _parse: function (data, typemap) {
189 if (safe.isPrimitive(data) || data instanceof Descriptor)
190 return data;
191 if (data.$dependency)
192 return new Reference(
193 data.$dependency,
194 data.lazy,
195 data.optional,
196 data["default"],
197 data.services && this._parseObject(data.services, typemap));
198 if (data.$value) {
199 var raw = !data.parse;
200 return new Value(raw ? data.$value : this._parse(
201 data.$value,
202 typemap), raw);
203 }
204 if (data.$type || data.$factory)
205 return this._parseService(data, typemap);
206 if (data instanceof Array)
207 return this._parseArray(data, typemap);
208
209 return this._parseObject(data, typemap);
210 },
211
212 _parseService: function (data, typemap) {
213 var me = this,
214 opts = {
215 owner: this
216 };
217 if (data.$type) {
218
219 opts.type = data.$type;
220
221 if (typeof (data.$type) === "string") {
222 typemap[data.$type] = null;
223 opts.typeMap = typemap;
224 }
225 }
226
227 if (data.$factory)
228 opts.factory = data.$factory;
229
230 if (data.services)
231 opts.services = me._parseObject(data.services, typemap);
232 if (data.inject)
233 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
234 return me._parseObject(x, typemap);
235 }) : me._parseObject(data.inject, typemap);
236 if (data.params)
237 opts.params = me._parse(data.params, typemap);
238
239 if (data.activation) {
240 if (typeof (data.activation) === "string") {
241 switch (data.activation.toLowerCase()) {
242 case "singleton":
243 opts.activation = Service.SINGLETON;
244 break;
245 case "container":
246 opts.activation = Service.CONTAINER;
247 break;
248 case "hierarchy":
249 opts.activation = Service.HIERARCHY;
250 break;
251 case "context":
252 opts.activation = Service.CONTEXT;
253 break;
254 case "call":
255 opts.activation = Service.CALL;
256 break;
257 default:
258 throw new Error("Unknown activation type: " +
259 data.activation);
260 }
261 } else {
262 opts.activation = Number(data.activation);
263 }
264 }
265
266 if (data.cleanup)
267 opts.cleanup = data.cleanup;
268
269 return new Service(opts);
270 },
271
272 _parseObject: function (data, typemap) {
273 if (data.constructor &&
274 data.constructor.prototype !== Object.prototype)
275 return new Value(data, true);
276
277 var o = {};
278
279 for (var p in data)
280 o[p] = this._parse(data[p], typemap);
281
282 return o;
283 },
284
285 _parseArray: function (data, typemap) {
286 if (data.constructor &&
287 data.constructor.prototype !== Array.prototype)
288 return new Value(data, true);
289
290 var me = this;
291 return data.map(function (x) {
292 return me._parse(x, typemap);
293 });
294 }
295
296 });
297
298 return Container;
299 });