ActivationContext.ts
169 lines
| 5.1 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r49 | import { TraceSource } from "../log/TraceSource"; | ||
|
|
r133 | import { argumentNotEmptyString } from "../safe"; | ||
|
|
r144 | import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime, ServiceContainer } from "./interfaces"; | ||
|
|
r114 | import { MapOf } from "../interfaces"; | ||
|
|
r49 | |||
| const trace = TraceSource.get("@implab/core/di/ActivationContext"); | ||||
|
|
r131 | export interface ActivationContextInfo { | ||
|
|
r49 | name: string; | ||
| service: string; | ||||
| } | ||||
|
|
r134 | let nextId = 1; | ||
|
|
r135 | /** This class is created once per `Container.resolve` method call and used to | ||
| * cache dependencies and to track created instances. The activation context | ||||
| * tracks services with `context` activation type. | ||||
| */ | ||||
|
|
r120 | export class ActivationContext<S extends object> { | ||
|
|
r114 | _cache: MapOf<any>; | ||
|
|
r49 | |||
|
|
r120 | _services: ContainerServiceMap<S>; | ||
|
|
r49 | |||
|
|
r114 | _visited: MapOf<any>; | ||
|
|
r49 | |||
| _name: string; | ||||
|
|
r131 | _service: Descriptor<S, any>; | ||
|
|
r49 | |||
|
|
r144 | _container: ServiceContainer<S>; | ||
|
|
r131 | |||
| _parent: ActivationContext<S> | undefined; | ||||
|
|
r49 | |||
|
|
r135 | /** Creates a new activation context with the specified parameters. | ||
| * @param container the container which starts the activation process | ||||
| * @param services the initial service registrations | ||||
| * @param name the name of the service being activated, this parameter is | ||||
| * used for the debug purpose. | ||||
| * @param service the service to activate, this parameter is used for the | ||||
| * debug purpose. | ||||
| */ | ||||
|
|
r144 | constructor(container: ServiceContainer<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) { | ||
|
|
r131 | this._name = name; | ||
| this._service = service; | ||||
| this._visited = {}; | ||||
| this._cache = {}; | ||||
|
|
r49 | this._services = services; | ||
|
|
r131 | this._container = container; | ||
|
|
r49 | } | ||
|
|
r135 | /** the name of the current resolving dependency */ | ||
|
|
r49 | getName() { | ||
| return this._name; | ||||
| } | ||||
|
|
r135 | /** Returns the container for which 'resolve' method was called */ | ||
|
|
r131 | getContainer() { | ||
| return this._container; | ||||
| } | ||||
|
|
r135 | /** Resolves the specified dependency in the current context | ||
| * @param name The name of the dependency being resolved | ||||
| */ | ||||
|
|
r133 | resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>; | ||
|
|
r135 | /** Resolves the specified dependency with the specified default value if | ||
| * the dependency is missing. | ||||
| * | ||||
| * @param name The name of the dependency being resolved | ||||
| * @param def A default value to return in case of the specified dependency | ||||
| * is missing. | ||||
| */ | ||||
|
|
r133 | resolve<K extends ContainerKeys<S>, T>(name: K, def: T): TypeOfService<S, K> | T; | ||
|
|
r135 | /** Resolves the specified dependency and returns undefined in case if the | ||
| * dependency is missing. | ||||
| * | ||||
| * @param name The name of the dependency being resolved | ||||
| */ | ||||
|
|
r133 | resolve<K extends ContainerKeys<S>>(name: K, def: undefined): TypeOfService<S, K> | undefined; | ||
| resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined { | ||||
|
|
r49 | const d = this._services[name]; | ||
|
|
r114 | if (d !== undefined) { | ||
|
|
r120 | return this.activate(d, name.toString()); | ||
|
|
r114 | } else { | ||
|
|
r133 | if (arguments.length > 1) | ||
|
|
r49 | return def; | ||
| else | ||||
| throw new Error(`Service ${name} not found`); | ||||
|
|
r114 | } | ||
|
|
r49 | } | ||
| /** | ||||
| * registers services local to the the activation context | ||||
| * | ||||
| * @name{string} the name of the service | ||||
| * @service{string} the service descriptor to register | ||||
| */ | ||||
|
|
r115 | register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) { | ||
|
|
r49 | argumentNotEmptyString(name, "name"); | ||
|
|
r120 | this._services[name] = service as any; | ||
|
|
r49 | } | ||
|
|
r134 | createLifetime(): ILifetime { | ||
| const id = nextId++; | ||||
| const me = this; | ||||
| return { | ||||
| initialize() { | ||||
| }, | ||||
| has() { | ||||
| return id in me._cache; | ||||
| }, | ||||
| get() { | ||||
| return me._cache[id]; | ||||
| }, | ||||
| store(item: any) { | ||||
| me._cache[id] = item; | ||||
| } | ||||
| }; | ||||
|
|
r49 | } | ||
|
|
r135 | |||
|
|
r115 | activate<T>(d: Descriptor<S, T>, name: string) { | ||
|
|
r49 | if (trace.isLogEnabled()) | ||
|
|
r158 | trace.log("enter {0} {1}", name, d); | ||
|
|
r49 | |||
|
|
r131 | const ctx = this.enter(d, name); | ||
| const v = d.activate(ctx); | ||||
|
|
r49 | |||
| if (trace.isLogEnabled()) | ||||
| trace.log(`leave ${name}`); | ||||
| return v; | ||||
| } | ||||
| visit(id: string) { | ||||
| const count = this._visited[id] || 0; | ||||
| this._visited[id] = count + 1; | ||||
| return count; | ||||
| } | ||||
|
|
r131 | getStack(): ActivationContextInfo[] { | ||
| const stack = [{ | ||||
| name: this._name, | ||||
| service: this._service.toString() | ||||
| }]; | ||||
| return this._parent ? | ||||
| stack.concat(this._parent.getStack()) : | ||||
| stack; | ||||
|
|
r49 | } | ||
|
|
r131 | private enter(service: Descriptor<S, any>, name: string): this { | ||
| const clone = Object.create(this); | ||||
| clone._name = name; | ||||
| clone._services = Object.create(this._services); | ||||
| clone._parent = this; | ||||
| clone._service = service; | ||||
| return clone; | ||||
|
|
r49 | } | ||
|
|
r132 | |||
| /** Creates a clone for the current context, used to protect it from modifications */ | ||||
| clone(): this { | ||||
| const clone = Object.create(this); | ||||
| clone._services = Object.create(this._services); | ||||
| return clone; | ||||
| } | ||||
|
|
r49 | } | ||
