ServiceDescriptor.js
288 lines
| 11.1 KiB
| application/javascript
|
JavascriptLexer
|
|
r0 | define( | |
| [ | |||
| "../declare", | |||
| "../safe", | |||
| "./Descriptor", | |||
| "./ValueDescriptor" | |||
| ], | |||
| function (declare, safe, Descriptor, Value) { | |||
| var SINGLETON_ACTIVATION = 1, | |||
| CONTAINER_ACTIVATION = 2, | |||
| CONTEXT_ACTIVATION = 3, | |||
| CALL_ACTIVATION = 4, | |||
| HIERARCHY_ACTIVATION = 5; | |||
| var injectMethod = function (target, method, context, args) { | |||
| var m = target[method]; | |||
| if (!m) | |||
| throw new Error("Method '" + method + "' not found"); | |||
| if (args instanceof Array) | |||
| m.apply(target, context.parse(args, "." + method)); | |||
| else | |||
| m.call(target, context.parse(args, "." + method)); | |||
| }; | |||
| var makeClenupCallback = function (target, method) { | |||
| if (typeof (method) === "string") { | |||
| return function () { | |||
| target[method](); | |||
| }; | |||
| } else { | |||
| return function () { | |||
| method(target); | |||
| }; | |||
| } | |||
| }; | |||
| var cacheId = 0; | |||
| var cls = declare( | |||
| Descriptor, { | |||
| _instance: null, | |||
| _hasInstance: false, | |||
| _activationType: CALL_ACTIVATION, | |||
| _services: null, | |||
| _type: null, | |||
| _typeMap: null, | |||
| _factory: null, | |||
| _params: undefined, | |||
| _inject: null, | |||
| _cleanup: null, | |||
| _cacheId: null, | |||
| _owner: null, | |||
| constructor: function (opts) { | |||
| safe.argumentNotNull(opts, "opts"); | |||
| safe.argumentNotNull(opts.owner, "opts.owner"); | |||
| this._owner = opts.owner; | |||
| if (!(opts.type || opts.factory)) | |||
| throw new Error( | |||
| "Either a type or a factory must be specified"); | |||
| if (typeof (opts.type) === "string" && !opts.typeMap) | |||
| throw new Error( | |||
| "The typeMap is required when the type is specified by its name"); | |||
| if (opts.activation) | |||
| this._activationType = opts.activation; | |||
| if (opts.type) | |||
| this._type = opts.type; | |||
| if (opts.params) | |||
| this._params = opts.params; | |||
| if (opts.inject) | |||
| this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject]; | |||
| if (opts.services) | |||
| this._services = opts.services; | |||
| if (opts.factory) | |||
| this._factory = opts.factory; | |||
| if (opts.typeMap) | |||
| this._typeMap = opts.typeMap; | |||
| if (opts.cleanup) { | |||
| if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function)) | |||
| throw new Error( | |||
| "The cleanup parameter must be either a function or a function name"); | |||
| this._cleanup = opts.cleanup; | |||
| } | |||
| this._cacheId = ++cacheId; | |||
| }, | |||
| activate: function (context, name) { | |||
| // if we have a local service records, register them first | |||
| var instance; | |||
| switch (this._activationType) { | |||
| case 1: // SINGLETON | |||
| // if the value is cached return it | |||
| if (this._hasInstance) | |||
| return this._instance; | |||
| var tof = this._type || this._factory; | |||
| // create the persistent cache identifier for the type | |||
| if (safe.isPrimitive(tof)) | |||
| this._cacheId = this._type; | |||
| else | |||
| this._cacheId = safe.oid(tof); | |||
| // singletons are bound to the root container | |||
| var container = context.container.getRootContainer(); | |||
| if (container.has(this._cacheId)) { | |||
| instance = container.get(this._cacheId); | |||
| } else { | |||
| instance = this._create(context, name); | |||
| container.store(this._cacheId, instance); | |||
| if (this._cleanup) | |||
| container.onDispose( | |||
| makeClenupCallback(instance, this._cleanup)); | |||
| } | |||
| this._hasInstance = true; | |||
| return (this._instance = instance); | |||
| case 2: // CONTAINER | |||
| //return a cached value | |||
| if (this._hasInstance) | |||
| return this._instance; | |||
| // create an instance | |||
| instance = this._create(context, name); | |||
| // the instance is bound to the container | |||
| if (this._cleanup) | |||
| this._owner.onDispose( | |||
| makeClenupCallback(instance, this._cleanup)); | |||
| // cache and return the instance | |||
| this._hasInstance = true; | |||
| return (this._instance = instance); | |||
| case 3: // CONTEXT | |||
| //return a cached value if one exists | |||
| if (context.has(this._cacheId)) | |||
| return context.get(this._cacheId); | |||
| // context context activated instances are controlled by callers | |||
| return context.store(this._cacheId, this._create( | |||
| context, | |||
| name)); | |||
| case 4: // CALL | |||
| // per-call created instances are controlled by callers | |||
| return this._create(context, name); | |||
| case 5: // HIERARCHY | |||
| // hierarchy activated instances are behave much like container activated | |||
| // except they are created and bound to the child container | |||
| // return a cached value | |||
| if (context.container.has(this._cacheId)) | |||
| return context.container.get(this._cacheId); | |||
| instance = this._create(context, name); | |||
| if (this._cleanup) | |||
| context.container.onDispose(makeClenupCallback( | |||
| instance, | |||
| this._cleanup)); | |||
| return context.container.store(this._cacheId, instance); | |||
| default: | |||
| throw "Invalid activation type: " + this._activationType; | |||
| } | |||
| }, | |||
| isInstanceCreated: function () { | |||
| return this._hasInstance; | |||
| }, | |||
| getInstance: function () { | |||
| return this._instance; | |||
| }, | |||
| _create: function (context, name) { | |||
| context.enter(name, this, Boolean(this._services)); | |||
| if (this._activationType != CALL_ACTIVATION && | |||
| context.visit(this._cacheId) > 0) | |||
| throw new Error("Recursion detected"); | |||
| if (this._services) { | |||
| for (var p in this._services) { | |||
| var sv = this._services[p]; | |||
| context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false)); | |||
| } | |||
| } | |||
| var instance; | |||
| if (!this._factory) { | |||
| var ctor, type = this._type; | |||
| if (typeof (type) === "string") { | |||
| ctor = this._typeMap[type]; | |||
| if (!ctor) | |||
| throw new Error("Failed to resolve the type '" + | |||
| type + "'"); | |||
| } else { | |||
| ctor = type; | |||
| } | |||
| if (this._params === undefined) { | |||
| this._factory = function () { | |||
| return new ctor(); | |||
| }; | |||
| } else if (this._params instanceof Array) { | |||
| this._factory = function () { | |||
| var inst = Object.create(ctor.prototype); | |||
| var ret = ctor.apply(inst, arguments); | |||
| return typeof (ret) === "object" ? ret : inst; | |||
| }; | |||
| } else { | |||
| this._factory = function (param) { | |||
| return new ctor(param); | |||
| }; | |||
| } | |||
| } | |||
| if (this._params === undefined) { | |||
| instance = this._factory(); | |||
| } else if (this._params instanceof Array) { | |||
| instance = this._factory.apply(this, context.parse( | |||
| this._params, | |||
| ".params")); | |||
| } else { | |||
| instance = this._factory(context.parse( | |||
| this._params, | |||
| ".params")); | |||
| } | |||
| if (this._inject) { | |||
| this._inject.forEach(function (spec) { | |||
| for (var m in spec) | |||
| injectMethod(instance, m, context, spec[m]); | |||
| }); | |||
| } | |||
| context.leave(); | |||
| return instance; | |||
| }, | |||
| // @constructor {singleton} foo/bar/Baz | |||
| // @factory {singleton} | |||
| toString: function () { | |||
| var parts = []; | |||
| parts.push(this._type ? "@constructor" : "@factory"); | |||
| parts.push(activationNames[this._activationType]); | |||
| if (typeof (this._type) === "string") | |||
| parts.push(this._type); | |||
| return parts.join(" "); | |||
| } | |||
| }); | |||
| cls.SINGLETON = SINGLETON_ACTIVATION; | |||
| cls.CONTAINER = CONTAINER_ACTIVATION; | |||
| cls.CONTEXT = CONTEXT_ACTIVATION; | |||
| cls.CALL = CALL_ACTIVATION; | |||
| cls.HIERARCHY = HIERARCHY_ACTIVATION; | |||
| var activationNames = [ | |||
| "", | |||
| "{singleton}", | |||
| "{container}", | |||
| "{context}", | |||
| "{call}", | |||
| "{hierarchy}" | |||
| ]; | |||
| return cls; | |||
| }); |
