chore: move core modules into core directory
BREAKING CHANGE: This change moves the http module into angular2/, so its import path is now angular2/http instead of http/http. Many other modules have also been moved around inside of angular2, but the public API paths have not changed as of this commit.
This commit is contained in:
447
modules/angular2/src/core/di/binding.ts
Normal file
447
modules/angular2/src/core/di/binding.ts
Normal file
@ -0,0 +1,447 @@
|
||||
import {
|
||||
Type,
|
||||
isBlank,
|
||||
isPresent,
|
||||
CONST,
|
||||
CONST_EXPR,
|
||||
BaseException,
|
||||
stringify,
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
import {Key} from './key';
|
||||
import {
|
||||
InjectMetadata,
|
||||
InjectableMetadata,
|
||||
OptionalMetadata,
|
||||
SelfMetadata,
|
||||
HostMetadata,
|
||||
SkipSelfMetadata,
|
||||
DependencyMetadata
|
||||
} from './metadata';
|
||||
import {NoAnnotationError} from './exceptions';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class Dependency {
|
||||
constructor(public key: Key, public optional: boolean, public lowerBoundVisibility: any,
|
||||
public upperBoundVisibility: any, public properties: List<any>) {}
|
||||
|
||||
static fromKey(key: Key): Dependency { return new Dependency(key, false, null, null, []); }
|
||||
}
|
||||
|
||||
const _EMPTY_LIST = CONST_EXPR([]);
|
||||
|
||||
/**
|
||||
* Describes how_ the {@link Injector} should instantiate a given token.
|
||||
*
|
||||
* See {@link bind}.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(String, { toValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class Binding {
|
||||
/**
|
||||
* Token used when retrieving this binding. Usually the `Type`.
|
||||
*/
|
||||
token;
|
||||
|
||||
/**
|
||||
* Binds an interface to an implementation / subclass.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
|
||||
* comparison.
|
||||
*
|
||||
* ```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);
|
||||
* ```
|
||||
*/
|
||||
toClass: Type;
|
||||
|
||||
/**
|
||||
* Binds a key to a value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(String, { toValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
toValue;
|
||||
|
||||
/**
|
||||
* Binds a key to the alias for an existing key.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy
|
||||
* comparison.
|
||||
*
|
||||
* ```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);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toAlias;
|
||||
|
||||
/**
|
||||
* Binds a key to a function which computes the value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [Number] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
toFactory: Function;
|
||||
|
||||
/**
|
||||
* Used in conjunction with `toFactory` and specifies a set of dependencies
|
||||
* (as `token`s) which should be injected into the factory function.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [Number] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
dependencies: List<any>;
|
||||
|
||||
constructor(
|
||||
token,
|
||||
{toClass, toValue, toAlias, toFactory, deps}:
|
||||
{toClass?: Type, toValue?: any, toAlias?: any, toFactory?: Function, deps?: List<any>}) {
|
||||
this.token = token;
|
||||
this.toClass = toClass;
|
||||
this.toValue = toValue;
|
||||
this.toAlias = toAlias;
|
||||
this.toFactory = toFactory;
|
||||
this.dependencies = deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the {@link Binding} into {@link ResolvedBinding}.
|
||||
*
|
||||
* {@link Injector} internally only uses {@link ResolvedBinding}, {@link Binding} contains
|
||||
* convenience binding syntax.
|
||||
*/
|
||||
resolve(): ResolvedBinding {
|
||||
var factoryFn: Function;
|
||||
var resolvedDeps;
|
||||
if (isPresent(this.toClass)) {
|
||||
var toClass = resolveForwardRef(this.toClass);
|
||||
factoryFn = reflector.factory(toClass);
|
||||
resolvedDeps = _dependenciesFor(toClass);
|
||||
} else if (isPresent(this.toAlias)) {
|
||||
factoryFn = (aliasInstance) => aliasInstance;
|
||||
resolvedDeps = [Dependency.fromKey(Key.get(this.toAlias))];
|
||||
} else if (isPresent(this.toFactory)) {
|
||||
factoryFn = this.toFactory;
|
||||
resolvedDeps = _constructDependencies(this.toFactory, this.dependencies);
|
||||
} else {
|
||||
factoryFn = () => this.toValue;
|
||||
resolvedDeps = _EMPTY_LIST;
|
||||
}
|
||||
|
||||
return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal resolved representation of a {@link Binding} used by the {@link Injector}.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
export class ResolvedBinding {
|
||||
constructor(
|
||||
/**
|
||||
* A key, usually a `Type`.
|
||||
*/
|
||||
public key: Key,
|
||||
|
||||
/**
|
||||
* Factory function which can return an instance of an object represented by a key.
|
||||
*/
|
||||
public factory: Function,
|
||||
|
||||
/**
|
||||
* Arguments (dependencies) to the `factory` function.
|
||||
*/
|
||||
public dependencies: List<Dependency>) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for imperatively constructing {@link Binding}s.
|
||||
*
|
||||
* This is only relevant for JavaScript. See {@link BindingBuilder}.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* bind(MyInterface).toClass(MyClass)
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
export function bind(token): BindingBuilder {
|
||||
return new BindingBuilder(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for the {@link bind} function.
|
||||
*/
|
||||
export class BindingBuilder {
|
||||
constructor(public token) {}
|
||||
|
||||
/**
|
||||
* Binds an interface to an implementation / subclass.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
|
||||
* easy comparison.
|
||||
*
|
||||
* ```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);
|
||||
* ```
|
||||
*/
|
||||
toClass(type: Type): Binding { return new Binding(this.token, {toClass: type}); }
|
||||
|
||||
/**
|
||||
* Binds a key to a value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(String).toValue('Hello')
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
toValue(value: any): Binding { return new Binding(this.token, {toValue: value}); }
|
||||
|
||||
/**
|
||||
* Binds a key to the alias for an existing key.
|
||||
*
|
||||
* An alias means that we will return the same instance as if the alias token was used. (This is
|
||||
* in contrast to `toClass` where a separate instance of `toClass` will be returned.)
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
|
||||
* comparison.
|
||||
*
|
||||
* ```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);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toAlias(aliasToken: /*Type*/ any): Binding {
|
||||
if (isBlank(aliasToken)) {
|
||||
throw new BaseException(`Can not alias ${stringify(this.token)} to a blank value!`);
|
||||
}
|
||||
return new Binding(this.token, {toAlias: aliasToken});
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a key to a function which computes the value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(Number).toFactory(() => { return 1+2; }),
|
||||
* bind(String).toFactory((v) => { return "Value: " + v; }, [Number])
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
toFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
|
||||
return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
|
||||
}
|
||||
}
|
||||
|
||||
function _constructDependencies(factoryFunction: Function, dependencies: List<any>):
|
||||
List<Dependency> {
|
||||
if (isBlank(dependencies)) {
|
||||
return _dependenciesFor(factoryFunction);
|
||||
} else {
|
||||
var params: List<List<any>> = ListWrapper.map(dependencies, (t) => [t]);
|
||||
return ListWrapper.map(dependencies, (t) => _extractToken(factoryFunction, t, params));
|
||||
}
|
||||
}
|
||||
|
||||
function _dependenciesFor(typeOrFunc): List<Dependency> {
|
||||
var params = reflector.parameters(typeOrFunc);
|
||||
if (isBlank(params)) return [];
|
||||
if (ListWrapper.any(params, (p) => isBlank(p))) {
|
||||
throw new NoAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
return ListWrapper.map(params, (p: List<any>) => _extractToken(typeOrFunc, p, params));
|
||||
}
|
||||
|
||||
function _extractToken(typeOrFunc, metadata /*List<any> | any*/, params: List<List<any>>):
|
||||
Dependency {
|
||||
var depProps = [];
|
||||
var token = null;
|
||||
var optional = false;
|
||||
|
||||
if (!isArray(metadata)) {
|
||||
return _createDependency(metadata, optional, null, null, depProps);
|
||||
}
|
||||
|
||||
var lowerBoundVisibility = null;
|
||||
var upperBoundVisibility = null;
|
||||
|
||||
for (var i = 0; i < metadata.length; ++i) {
|
||||
var paramMetadata = metadata[i];
|
||||
|
||||
if (paramMetadata instanceof Type) {
|
||||
token = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof InjectMetadata) {
|
||||
token = paramMetadata.token;
|
||||
|
||||
} else if (paramMetadata instanceof OptionalMetadata) {
|
||||
optional = true;
|
||||
|
||||
} else if (paramMetadata instanceof SelfMetadata) {
|
||||
upperBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof HostMetadata) {
|
||||
upperBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof SkipSelfMetadata) {
|
||||
lowerBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof DependencyMetadata) {
|
||||
if (isPresent(paramMetadata.token)) {
|
||||
token = paramMetadata.token;
|
||||
}
|
||||
depProps.push(paramMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
token = resolveForwardRef(token);
|
||||
|
||||
if (isPresent(token)) {
|
||||
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
|
||||
} else {
|
||||
throw new NoAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
}
|
||||
|
||||
function _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps):
|
||||
Dependency {
|
||||
return new Dependency(Key.get(token), optional, lowerBoundVisibility, upperBoundVisibility,
|
||||
depProps);
|
||||
}
|
46
modules/angular2/src/core/di/decorators.dart
Normal file
46
modules/angular2/src/core/di/decorators.dart
Normal file
@ -0,0 +1,46 @@
|
||||
library angular2.di.decorators;
|
||||
|
||||
import 'metadata.dart';
|
||||
export 'metadata.dart';
|
||||
|
||||
/**
|
||||
* {@link InjectMetadata}.
|
||||
*/
|
||||
class Inject extends InjectMetadata {
|
||||
const Inject(dynamic token) : super(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OptionalMetadata}.
|
||||
*/
|
||||
class Optional extends OptionalMetadata {
|
||||
const Optional() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link InjectableMetadata}.
|
||||
*/
|
||||
class Injectable extends InjectableMetadata {
|
||||
const Injectable() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SelfMetadata}.
|
||||
*/
|
||||
class Self extends SelfMetadata {
|
||||
const Self() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HostMetadata}.
|
||||
*/
|
||||
class Host extends HostMetadata {
|
||||
const Host() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SkipSelfMetadata}.
|
||||
*/
|
||||
class SkipSelf extends SkipSelfMetadata {
|
||||
const SkipSelf() : super();
|
||||
}
|
87
modules/angular2/src/core/di/decorators.ts
Normal file
87
modules/angular2/src/core/di/decorators.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import {
|
||||
InjectMetadata,
|
||||
OptionalMetadata,
|
||||
InjectableMetadata,
|
||||
SelfMetadata,
|
||||
HostMetadata,
|
||||
SkipSelfMetadata
|
||||
} from './metadata';
|
||||
import {makeDecorator, makeParamDecorator, TypeDecorator} from '../util/decorators';
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectMetadata}.
|
||||
*/
|
||||
export interface InjectFactory {
|
||||
(token: any): any;
|
||||
new (token: any): InjectMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link OptionalMetadata}.
|
||||
*/
|
||||
export interface OptionalFactory {
|
||||
(): any;
|
||||
new (): OptionalMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectableMetadata}.
|
||||
*/
|
||||
export interface InjectableFactory {
|
||||
(): any;
|
||||
new (): InjectableMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SelfMetadata}.
|
||||
*/
|
||||
export interface SelfFactory {
|
||||
(): any;
|
||||
new (): SelfMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link HostMetadata}.
|
||||
*/
|
||||
export interface HostFactory {
|
||||
(): any;
|
||||
new (): HostMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SkipSelfMetadata}.
|
||||
*/
|
||||
export interface SkipSelfFactory {
|
||||
(): any;
|
||||
new (): SkipSelfMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectMetadata}.
|
||||
*/
|
||||
export var Inject: InjectFactory = makeParamDecorator(InjectMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link OptionalMetadata}.
|
||||
*/
|
||||
export var Optional: OptionalFactory = makeParamDecorator(OptionalMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectableMetadata}.
|
||||
*/
|
||||
export var Injectable: InjectableFactory = <InjectableFactory>makeDecorator(InjectableMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SelfMetadata}.
|
||||
*/
|
||||
export var Self: SelfFactory = makeParamDecorator(SelfMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link HostMetadata}.
|
||||
*/
|
||||
export var Host: HostFactory = makeParamDecorator(HostMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SkipSelfMetadata}.
|
||||
*/
|
||||
export var SkipSelf: SkipSelfFactory = makeParamDecorator(SkipSelfMetadata);
|
169
modules/angular2/src/core/di/exceptions.ts
Normal file
169
modules/angular2/src/core/di/exceptions.ts
Normal file
@ -0,0 +1,169 @@
|
||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {stringify, BaseException, isBlank} from 'angular2/src/facade/lang';
|
||||
import {Key} from './key';
|
||||
import {Injector} from './injector';
|
||||
|
||||
function findFirstClosedCycle(keys: List<any>): List<any> {
|
||||
var res = [];
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
if (ListWrapper.contains(res, keys[i])) {
|
||||
res.push(keys[i]);
|
||||
return res;
|
||||
} else {
|
||||
res.push(keys[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function constructResolvingPath(keys: List<any>): string {
|
||||
if (keys.length > 1) {
|
||||
var reversed = findFirstClosedCycle(ListWrapper.reversed(keys));
|
||||
var tokenStrs = ListWrapper.map(reversed, (k) => stringify(k.token));
|
||||
return " (" + tokenStrs.join(' -> ') + ")";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for all errors arising from misconfigured bindings.
|
||||
*/
|
||||
export class AbstractBindingError extends BaseException {
|
||||
name: string;
|
||||
message: string;
|
||||
keys: List<Key>;
|
||||
injectors: List<Injector>;
|
||||
constructResolvingMessage: Function;
|
||||
|
||||
constructor(injector: Injector, key: Key, constructResolvingMessage: Function, originalException?,
|
||||
originalStack?) {
|
||||
super("DI Exception", originalException, originalStack, null);
|
||||
this.keys = [key];
|
||||
this.injectors = [injector];
|
||||
this.constructResolvingMessage = constructResolvingMessage;
|
||||
this.message = this.constructResolvingMessage(this.keys);
|
||||
}
|
||||
|
||||
addKey(injector: Injector, key: Key): void {
|
||||
this.injectors.push(injector);
|
||||
this.keys.push(key);
|
||||
this.message = this.constructResolvingMessage(this.keys);
|
||||
}
|
||||
|
||||
get context() { return this.injectors[this.injectors.length - 1].debugContext(); }
|
||||
|
||||
toString(): string { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the
|
||||
* {@link Injector} does not have a {@link Binding} for {@link Key}.
|
||||
*/
|
||||
export class NoBindingError extends AbstractBindingError {
|
||||
constructor(injector: Injector, key: Key) {
|
||||
super(injector, key, function(keys: List<any>) {
|
||||
var first = stringify(ListWrapper.first(keys).token);
|
||||
return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when dependencies form a cycle.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```javascript
|
||||
* class A {
|
||||
* constructor(b:B) {}
|
||||
* }
|
||||
* class B {
|
||||
* constructor(a:A) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
|
||||
*/
|
||||
export class CyclicDependencyError extends AbstractBindingError {
|
||||
constructor(injector: Injector, key: Key) {
|
||||
super(injector, key, function(keys: List<any>) {
|
||||
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a constructing type returns with an Error.
|
||||
*
|
||||
* The `InstantiationError` class contains the original error plus the dependency graph which caused
|
||||
* this object to be instantiated.
|
||||
*/
|
||||
export class InstantiationError extends AbstractBindingError {
|
||||
causeKey: Key;
|
||||
constructor(injector: Injector, originalException, originalStack, key: Key) {
|
||||
super(injector, key, function(keys: List<any>) {
|
||||
var first = stringify(ListWrapper.first(keys).token);
|
||||
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
|
||||
}, originalException, originalStack);
|
||||
|
||||
this.causeKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an object other then {@link Binding} (or `Type`) is passed to {@link Injector}
|
||||
* creation.
|
||||
*/
|
||||
export class InvalidBindingError extends BaseException {
|
||||
message: string;
|
||||
constructor(binding) {
|
||||
super();
|
||||
this.message = "Invalid binding - only instances of Binding and Type are allowed, got: " +
|
||||
binding.toString();
|
||||
}
|
||||
|
||||
toString(): string { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the class has no annotation information.
|
||||
*
|
||||
* Lack of annotation information prevents the {@link Injector} from determining which dependencies
|
||||
* need to be injected into the constructor.
|
||||
*/
|
||||
export class NoAnnotationError extends BaseException {
|
||||
name: string;
|
||||
message: string;
|
||||
constructor(typeOrFunc, params: List<List<any>>) {
|
||||
super();
|
||||
var signature = [];
|
||||
for (var i = 0, ii = params.length; i < ii; i++) {
|
||||
var parameter = params[i];
|
||||
if (isBlank(parameter) || parameter.length == 0) {
|
||||
signature.push('?');
|
||||
} else {
|
||||
signature.push(ListWrapper.map(parameter, stringify).join(' '));
|
||||
}
|
||||
}
|
||||
this.message = "Cannot resolve all parameters for " + stringify(typeOrFunc) + "(" +
|
||||
signature.join(', ') + "). " +
|
||||
'Make sure they all have valid type or annotations.';
|
||||
}
|
||||
|
||||
toString(): string { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when getting an object by index.
|
||||
*/
|
||||
export class OutOfBoundsError extends BaseException {
|
||||
message: string;
|
||||
constructor(index) {
|
||||
super();
|
||||
this.message = `Index ${index} is out-of-bounds.`;
|
||||
}
|
||||
|
||||
toString(): string { return this.message; }
|
||||
}
|
15
modules/angular2/src/core/di/forward_ref.dart
Normal file
15
modules/angular2/src/core/di/forward_ref.dart
Normal file
@ -0,0 +1,15 @@
|
||||
library angular2.di.forward_ref;
|
||||
|
||||
typedef dynamic ForwardRefFn();
|
||||
|
||||
/**
|
||||
* Dart does not have the forward ref problem, so this function is a noop.
|
||||
*/
|
||||
forwardRef(ForwardRefFn forwardRefFn) => forwardRefFn();
|
||||
|
||||
/**
|
||||
* Lazily retrieve the reference value.
|
||||
*
|
||||
* See: {@link forwardRef}
|
||||
*/
|
||||
resolveForwardRef(type) => type;
|
47
modules/angular2/src/core/di/forward_ref.ts
Normal file
47
modules/angular2/src/core/di/forward_ref.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {Type, stringify, isFunction} from 'angular2/src/facade/lang';
|
||||
|
||||
export interface ForwardRefFn { (): any; }
|
||||
|
||||
/**
|
||||
* Allows to refer to references which are not yet defined.
|
||||
*
|
||||
* This situation arises when the key which we need te refer to for the purposes of DI is declared,
|
||||
* but not yet defined.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```
|
||||
* class Door {
|
||||
* // Incorrect way to refer to a reference which is defined later.
|
||||
* // This fails because `Lock` is undefined at this point.
|
||||
* constructor(lock:Lock) { }
|
||||
*
|
||||
* // Correct way to refer to a reference which is defined later.
|
||||
* // The reference needs to be captured in a closure.
|
||||
* constructor(@Inject(forwardRef(() => Lock)) lock:Lock) { }
|
||||
* }
|
||||
*
|
||||
* // Only at this point the lock is defined.
|
||||
* class Lock {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function forwardRef(forwardRefFn: ForwardRefFn): Type {
|
||||
(<any>forwardRefFn).__forward_ref__ = forwardRef;
|
||||
(<any>forwardRefFn).toString = function() { return stringify(this()); };
|
||||
return (<Type><any>forwardRefFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily retrieve the reference value.
|
||||
*
|
||||
* See: {@link forwardRef}
|
||||
*/
|
||||
export function resolveForwardRef(type: any): any {
|
||||
if (isFunction(type) && type.hasOwnProperty('__forward_ref__') &&
|
||||
type.__forward_ref__ === forwardRef) {
|
||||
return (<ForwardRefFn>type)();
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
850
modules/angular2/src/core/di/injector.ts
Normal file
850
modules/angular2/src/core/di/injector.ts
Normal file
@ -0,0 +1,850 @@
|
||||
import {Map, List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ResolvedBinding, Binding, Dependency, BindingBuilder, bind} from './binding';
|
||||
import {
|
||||
AbstractBindingError,
|
||||
NoBindingError,
|
||||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidBindingError,
|
||||
OutOfBoundsError
|
||||
} from './exceptions';
|
||||
import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Key} from './key';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
|
||||
|
||||
// Threshold for the dynamic version
|
||||
const _MAX_CONSTRUCTION_COUNTER = 10;
|
||||
|
||||
export const UNDEFINED: Object = CONST_EXPR(new Object());
|
||||
|
||||
export enum Visibility {
|
||||
Public,
|
||||
Private,
|
||||
PublicAndPrivate
|
||||
}
|
||||
|
||||
function canSee(src: Visibility, dst: Visibility): boolean {
|
||||
return (src === dst) ||
|
||||
(dst === Visibility.PublicAndPrivate || src === Visibility.PublicAndPrivate);
|
||||
}
|
||||
|
||||
|
||||
export interface ProtoInjectorStrategy {
|
||||
getBindingAtIndex(index: number): ResolvedBinding;
|
||||
createInjectorStrategy(inj: Injector): InjectorStrategy;
|
||||
}
|
||||
|
||||
export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||
binding0: ResolvedBinding = null;
|
||||
binding1: ResolvedBinding = null;
|
||||
binding2: ResolvedBinding = null;
|
||||
binding3: ResolvedBinding = null;
|
||||
binding4: ResolvedBinding = null;
|
||||
binding5: ResolvedBinding = null;
|
||||
binding6: ResolvedBinding = null;
|
||||
binding7: ResolvedBinding = null;
|
||||
binding8: ResolvedBinding = null;
|
||||
binding9: ResolvedBinding = null;
|
||||
|
||||
keyId0: number = null;
|
||||
keyId1: number = null;
|
||||
keyId2: number = null;
|
||||
keyId3: number = null;
|
||||
keyId4: number = null;
|
||||
keyId5: number = null;
|
||||
keyId6: number = null;
|
||||
keyId7: number = null;
|
||||
keyId8: number = null;
|
||||
keyId9: number = null;
|
||||
|
||||
visibility0: Visibility = null;
|
||||
visibility1: Visibility = null;
|
||||
visibility2: Visibility = null;
|
||||
visibility3: Visibility = null;
|
||||
visibility4: Visibility = null;
|
||||
visibility5: Visibility = null;
|
||||
visibility6: Visibility = null;
|
||||
visibility7: Visibility = null;
|
||||
visibility8: Visibility = null;
|
||||
visibility9: Visibility = null;
|
||||
|
||||
constructor(protoEI: ProtoInjector, bwv: BindingWithVisibility[]) {
|
||||
var length = bwv.length;
|
||||
|
||||
if (length > 0) {
|
||||
this.binding0 = bwv[0].binding;
|
||||
this.keyId0 = bwv[0].getKeyId();
|
||||
this.visibility0 = bwv[0].visibility;
|
||||
}
|
||||
if (length > 1) {
|
||||
this.binding1 = bwv[1].binding;
|
||||
this.keyId1 = bwv[1].getKeyId();
|
||||
this.visibility1 = bwv[1].visibility;
|
||||
}
|
||||
if (length > 2) {
|
||||
this.binding2 = bwv[2].binding;
|
||||
this.keyId2 = bwv[2].getKeyId();
|
||||
this.visibility2 = bwv[2].visibility;
|
||||
}
|
||||
if (length > 3) {
|
||||
this.binding3 = bwv[3].binding;
|
||||
this.keyId3 = bwv[3].getKeyId();
|
||||
this.visibility3 = bwv[3].visibility;
|
||||
}
|
||||
if (length > 4) {
|
||||
this.binding4 = bwv[4].binding;
|
||||
this.keyId4 = bwv[4].getKeyId();
|
||||
this.visibility4 = bwv[4].visibility;
|
||||
}
|
||||
if (length > 5) {
|
||||
this.binding5 = bwv[5].binding;
|
||||
this.keyId5 = bwv[5].getKeyId();
|
||||
this.visibility5 = bwv[5].visibility;
|
||||
}
|
||||
if (length > 6) {
|
||||
this.binding6 = bwv[6].binding;
|
||||
this.keyId6 = bwv[6].getKeyId();
|
||||
this.visibility6 = bwv[6].visibility;
|
||||
}
|
||||
if (length > 7) {
|
||||
this.binding7 = bwv[7].binding;
|
||||
this.keyId7 = bwv[7].getKeyId();
|
||||
this.visibility7 = bwv[7].visibility;
|
||||
}
|
||||
if (length > 8) {
|
||||
this.binding8 = bwv[8].binding;
|
||||
this.keyId8 = bwv[8].getKeyId();
|
||||
this.visibility8 = bwv[8].visibility;
|
||||
}
|
||||
if (length > 9) {
|
||||
this.binding9 = bwv[9].binding;
|
||||
this.keyId9 = bwv[9].getKeyId();
|
||||
this.visibility9 = bwv[9].visibility;
|
||||
}
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any {
|
||||
if (index == 0) return this.binding0;
|
||||
if (index == 1) return this.binding1;
|
||||
if (index == 2) return this.binding2;
|
||||
if (index == 3) return this.binding3;
|
||||
if (index == 4) return this.binding4;
|
||||
if (index == 5) return this.binding5;
|
||||
if (index == 6) return this.binding6;
|
||||
if (index == 7) return this.binding7;
|
||||
if (index == 8) return this.binding8;
|
||||
if (index == 9) return this.binding9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
createInjectorStrategy(injector: Injector): InjectorStrategy {
|
||||
return new InjectorInlineStrategy(injector, this);
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||
bindings: ResolvedBinding[];
|
||||
keyIds: number[];
|
||||
visibilities: Visibility[];
|
||||
|
||||
constructor(protoInj: ProtoInjector, bwv: BindingWithVisibility[]) {
|
||||
var len = bwv.length;
|
||||
|
||||
this.bindings = ListWrapper.createFixedSize(len);
|
||||
this.keyIds = ListWrapper.createFixedSize(len);
|
||||
this.visibilities = ListWrapper.createFixedSize(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
this.bindings[i] = bwv[i].binding;
|
||||
this.keyIds[i] = bwv[i].getKeyId();
|
||||
this.visibilities[i] = bwv[i].visibility;
|
||||
}
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any {
|
||||
if (index < 0 || index >= this.bindings.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
return this.bindings[index];
|
||||
}
|
||||
|
||||
createInjectorStrategy(ei: Injector): InjectorStrategy {
|
||||
return new InjectorDynamicStrategy(this, ei);
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoInjector {
|
||||
_strategy: ProtoInjectorStrategy;
|
||||
numberOfBindings: number;
|
||||
|
||||
constructor(bwv: BindingWithVisibility[]) {
|
||||
this.numberOfBindings = bwv.length;
|
||||
this._strategy = bwv.length > _MAX_CONSTRUCTION_COUNTER ?
|
||||
new ProtoInjectorDynamicStrategy(this, bwv) :
|
||||
new ProtoInjectorInlineStrategy(this, bwv);
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface InjectorStrategy {
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any;
|
||||
getObjAtIndex(index: number): any;
|
||||
getMaxNumberOfObjects(): number;
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void;
|
||||
resetConstructionCounter(): void;
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: Visibility): any;
|
||||
}
|
||||
|
||||
export class InjectorInlineStrategy implements InjectorStrategy {
|
||||
obj0: any = UNDEFINED;
|
||||
obj1: any = UNDEFINED;
|
||||
obj2: any = UNDEFINED;
|
||||
obj3: any = UNDEFINED;
|
||||
obj4: any = UNDEFINED;
|
||||
obj5: any = UNDEFINED;
|
||||
obj6: any = UNDEFINED;
|
||||
obj7: any = UNDEFINED;
|
||||
obj8: any = UNDEFINED;
|
||||
obj9: any = UNDEFINED;
|
||||
|
||||
constructor(public injector: Injector, public protoStrategy: ProtoInjectorInlineStrategy) {}
|
||||
|
||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: Visibility): any {
|
||||
return this.injector._new(binding, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
|
||||
if (p.keyId0 === keyId && canSee(p.visibility0, visibility)) {
|
||||
if (this.obj0 === UNDEFINED) {
|
||||
this.obj0 = inj._new(p.binding0, p.visibility0);
|
||||
}
|
||||
return this.obj0;
|
||||
}
|
||||
if (p.keyId1 === keyId && canSee(p.visibility1, visibility)) {
|
||||
if (this.obj1 === UNDEFINED) {
|
||||
this.obj1 = inj._new(p.binding1, p.visibility1);
|
||||
}
|
||||
return this.obj1;
|
||||
}
|
||||
if (p.keyId2 === keyId && canSee(p.visibility2, visibility)) {
|
||||
if (this.obj2 === UNDEFINED) {
|
||||
this.obj2 = inj._new(p.binding2, p.visibility2);
|
||||
}
|
||||
return this.obj2;
|
||||
}
|
||||
if (p.keyId3 === keyId && canSee(p.visibility3, visibility)) {
|
||||
if (this.obj3 === UNDEFINED) {
|
||||
this.obj3 = inj._new(p.binding3, p.visibility3);
|
||||
}
|
||||
return this.obj3;
|
||||
}
|
||||
if (p.keyId4 === keyId && canSee(p.visibility4, visibility)) {
|
||||
if (this.obj4 === UNDEFINED) {
|
||||
this.obj4 = inj._new(p.binding4, p.visibility4);
|
||||
}
|
||||
return this.obj4;
|
||||
}
|
||||
if (p.keyId5 === keyId && canSee(p.visibility5, visibility)) {
|
||||
if (this.obj5 === UNDEFINED) {
|
||||
this.obj5 = inj._new(p.binding5, p.visibility5);
|
||||
}
|
||||
return this.obj5;
|
||||
}
|
||||
if (p.keyId6 === keyId && canSee(p.visibility6, visibility)) {
|
||||
if (this.obj6 === UNDEFINED) {
|
||||
this.obj6 = inj._new(p.binding6, p.visibility6);
|
||||
}
|
||||
return this.obj6;
|
||||
}
|
||||
if (p.keyId7 === keyId && canSee(p.visibility7, visibility)) {
|
||||
if (this.obj7 === UNDEFINED) {
|
||||
this.obj7 = inj._new(p.binding7, p.visibility7);
|
||||
}
|
||||
return this.obj7;
|
||||
}
|
||||
if (p.keyId8 === keyId && canSee(p.visibility8, visibility)) {
|
||||
if (this.obj8 === UNDEFINED) {
|
||||
this.obj8 = inj._new(p.binding8, p.visibility8);
|
||||
}
|
||||
return this.obj8;
|
||||
}
|
||||
if (p.keyId9 === keyId && canSee(p.visibility9, visibility)) {
|
||||
if (this.obj9 === UNDEFINED) {
|
||||
this.obj9 = inj._new(p.binding9, p.visibility9);
|
||||
}
|
||||
return this.obj9;
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index == 0) return this.obj0;
|
||||
if (index == 1) return this.obj1;
|
||||
if (index == 2) return this.obj2;
|
||||
if (index == 3) return this.obj3;
|
||||
if (index == 4) return this.obj4;
|
||||
if (index == 5) return this.obj5;
|
||||
if (index == 6) return this.obj6;
|
||||
if (index == 7) return this.obj7;
|
||||
if (index == 8) return this.obj8;
|
||||
if (index == 9) return this.obj9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
|
||||
}
|
||||
|
||||
|
||||
export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||
objs: any[];
|
||||
|
||||
constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector) {
|
||||
this.objs = ListWrapper.createFixedSize(protoStrategy.bindings.length);
|
||||
ListWrapper.fill(this.objs, UNDEFINED);
|
||||
}
|
||||
|
||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
instantiateBinding(binding: ResolvedBinding, visibility: Visibility): any {
|
||||
return this.injector._new(binding, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (p.keyIds[i] === keyId && canSee(p.visibilities[i], visibility)) {
|
||||
if (this.objs[i] === UNDEFINED) {
|
||||
this.objs[i] = this.injector._new(p.bindings[i], p.visibilities[i]);
|
||||
}
|
||||
|
||||
return this.objs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index < 0 || index >= this.objs.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
return this.objs[index];
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||
}
|
||||
|
||||
export class BindingWithVisibility {
|
||||
constructor(public binding: ResolvedBinding, public visibility: Visibility){};
|
||||
|
||||
getKeyId(): number { return this.binding.key.id; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to provide dependencies that cannot be easily expressed as bindings.
|
||||
*/
|
||||
export interface DependencyProvider {
|
||||
getDependency(injector: Injector, binding: ResolvedBinding, dependency: Dependency): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency injection container used for resolving dependencies.
|
||||
*
|
||||
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
||||
* constructor dependencies.
|
||||
* In typical use, application code asks for the dependencies in the constructor and they are
|
||||
* resolved by the `Injector`.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* Suppose that we want to inject an `Engine` into class `Car`, we would define it like this:
|
||||
*
|
||||
* ```javascript
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* class Car {
|
||||
* constructor(@Inject(Engine) engine) {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the
|
||||
* `root` object, `Car`, so that the `Injector` can recursively build all of that object's
|
||||
*dependencies.
|
||||
*
|
||||
* ```javascript
|
||||
* main() {
|
||||
* var injector = Injector.resolveAndCreate([Car, Engine]);
|
||||
*
|
||||
* // Get a reference to the `root` object, which will recursively instantiate the tree.
|
||||
* var car = injector.get(Car);
|
||||
* }
|
||||
* ```
|
||||
* Notice that we don't use the `new` operator because we explicitly want to have the `Injector`
|
||||
* resolve all of the object's dependencies automatically.
|
||||
*/
|
||||
export class Injector {
|
||||
/**
|
||||
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested lists and converting individual
|
||||
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
|
||||
* for the {@link Injector} for performance-sensitive code.
|
||||
*
|
||||
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
|
||||
* recursive list of more bindings.
|
||||
*
|
||||
* The returned list is sparse, indexed by `id` for the {@link Key}. It is generally not useful to
|
||||
*application code
|
||||
* other than for passing it to {@link Injector} functions that require resolved binding lists,
|
||||
*such as
|
||||
* `fromResolvedBindings` and `createChildFromResolved`.
|
||||
*/
|
||||
static resolve(bindings: List<Type | Binding | List<any>>): List<ResolvedBinding> {
|
||||
var resolvedBindings = _resolveBindings(bindings);
|
||||
var flatten = _flattenBindings(resolvedBindings, new Map());
|
||||
return _createListOfBindings(flatten);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves bindings and creates an injector based on those bindings. This function is slower than
|
||||
* the corresponding `fromResolvedBindings` because it needs to resolve bindings first. See
|
||||
*`resolve`
|
||||
* for the {@link Injector}.
|
||||
*
|
||||
* Prefer `fromResolvedBindings` in performance-critical code that creates lots of injectors.
|
||||
*
|
||||
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
|
||||
*recursive list of more
|
||||
* bindings.
|
||||
* @param `depProvider`
|
||||
*/
|
||||
static resolveAndCreate(bindings: List<Type | Binding | List<any>>,
|
||||
depProvider: DependencyProvider = null): Injector {
|
||||
var resolvedBindings = Injector.resolve(bindings);
|
||||
return Injector.fromResolvedBindings(resolvedBindings, depProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening.
|
||||
* This API is the recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the
|
||||
* {@link Injector}.
|
||||
* @param `depProvider`
|
||||
*/
|
||||
static fromResolvedBindings(bindings: List<ResolvedBinding>,
|
||||
depProvider: DependencyProvider = null): Injector {
|
||||
var bd = bindings.map(b => new BindingWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
var inj = new Injector(proto, null, depProvider);
|
||||
return inj;
|
||||
}
|
||||
|
||||
_strategy: InjectorStrategy;
|
||||
_isHost: boolean = false;
|
||||
_constructionCounter: number = 0;
|
||||
|
||||
constructor(public _proto: ProtoInjector, public _parent: Injector = null,
|
||||
private _depProvider: DependencyProvider = null,
|
||||
private _debugContext: Function = null) {
|
||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns debug information about the injector.
|
||||
*
|
||||
* This information is included into exceptions thrown by the injector.
|
||||
*/
|
||||
debugContext(): any { return this._debugContext(); }
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector.
|
||||
*
|
||||
* @param `token`: usually the `Type` of an object. (Same as the token used while setting up a
|
||||
*binding).
|
||||
* @returns an instance represented by the token. Throws if not found.
|
||||
*/
|
||||
get(token: any): any {
|
||||
return this._getByKey(Key.get(token), null, null, false, Visibility.PublicAndPrivate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector.
|
||||
*
|
||||
* @param `token`: usually a `Type`. (Same as the token used while setting up a binding).
|
||||
* @returns an instance represented by the token. Returns `null` if not found.
|
||||
*/
|
||||
getOptional(token: any): any {
|
||||
return this._getByKey(Key.get(token), null, null, true, Visibility.PublicAndPrivate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector.
|
||||
*
|
||||
* @param `index`: index of an instance.
|
||||
* @returns an instance represented by the index. Throws if not found.
|
||||
*/
|
||||
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
|
||||
|
||||
/**
|
||||
* Direct parent of this injector.
|
||||
*/
|
||||
get parent(): Injector { return this._parent; }
|
||||
|
||||
/**
|
||||
* Internal. Do not use.
|
||||
*
|
||||
* We return `any` not to export the InjectorStrategy type.
|
||||
*/
|
||||
get internalStrategy(): any { return this._strategy; }
|
||||
|
||||
/**
|
||||
* Creates a child injector and loads a new set of bindings into it.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested lists and converting individual
|
||||
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
|
||||
* for the {@link Injector} for performance-sensitive code.
|
||||
*
|
||||
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
|
||||
* recursive list of more bindings.
|
||||
* @param `depProvider`
|
||||
*/
|
||||
resolveAndCreateChild(bindings: List<Type | Binding | List<any>>,
|
||||
depProvider: DependencyProvider = null): Injector {
|
||||
var resovledBindings = Injector.resolve(bindings);
|
||||
return this.createChildFromResolved(resovledBindings, depProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a child injector and loads a new set of {@link ResolvedBinding}s into it.
|
||||
*
|
||||
* @param `bindings`: A sparse list of {@link ResolvedBinding}s.
|
||||
* See `resolve` for the {@link Injector}.
|
||||
* @param `depProvider`
|
||||
* @returns a new child {@link Injector}.
|
||||
*/
|
||||
createChildFromResolved(bindings: List<ResolvedBinding>,
|
||||
depProvider: DependencyProvider = null): Injector {
|
||||
var bd = bindings.map(b => new BindingWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
var inj = new Injector(proto, null, depProvider);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a binding and instantiates an object in the context of the injector.
|
||||
*
|
||||
* @param `binding`: either a type or a binding.
|
||||
* @returns an object created using binding.
|
||||
*/
|
||||
resolveAndInstantiate(binding: Type | Binding): any {
|
||||
return this.instantiateResolved(Injector.resolve([binding])[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates an object using a resolved bindin in the context of the injector.
|
||||
*
|
||||
* @param `binding`: a resolved binding
|
||||
* @returns an object created using binding.
|
||||
*/
|
||||
instantiateResolved(binding: ResolvedBinding): any {
|
||||
return this._instantiate(binding, Visibility.PublicAndPrivate);
|
||||
}
|
||||
|
||||
_new(binding: ResolvedBinding, visibility: Visibility): any {
|
||||
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
||||
throw new CyclicDependencyError(this, binding.key);
|
||||
}
|
||||
return this._instantiate(binding, visibility);
|
||||
}
|
||||
|
||||
private _instantiate(binding: ResolvedBinding, visibility: Visibility): any {
|
||||
var factory = binding.factory;
|
||||
var deps = binding.dependencies;
|
||||
var length = deps.length;
|
||||
|
||||
var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;
|
||||
try {
|
||||
d0 = length > 0 ? this._getByDependency(binding, deps[0], visibility) : null;
|
||||
d1 = length > 1 ? this._getByDependency(binding, deps[1], visibility) : null;
|
||||
d2 = length > 2 ? this._getByDependency(binding, deps[2], visibility) : null;
|
||||
d3 = length > 3 ? this._getByDependency(binding, deps[3], visibility) : null;
|
||||
d4 = length > 4 ? this._getByDependency(binding, deps[4], visibility) : null;
|
||||
d5 = length > 5 ? this._getByDependency(binding, deps[5], visibility) : null;
|
||||
d6 = length > 6 ? this._getByDependency(binding, deps[6], visibility) : null;
|
||||
d7 = length > 7 ? this._getByDependency(binding, deps[7], visibility) : null;
|
||||
d8 = length > 8 ? this._getByDependency(binding, deps[8], visibility) : null;
|
||||
d9 = length > 9 ? this._getByDependency(binding, deps[9], visibility) : null;
|
||||
d10 = length > 10 ? this._getByDependency(binding, deps[10], visibility) : null;
|
||||
d11 = length > 11 ? this._getByDependency(binding, deps[11], visibility) : null;
|
||||
d12 = length > 12 ? this._getByDependency(binding, deps[12], visibility) : null;
|
||||
d13 = length > 13 ? this._getByDependency(binding, deps[13], visibility) : null;
|
||||
d14 = length > 14 ? this._getByDependency(binding, deps[14], visibility) : null;
|
||||
d15 = length > 15 ? this._getByDependency(binding, deps[15], visibility) : null;
|
||||
d16 = length > 16 ? this._getByDependency(binding, deps[16], visibility) : null;
|
||||
d17 = length > 17 ? this._getByDependency(binding, deps[17], visibility) : null;
|
||||
d18 = length > 18 ? this._getByDependency(binding, deps[18], visibility) : null;
|
||||
d19 = length > 19 ? this._getByDependency(binding, deps[19], visibility) : null;
|
||||
} catch (e) {
|
||||
if (e instanceof AbstractBindingError) {
|
||||
e.addKey(this, binding.key);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
var obj;
|
||||
try {
|
||||
switch (length) {
|
||||
case 0:
|
||||
obj = factory();
|
||||
break;
|
||||
case 1:
|
||||
obj = factory(d0);
|
||||
break;
|
||||
case 2:
|
||||
obj = factory(d0, d1);
|
||||
break;
|
||||
case 3:
|
||||
obj = factory(d0, d1, d2);
|
||||
break;
|
||||
case 4:
|
||||
obj = factory(d0, d1, d2, d3);
|
||||
break;
|
||||
case 5:
|
||||
obj = factory(d0, d1, d2, d3, d4);
|
||||
break;
|
||||
case 6:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5);
|
||||
break;
|
||||
case 7:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6);
|
||||
break;
|
||||
case 8:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
|
||||
break;
|
||||
case 9:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
|
||||
break;
|
||||
case 10:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
|
||||
break;
|
||||
case 11:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
|
||||
break;
|
||||
case 12:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
|
||||
break;
|
||||
case 13:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
|
||||
break;
|
||||
case 14:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
|
||||
break;
|
||||
case 15:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
|
||||
break;
|
||||
case 16:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
|
||||
break;
|
||||
case 17:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
|
||||
break;
|
||||
case 18:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17);
|
||||
break;
|
||||
case 19:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18);
|
||||
break;
|
||||
case 20:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18, d19);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
throw new InstantiationError(this, e, e.stack, binding.key);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private _getByDependency(binding: ResolvedBinding, dep: Dependency,
|
||||
bindingVisibility: Visibility): any {
|
||||
var special = isPresent(this._depProvider) ?
|
||||
this._depProvider.getDependency(this, binding, dep) :
|
||||
UNDEFINED;
|
||||
if (special !== UNDEFINED) {
|
||||
return special;
|
||||
} else {
|
||||
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
|
||||
dep.optional, bindingVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
private _getByKey(key: Key, lowerBoundVisibility: Object, upperBoundVisibility: Object,
|
||||
optional: boolean, bindingVisibility: Visibility): any {
|
||||
if (key === INJECTOR_KEY) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (upperBoundVisibility instanceof SelfMetadata) {
|
||||
return this._getByKeySelf(key, optional, bindingVisibility);
|
||||
|
||||
} else if (upperBoundVisibility instanceof HostMetadata) {
|
||||
return this._getByKeyHost(key, optional, bindingVisibility, lowerBoundVisibility);
|
||||
|
||||
} else {
|
||||
return this._getByKeyDefault(key, optional, bindingVisibility, lowerBoundVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
_throwOrNull(key: Key, optional: boolean): any {
|
||||
if (optional) {
|
||||
return null;
|
||||
} else {
|
||||
throw new NoBindingError(this, key);
|
||||
}
|
||||
}
|
||||
|
||||
_getByKeySelf(key: Key, optional: boolean, bindingVisibility: Visibility): any {
|
||||
var obj = this._strategy.getObjByKeyId(key.id, bindingVisibility);
|
||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
|
||||
}
|
||||
|
||||
_getByKeyHost(key: Key, optional: boolean, bindingVisibility: Visibility,
|
||||
lowerBoundVisibility: Object): any {
|
||||
var inj = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
if (inj._isHost) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
}
|
||||
}
|
||||
|
||||
while (inj != null) {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
if (isPresent(inj._parent) && inj._isHost) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
}
|
||||
}
|
||||
|
||||
return this._throwOrNull(key, optional);
|
||||
}
|
||||
|
||||
_getPrivateDependency(key: Key, optional: boolean, inj: Injector): any {
|
||||
var obj = inj._parent._strategy.getObjByKeyId(key.id, Visibility.Private);
|
||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
|
||||
}
|
||||
|
||||
_getByKeyDefault(key: Key, optional: boolean, bindingVisibility: Visibility,
|
||||
lowerBoundVisibility: Object): any {
|
||||
var inj = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
bindingVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
while (inj != null) {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
bindingVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
return this._throwOrNull(key, optional);
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
return `Injector(bindings: [${_mapBindings(this, b => ` "${b.key.displayName}" `).join(", ")}])`;
|
||||
}
|
||||
|
||||
toString(): string { return this.displayName; }
|
||||
}
|
||||
|
||||
var INJECTOR_KEY = Key.get(Injector);
|
||||
|
||||
|
||||
function _resolveBindings(bindings: List<Type | Binding | List<any>>): List<ResolvedBinding> {
|
||||
var resolvedList = ListWrapper.createFixedSize(bindings.length);
|
||||
for (var i = 0; i < bindings.length; i++) {
|
||||
var unresolved = resolveForwardRef(bindings[i]);
|
||||
var resolved;
|
||||
if (unresolved instanceof ResolvedBinding) {
|
||||
resolved = unresolved; // ha-ha! I'm easily amused
|
||||
} else if (unresolved instanceof Type) {
|
||||
resolved = bind(unresolved).toClass(unresolved).resolve();
|
||||
} else if (unresolved instanceof Binding) {
|
||||
resolved = unresolved.resolve();
|
||||
} else if (unresolved instanceof List) {
|
||||
resolved = _resolveBindings(unresolved);
|
||||
} else if (unresolved instanceof BindingBuilder) {
|
||||
throw new InvalidBindingError(unresolved.token);
|
||||
} else {
|
||||
throw new InvalidBindingError(unresolved);
|
||||
}
|
||||
resolvedList[i] = resolved;
|
||||
}
|
||||
return resolvedList;
|
||||
}
|
||||
|
||||
function _createListOfBindings(flattenedBindings: Map<number, ResolvedBinding>):
|
||||
List<ResolvedBinding> {
|
||||
return MapWrapper.values(flattenedBindings);
|
||||
}
|
||||
|
||||
function _flattenBindings(bindings: List<ResolvedBinding | List<any>>,
|
||||
res: Map<number, ResolvedBinding>): Map<number, ResolvedBinding> {
|
||||
ListWrapper.forEach(bindings, function(b) {
|
||||
if (b instanceof ResolvedBinding) {
|
||||
res.set(b.key.id, b);
|
||||
} else if (b instanceof List) {
|
||||
_flattenBindings(b, res);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
function _mapBindings(injector: Injector, fn: Function): any[] {
|
||||
var res = [];
|
||||
for (var i = 0; i < injector._proto.numberOfBindings; ++i) {
|
||||
res.push(fn(injector._proto.getBindingAtIndex(i)));
|
||||
}
|
||||
return res;
|
||||
}
|
66
modules/angular2/src/core/di/key.ts
Normal file
66
modules/angular2/src/core/di/key.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {stringify, CONST, Type, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {TypeLiteral} from './type_literal';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
|
||||
export {TypeLiteral} from './type_literal';
|
||||
|
||||
/**
|
||||
* A unique object used for retrieving items from the {@link Injector}.
|
||||
*
|
||||
* Keys have:
|
||||
* - a system-wide unique `id`.
|
||||
* - a `token`, usually the `Type` of the instance.
|
||||
*
|
||||
* Keys are used internally by the {@link Injector} because their system-wide unique `id`s allow the
|
||||
* injector to index in arrays rather than looking up items in maps.
|
||||
*/
|
||||
export class Key {
|
||||
constructor(public token: Object, public id: number) {
|
||||
if (isBlank(token)) {
|
||||
throw new BaseException('Token must be defined!');
|
||||
}
|
||||
}
|
||||
|
||||
get displayName(): string { return stringify(this.token); }
|
||||
|
||||
/**
|
||||
* Retrieves a `Key` for a token.
|
||||
*/
|
||||
static get(token: Object): Key { return _globalKeyRegistry.get(resolveForwardRef(token)); }
|
||||
|
||||
/**
|
||||
* @returns the number of keys registered in the system.
|
||||
*/
|
||||
static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class KeyRegistry {
|
||||
private _allKeys: Map<Object, Key> = new Map();
|
||||
|
||||
get(token: Object): Key {
|
||||
if (token instanceof Key) return token;
|
||||
|
||||
// TODO: workaround for https://github.com/Microsoft/TypeScript/issues/3123
|
||||
var theToken = token;
|
||||
if (token instanceof TypeLiteral) {
|
||||
theToken = token.type;
|
||||
}
|
||||
token = theToken;
|
||||
|
||||
if (this._allKeys.has(token)) {
|
||||
return this._allKeys.get(token);
|
||||
}
|
||||
|
||||
var newKey = new Key(token, Key.numberOfKeys);
|
||||
this._allKeys.set(token, newKey);
|
||||
return newKey;
|
||||
}
|
||||
|
||||
get numberOfKeys(): number { return MapWrapper.size(this._allKeys); }
|
||||
}
|
||||
|
||||
var _globalKeyRegistry = new KeyRegistry();
|
166
modules/angular2/src/core/di/metadata.ts
Normal file
166
modules/angular2/src/core/di/metadata.ts
Normal file
@ -0,0 +1,166 @@
|
||||
import {CONST, CONST_EXPR, stringify, isBlank, isPresent} from "angular2/src/facade/lang";
|
||||
|
||||
/**
|
||||
* A parameter metadata that specifies a dependency.
|
||||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@Inject(MyService) aService:MyService) {}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
@CONST()
|
||||
export class InjectMetadata {
|
||||
constructor(public token) {}
|
||||
toString(): string { return `@Inject(${stringify(this.token)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
|
||||
* the dependency is not found.
|
||||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@Optional() aService:MyService) {
|
||||
* this.aService = aService;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class OptionalMetadata {
|
||||
toString(): string { return `@Optional()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* `DependencyMetadata is used by the framework to extend DI.
|
||||
*
|
||||
* Only metadata implementing `DependencyMetadata` are added to the list of dependency
|
||||
* properties.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* class Exclude extends DependencyMetadata {}
|
||||
* class NotDependencyProperty {}
|
||||
*
|
||||
* class AComponent {
|
||||
* constructor(@Exclude @NotDependencyProperty aService:AService) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* will create the following dependency:
|
||||
*
|
||||
* ```
|
||||
* new Dependency(Key.get(AService), [new Exclude()])
|
||||
* ```
|
||||
*
|
||||
* The framework can use `new Exclude()` to handle the `aService` dependency
|
||||
* in a specific way.
|
||||
*/
|
||||
@CONST()
|
||||
export class DependencyMetadata {
|
||||
get token() { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker metadata that marks a class as available to `Injector` for creation. Used by tooling
|
||||
* for generating constructor stubs.
|
||||
*
|
||||
* ```
|
||||
* class NeedsService {
|
||||
* constructor(svc:UsefulService) {}
|
||||
* }
|
||||
*
|
||||
* @Injectable
|
||||
* class UsefulService {}
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class InjectableMetadata {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from itself.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Self() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
||||
* var nd = inj.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(Dependency);
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class SelfMetadata {
|
||||
toString(): string { return `@Self()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the dependency resolution should start from the parent injector.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* class Service {}
|
||||
*
|
||||
* class ParentService implements Service {
|
||||
* }
|
||||
*
|
||||
* class ChildService implements Service {
|
||||
* constructor(public @SkipSelf() parentService:Service) {}
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([
|
||||
* bind(Service).toClass(ParentService)
|
||||
* ]);
|
||||
* var child = parent.resolveAndCreateChild([
|
||||
* bind(Service).toClass(ChildSerice)
|
||||
* ]);
|
||||
* var s = child.get(Service);
|
||||
* expect(s).toBeAnInstanceOf(ChildService);
|
||||
* expect(s.parentService).toBeAnInstanceOf(ParentService);
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class SkipSelfMetadata {
|
||||
toString(): string { return `@SkipSelf()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
||||
* closest host.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Host() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([
|
||||
* bind(Dependency).toClass(HostDependency)
|
||||
* ]);
|
||||
* var child = parent.resolveAndCreateChild([]);
|
||||
* var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]);
|
||||
* var nd = grandChild.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(HostDependency);
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class HostMetadata {
|
||||
toString(): string { return `@Host()`; }
|
||||
}
|
10
modules/angular2/src/core/di/opaque_token.ts
Normal file
10
modules/angular2/src/core/di/opaque_token.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
|
||||
@CONST()
|
||||
export class OpaqueToken {
|
||||
_desc: string;
|
||||
|
||||
constructor(desc: string) { this._desc = 'Token(' + desc + ')'; }
|
||||
|
||||
toString(): string { return this._desc; }
|
||||
}
|
4
modules/angular2/src/core/di/type_info.dart
Normal file
4
modules/angular2/src/core/di/type_info.dart
Normal file
@ -0,0 +1,4 @@
|
||||
library angular2.di.type_info;
|
||||
|
||||
// In dart always return empty, as we can get the co
|
||||
argsLength(Type type) => 0;
|
0
modules/angular2/src/core/di/type_info.ts
Normal file
0
modules/angular2/src/core/di/type_info.ts
Normal file
26
modules/angular2/src/core/di/type_literal.dart
Normal file
26
modules/angular2/src/core/di/type_literal.dart
Normal file
@ -0,0 +1,26 @@
|
||||
library angular2.di.type_literal;
|
||||
|
||||
/**
|
||||
* Use type literals as DI keys corresponding to generic types.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* Injector.resolveAndCreate([
|
||||
* bind(new TypeLiteral<List<int>>()).toValue([1, 2, 3])
|
||||
* ]);
|
||||
*
|
||||
* class Foo {
|
||||
* // Delend on `List<int>` normally.
|
||||
* Foo(List<int> list) { ... }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This capability might be added to the language one day. See:
|
||||
*
|
||||
* https://code.google.com/p/dart/issues/detail?id=11923
|
||||
*/
|
||||
class TypeLiteral<T> {
|
||||
const TypeLiteral();
|
||||
Type get type => T;
|
||||
}
|
7
modules/angular2/src/core/di/type_literal.ts
Normal file
7
modules/angular2/src/core/di/type_literal.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Type literals is a Dart-only feature. This is here only so we can x-compile
|
||||
* to multiple languages.
|
||||
*/
|
||||
export class TypeLiteral {
|
||||
get type(): any { throw new Error("Type literals are only supported in Dart"); }
|
||||
}
|
Reference in New Issue
Block a user