##// END OF EJS Templates
Merge
Merge

File last commit:

r0:6a5387d69bf1 default
r3:af6fce5deaab merge default
Show More
ServiceDescriptor.js
288 lines | 11.1 KiB | application/javascript | JavascriptLexer
/ src / js / di / ServiceDescriptor.js
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;
});