2015-06-11 19:32:55 +02:00
|
|
|
import {
|
|
|
|
Type,
|
|
|
|
isBlank,
|
|
|
|
isPresent,
|
|
|
|
CONST,
|
2015-06-17 10:12:06 +02:00
|
|
|
CONST_EXPR,
|
2015-06-11 19:32:55 +02:00
|
|
|
stringify,
|
2015-09-02 10:21:28 -07:00
|
|
|
isArray,
|
|
|
|
normalizeBool
|
2015-08-20 14:28:25 -07:00
|
|
|
} from 'angular2/src/core/facade/lang';
|
2015-09-10 15:25:36 -07:00
|
|
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
2015-08-28 11:29:19 -07:00
|
|
|
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
2015-08-20 14:28:25 -07:00
|
|
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
2014-10-09 10:55:18 -04:00
|
|
|
import {Key} from './key';
|
2015-04-24 15:19:11 -07:00
|
|
|
import {
|
2015-07-08 12:04:24 -07:00
|
|
|
InjectMetadata,
|
|
|
|
InjectableMetadata,
|
|
|
|
OptionalMetadata,
|
2015-07-29 11:26:09 -07:00
|
|
|
SelfMetadata,
|
|
|
|
HostMetadata,
|
|
|
|
SkipSelfMetadata,
|
2015-07-08 12:04:24 -07:00
|
|
|
DependencyMetadata
|
|
|
|
} from './metadata';
|
2015-09-02 10:21:28 -07:00
|
|
|
import {
|
|
|
|
NoAnnotationError,
|
|
|
|
MixingMultiBindingsWithRegularBindings,
|
|
|
|
InvalidBindingError
|
|
|
|
} from './exceptions';
|
2015-05-13 15:54:46 -07:00
|
|
|
import {resolveForwardRef} from './forward_ref';
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
2014-10-09 10:55:18 -04:00
|
|
|
export class Dependency {
|
2015-07-29 11:26:09 -07:00
|
|
|
constructor(public key: Key, public optional: boolean, public lowerBoundVisibility: any,
|
2015-08-28 11:29:19 -07:00
|
|
|
public upperBoundVisibility: any, public properties: any[]) {}
|
2015-02-27 07:42:51 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
static fromKey(key: Key): Dependency { return new Dependency(key, false, null, null, []); }
|
2014-10-09 10:55:18 -04:00
|
|
|
}
|
2014-10-09 11:35:13 -04:00
|
|
|
|
2015-06-17 10:12:06 +02:00
|
|
|
const _EMPTY_LIST = CONST_EXPR([]);
|
2015-04-09 23:17:05 -07:00
|
|
|
|
|
|
|
/**
|
2015-08-10 21:42:47 -07:00
|
|
|
* Describes how_ the {@link Injector} should instantiate a given token.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-17 13:01:07 -07:00
|
|
|
* See {@link bind}.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(String, { toValue: 'Hello' })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
@CONST()
|
2014-09-30 14:56:33 -04:00
|
|
|
export class Binding {
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Token used when retrieving this binding. Usually the `Type`.
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
token;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds an interface to an implementation / subclass.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-09-09 11:45:35 -07:00
|
|
|
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
|
|
|
|
* easy
|
2015-04-24 15:19:11 -07:00
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
*
|
|
|
|
* class Vehicle {}
|
|
|
|
*
|
|
|
|
* class Car extends Vehicle {}
|
|
|
|
*
|
|
|
|
* var injectorClass = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* new Binding(Vehicle, { toClass: Car })
|
|
|
|
* ]);
|
|
|
|
* var injectorAlias = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* new Binding(Vehicle, { toAlias: Car })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
*
|
|
|
|
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
|
|
|
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toClass: Type;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(String, { toValue: 'Hello' })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
toValue;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to the alias for an existing key.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* An alias means that {@link Injector} returns the same instance as if the alias token was used.
|
|
|
|
* This is in contrast to `toClass` where a separate instance of `toClass` is returned.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-09-10 23:03:46 -07:00
|
|
|
* Because `toAlias` and `toClass` are often confused the example contains both use cases for easy
|
2015-04-24 15:19:11 -07:00
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
*
|
|
|
|
* class Vehicle {}
|
|
|
|
*
|
|
|
|
* class Car extends Vehicle {}
|
|
|
|
*
|
|
|
|
* var injectorAlias = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* new Binding(Vehicle, { toAlias: Car })
|
|
|
|
* ]);
|
|
|
|
* var injectorClass = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* new Binding(Vehicle, { toClass: Car })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
|
|
|
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
toAlias;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
|
|
|
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
2015-05-14 14:28:47 +02:00
|
|
|
* dependencies: [Number] })
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toFactory: Function;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-06-26 15:59:18 -07:00
|
|
|
* Used in conjunction with `toFactory` and specifies a set of dependencies
|
2015-04-24 15:19:11 -07:00
|
|
|
* (as `token`s) which should be injected into the factory function.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
|
|
|
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
2015-05-14 14:28:47 +02:00
|
|
|
* dependencies: [Number] })
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-09-02 10:21:28 -07:00
|
|
|
dependencies: Object[];
|
2015-04-24 15:19:11 -07:00
|
|
|
|
2015-09-02 10:21:28 -07:00
|
|
|
_multi: boolean;
|
|
|
|
|
|
|
|
constructor(token, {toClass, toValue, toAlias, toFactory, deps, multi}: {
|
|
|
|
toClass?: Type,
|
|
|
|
toValue?: any,
|
|
|
|
toAlias?: any,
|
|
|
|
toFactory?: Function,
|
|
|
|
deps?: Object[],
|
|
|
|
multi?: boolean
|
|
|
|
}) {
|
2015-04-09 23:17:05 -07:00
|
|
|
this.token = token;
|
|
|
|
this.toClass = toClass;
|
|
|
|
this.toValue = toValue;
|
|
|
|
this.toAlias = toAlias;
|
|
|
|
this.toFactory = toFactory;
|
|
|
|
this.dependencies = deps;
|
2015-09-02 10:21:28 -07:00
|
|
|
this._multi = multi;
|
2015-04-09 23:17:05 -07:00
|
|
|
}
|
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-09-02 10:21:28 -07:00
|
|
|
* Used to create multiple bindings matching the same token.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding("Strings", { toValue: "String1", multi: true}),
|
|
|
|
* new Binding("Strings", { toValue: "String2", multi: true})
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get("Strings")).toEqual(["String1", "String2"]);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* Multi bindings and regular bindings cannot be mixed. The following
|
|
|
|
* will throw an exception:
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-09-02 10:21:28 -07:00
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding("Strings", { toValue: "String1", multi: true}),
|
|
|
|
* new Binding("Strings", { toValue: "String2"})
|
|
|
|
* ]);
|
|
|
|
* ```
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-09-02 10:21:28 -07:00
|
|
|
get multi(): boolean { return normalizeBool(this._multi); }
|
2015-04-09 23:17:05 -07:00
|
|
|
}
|
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* An internal resolved representation of a {@link Binding} used by the {@link Injector}.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* A {@link Binding} is resolved when it has a factory function. Binding to a class, alias, or
|
|
|
|
* value, are just convenience methods, as {@link Injector} only operates on calling factory
|
|
|
|
* functions.
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
export class ResolvedBinding {
|
2015-05-11 10:28:07 -07:00
|
|
|
constructor(
|
|
|
|
/**
|
|
|
|
* A key, usually a `Type`.
|
|
|
|
*/
|
|
|
|
public key: Key,
|
2015-04-17 03:29:05 -07:00
|
|
|
|
2015-09-02 10:21:28 -07:00
|
|
|
/**
|
|
|
|
* Factory function which can return an instance of an object represented by a key.
|
|
|
|
*/
|
|
|
|
public resolvedFactories: ResolvedFactory[],
|
|
|
|
|
|
|
|
public multiBinding: boolean) {}
|
|
|
|
get resolvedFactory(): ResolvedFactory { return this.resolvedFactories[0]; }
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ResolvedFactory {
|
|
|
|
constructor(
|
2015-05-11 10:28:07 -07:00
|
|
|
/**
|
|
|
|
* Factory function which can return an instance of an object represented by a key.
|
|
|
|
*/
|
|
|
|
public factory: Function,
|
2015-04-17 03:29:05 -07:00
|
|
|
|
2015-05-11 10:28:07 -07:00
|
|
|
/**
|
|
|
|
* Arguments (dependencies) to the `factory` function.
|
|
|
|
*/
|
2015-08-28 11:29:19 -07:00
|
|
|
public dependencies: Dependency[]) {}
|
2014-09-30 14:56:33 -04:00
|
|
|
}
|
|
|
|
|
2015-04-09 23:17:05 -07:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Provides an API for imperatively constructing {@link Binding}s.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-09-03 16:17:23 -07:00
|
|
|
* To construct a {@link Binding}, bind a `token` to either a class, a value or a factory function.
|
|
|
|
* See {@link BindingBuilder} for more details.
|
|
|
|
*
|
|
|
|
* The `token` is most commonly an {@link angular2/di/OpaqueToken} or a class.
|
|
|
|
*
|
|
|
|
* `bind` is only relevant for JavaScript. For Dart use the {@link Binding} constructor.
|
2015-04-20 16:37:49 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-09-03 16:17:23 -07:00
|
|
|
* ```typescript
|
|
|
|
* // inj.get(MyClass) would instantiate MyClass
|
|
|
|
* bind(MyClass).toClass(MyClass);
|
|
|
|
*
|
|
|
|
* // inj.get(MyClass) === 'my class'
|
|
|
|
* bind(MyClass).toValue('my class');
|
2015-04-20 16:37:49 +00:00
|
|
|
*
|
2015-09-03 16:17:23 -07:00
|
|
|
* // inj.get(MyClass) would instantiate the depenency and call the factory function with the
|
|
|
|
* // instance
|
|
|
|
* bind(MyClass).toFactory(dep => new MyClass(dep), [DepClass]);
|
|
|
|
*
|
|
|
|
* // inj.get(MyOtherClass) === inj.get(MyClass)
|
|
|
|
* bind(MyOtherClass).toAlias(MyClass);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ```dart
|
|
|
|
* var binding = new Binding(MyClass, toClass: MyClass);
|
|
|
|
* var binding = new Binding(MyClass, toValue: 'my class');
|
|
|
|
* var binding = new Binding(MyClass, toFactory: (dep) => new MyClass(dep),
|
|
|
|
* dependencies: [DepClass]);
|
|
|
|
* var binding = new Binding(MyOtherClass, toAlias: MyClass);
|
2015-04-20 16:37:49 +00:00
|
|
|
* ```
|
2015-09-03 16:17:23 -07:00
|
|
|
*
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
export function bind(token): BindingBuilder {
|
2014-09-30 14:56:33 -04:00
|
|
|
return new BindingBuilder(token);
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:17:05 -07:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Helper class for the {@link bind} function.
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2014-09-30 14:56:33 -04:00
|
|
|
export class BindingBuilder {
|
2015-05-11 10:28:07 -07:00
|
|
|
constructor(public token) {}
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds an interface to an implementation / subclass.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
|
|
|
|
* easy comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
*
|
|
|
|
* class Vehicle {}
|
|
|
|
*
|
|
|
|
* class Car extends Vehicle {}
|
|
|
|
*
|
|
|
|
* var injectorClass = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* bind(Vehicle).toClass(Car)
|
|
|
|
* ]);
|
|
|
|
* var injectorAlias = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* bind(Vehicle).toAlias(Car)
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
*
|
|
|
|
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
|
|
|
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toClass(type: Type): Binding { return new Binding(this.token, {toClass: type}); }
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* bind(String).toValue('Hello')
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-07-07 20:03:00 -07:00
|
|
|
toValue(value: any): Binding { return new Binding(this.token, {toValue: value}); }
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to the alias for an existing key.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* An alias means that we will return the same instance as if the alias token was used. (This is
|
2015-07-07 20:03:00 -07:00
|
|
|
* in contrast to `toClass` where a separate instance of `toClass` will be returned.)
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-09-09 11:45:35 -07:00
|
|
|
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
|
|
|
|
* easy
|
2015-04-24 15:19:11 -07:00
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
*
|
|
|
|
* class Vehicle {}
|
|
|
|
*
|
|
|
|
* class Car extends Vehicle {}
|
|
|
|
*
|
|
|
|
* var injectorAlias = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* bind(Vehicle).toAlias(Car)
|
|
|
|
* ]);
|
|
|
|
* var injectorClass = Injector.resolveAndCreate([
|
|
|
|
* Car,
|
|
|
|
* bind(Vehicle).toClass(Car)
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
|
|
|
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-07-07 20:03:00 -07:00
|
|
|
toAlias(aliasToken: /*Type*/ any): Binding {
|
2015-05-26 10:55:12 +02:00
|
|
|
if (isBlank(aliasToken)) {
|
|
|
|
throw new BaseException(`Can not alias ${stringify(this.token)} to a blank value!`);
|
|
|
|
}
|
|
|
|
return new Binding(this.token, {toAlias: aliasToken});
|
|
|
|
}
|
2015-02-21 15:18:06 +01:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
2015-05-28 17:53:13 +02:00
|
|
|
* bind(Number).toFactory(() => { return 1+2; }),
|
|
|
|
* bind(String).toFactory((v) => { return "Value: " + v; }, [Number])
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-08-28 11:29:19 -07:00
|
|
|
toFactory(factoryFunction: Function, dependencies?: any[]): Binding {
|
2015-04-24 15:19:11 -07:00
|
|
|
return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
|
2014-09-30 14:56:33 -04:00
|
|
|
}
|
2015-04-09 23:17:05 -07:00
|
|
|
}
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-09-02 10:21:28 -07:00
|
|
|
/**
|
|
|
|
* Resolve a single binding.
|
|
|
|
*/
|
|
|
|
export function resolveFactory(binding: Binding): ResolvedFactory {
|
|
|
|
var factoryFn: Function;
|
|
|
|
var resolvedDeps;
|
|
|
|
if (isPresent(binding.toClass)) {
|
|
|
|
var toClass = resolveForwardRef(binding.toClass);
|
|
|
|
factoryFn = reflector.factory(toClass);
|
|
|
|
resolvedDeps = _dependenciesFor(toClass);
|
|
|
|
} else if (isPresent(binding.toAlias)) {
|
|
|
|
factoryFn = (aliasInstance) => aliasInstance;
|
|
|
|
resolvedDeps = [Dependency.fromKey(Key.get(binding.toAlias))];
|
|
|
|
} else if (isPresent(binding.toFactory)) {
|
|
|
|
factoryFn = binding.toFactory;
|
|
|
|
resolvedDeps = _constructDependencies(binding.toFactory, binding.dependencies);
|
|
|
|
} else {
|
|
|
|
factoryFn = () => binding.toValue;
|
|
|
|
resolvedDeps = _EMPTY_LIST;
|
|
|
|
}
|
|
|
|
return new ResolvedFactory(factoryFn, resolvedDeps);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the {@link Binding} into {@link ResolvedBinding}.
|
|
|
|
*
|
|
|
|
* {@link Injector} internally only uses {@link ResolvedBinding}, {@link Binding} contains
|
|
|
|
* convenience binding syntax.
|
|
|
|
*/
|
|
|
|
export function resolveBinding(binding: Binding): ResolvedBinding {
|
|
|
|
return new ResolvedBinding(Key.get(binding.token), [resolveFactory(binding)], false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve a list of Bindings.
|
|
|
|
*/
|
|
|
|
export function resolveBindings(bindings: Array<Type | Binding | any[]>): ResolvedBinding[] {
|
|
|
|
var normalized = _createListOfBindings(_normalizeBindings(bindings, new Map()));
|
|
|
|
return normalized.map(b => {
|
|
|
|
if (b instanceof _NormalizedBinding) {
|
|
|
|
return new ResolvedBinding(b.key, [b.resolvedFactory], false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
var arr = <_NormalizedBinding[]>b;
|
|
|
|
return new ResolvedBinding(arr[0].key, arr.map(_ => _.resolvedFactory), true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The algorithm works as follows:
|
|
|
|
*
|
|
|
|
* [Binding] -> [_NormalizedBinding|[_NormalizedBinding]] -> [ResolvedBinding]
|
|
|
|
*
|
|
|
|
* _NormalizedBinding is essentially a resolved binding before it was grouped by key.
|
|
|
|
*/
|
|
|
|
class _NormalizedBinding {
|
|
|
|
constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function _createListOfBindings(flattenedBindings: Map<number, any>): any[] {
|
|
|
|
return MapWrapper.values(flattenedBindings);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _normalizeBindings(bindings: Array<Type | Binding | any[]>,
|
|
|
|
res: Map<number, _NormalizedBinding | _NormalizedBinding[]>):
|
|
|
|
Map<number, _NormalizedBinding | _NormalizedBinding[]> {
|
|
|
|
ListWrapper.forEach(bindings, (b) => {
|
|
|
|
if (b instanceof Type) {
|
|
|
|
_normalizeBinding(bind(b).toClass(b), res);
|
|
|
|
|
|
|
|
} else if (b instanceof Binding) {
|
|
|
|
_normalizeBinding(b, res);
|
|
|
|
|
|
|
|
} else if (b instanceof Array) {
|
|
|
|
_normalizeBindings(b, res);
|
|
|
|
|
|
|
|
} else if (b instanceof BindingBuilder) {
|
|
|
|
throw new InvalidBindingError(b.token);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
throw new InvalidBindingError(b);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _normalizeBinding(b: Binding, res: Map<number, _NormalizedBinding | _NormalizedBinding[]>):
|
|
|
|
void {
|
|
|
|
var key = Key.get(b.token);
|
|
|
|
var factory = resolveFactory(b);
|
|
|
|
var normalized = new _NormalizedBinding(key, factory);
|
|
|
|
|
|
|
|
if (b.multi) {
|
|
|
|
var existingBinding = res.get(key.id);
|
|
|
|
|
|
|
|
if (existingBinding instanceof Array) {
|
|
|
|
existingBinding.push(normalized);
|
|
|
|
|
|
|
|
} else if (isBlank(existingBinding)) {
|
|
|
|
res.set(key.id, [normalized]);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
throw new MixingMultiBindingsWithRegularBindings(existingBinding, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
var existingBinding = res.get(key.id);
|
|
|
|
|
|
|
|
if (existingBinding instanceof Array) {
|
|
|
|
throw new MixingMultiBindingsWithRegularBindings(existingBinding, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
res.set(key.id, normalized);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-28 11:29:19 -07:00
|
|
|
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
|
2015-05-18 17:19:54 -07:00
|
|
|
if (isBlank(dependencies)) {
|
|
|
|
return _dependenciesFor(factoryFunction);
|
|
|
|
} else {
|
2015-08-28 11:29:19 -07:00
|
|
|
var params: any[][] = ListWrapper.map(dependencies, (t) => [t]);
|
2015-05-18 17:19:54 -07:00
|
|
|
return ListWrapper.map(dependencies, (t) => _extractToken(factoryFunction, t, params));
|
|
|
|
}
|
2014-10-09 12:09:50 -04:00
|
|
|
}
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-08-28 11:29:19 -07:00
|
|
|
function _dependenciesFor(typeOrFunc): Dependency[] {
|
2014-11-20 12:07:48 -08:00
|
|
|
var params = reflector.parameters(typeOrFunc);
|
|
|
|
if (isBlank(params)) return [];
|
2015-05-01 14:05:19 -07:00
|
|
|
if (ListWrapper.any(params, (p) => isBlank(p))) {
|
2015-05-18 17:19:54 -07:00
|
|
|
throw new NoAnnotationError(typeOrFunc, params);
|
2015-05-01 14:05:19 -07:00
|
|
|
}
|
2015-08-28 11:29:19 -07:00
|
|
|
return ListWrapper.map(params, (p: any[]) => _extractToken(typeOrFunc, p, params));
|
2014-11-20 12:07:48 -08:00
|
|
|
}
|
|
|
|
|
2015-08-28 11:29:19 -07:00
|
|
|
function _extractToken(typeOrFunc, metadata /*any[] | any*/, params: any[][]): Dependency {
|
2014-11-20 12:07:48 -08:00
|
|
|
var depProps = [];
|
2015-02-27 07:42:51 -08:00
|
|
|
var token = null;
|
|
|
|
var optional = false;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
if (!isArray(metadata)) {
|
|
|
|
return _createDependency(metadata, optional, null, null, depProps);
|
2015-05-21 08:34:48 -07:00
|
|
|
}
|
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
var lowerBoundVisibility = null;
|
|
|
|
var upperBoundVisibility = null;
|
2015-06-26 15:59:18 -07:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
for (var i = 0; i < metadata.length; ++i) {
|
|
|
|
var paramMetadata = metadata[i];
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
if (paramMetadata instanceof Type) {
|
|
|
|
token = paramMetadata;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
} else if (paramMetadata instanceof InjectMetadata) {
|
|
|
|
token = paramMetadata.token;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
} else if (paramMetadata instanceof OptionalMetadata) {
|
2015-02-27 07:42:51 -08:00
|
|
|
optional = true;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
} else if (paramMetadata instanceof SelfMetadata) {
|
|
|
|
upperBoundVisibility = paramMetadata;
|
|
|
|
|
|
|
|
} else if (paramMetadata instanceof HostMetadata) {
|
|
|
|
upperBoundVisibility = paramMetadata;
|
2015-06-26 15:59:18 -07:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
} else if (paramMetadata instanceof SkipSelfMetadata) {
|
|
|
|
lowerBoundVisibility = paramMetadata;
|
|
|
|
|
|
|
|
} else if (paramMetadata instanceof DependencyMetadata) {
|
|
|
|
if (isPresent(paramMetadata.token)) {
|
|
|
|
token = paramMetadata.token;
|
2015-03-29 14:56:18 +02:00
|
|
|
}
|
2015-07-29 11:26:09 -07:00
|
|
|
depProps.push(paramMetadata);
|
2014-11-20 12:07:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 15:54:46 -07:00
|
|
|
token = resolveForwardRef(token);
|
|
|
|
|
2015-02-27 07:42:51 -08:00
|
|
|
if (isPresent(token)) {
|
2015-07-29 11:26:09 -07:00
|
|
|
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
|
2014-11-20 12:07:48 -08:00
|
|
|
} else {
|
2015-05-18 17:19:54 -07:00
|
|
|
throw new NoAnnotationError(typeOrFunc, params);
|
2014-11-20 12:07:48 -08:00
|
|
|
}
|
|
|
|
}
|
2015-06-26 15:59:18 -07:00
|
|
|
|
2015-07-29 11:26:09 -07:00
|
|
|
function _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps):
|
|
|
|
Dependency {
|
|
|
|
return new Dependency(Key.get(token), optional, lowerBoundVisibility, upperBoundVisibility,
|
|
|
|
depProps);
|
2014-11-20 12:07:48 -08:00
|
|
|
}
|