platform/packages/container/angel_container/lib/src/container.dart

240 lines
6.3 KiB
Dart
Raw Normal View History

2019-04-17 02:21:51 +00:00
import 'dart:async';
2018-07-10 18:18:10 +00:00
import 'exception.dart';
import 'reflector.dart';
class Container {
final Reflector reflector;
final Map<Type, dynamic> _singletons = {};
2018-08-20 04:40:30 +00:00
final Map<Type, dynamic Function(Container)> _factories = {};
2018-10-22 15:35:35 +00:00
final Map<String, dynamic> _namedSingletons = {};
2021-03-18 00:21:42 +00:00
final Container? _parent;
2018-07-10 18:18:10 +00:00
2018-08-20 04:40:30 +00:00
Container(this.reflector) : _parent = null;
2018-07-10 18:18:10 +00:00
2021-03-18 00:21:42 +00:00
Container._child(Container this._parent) : reflector = _parent.reflector;
2018-07-10 18:18:10 +00:00
2018-08-20 04:40:30 +00:00
bool get isRoot => _parent == null;
2018-08-11 19:07:35 +00:00
2018-08-20 04:40:30 +00:00
/// Creates a child [Container] that can define its own singletons and factories.
///
/// Use this to create children of a global "scope."
Container createChild() {
2019-10-12 13:34:15 +00:00
return Container._child(this);
2018-08-20 04:40:30 +00:00
}
2018-07-10 18:18:10 +00:00
2019-04-10 19:20:02 +00:00
/// Determines if the container has an injection of the given type.
2022-01-23 05:10:50 +00:00
bool has<T>([Type? t]) {
2021-10-04 05:13:56 +00:00
var t2 = T;
2022-01-23 05:10:50 +00:00
if (t != null) {
t2 = t;
} else if (T == dynamic && t == null) {
2021-10-04 05:13:56 +00:00
return false;
}
2018-08-26 22:55:21 +00:00
2021-10-04 05:13:56 +00:00
Container? search = this;
2018-08-26 22:55:21 +00:00
while (search != null) {
2021-10-04 05:13:56 +00:00
if (search._singletons.containsKey(t2)) {
2018-08-26 22:55:21 +00:00
return true;
2021-10-04 05:13:56 +00:00
} else if (search._factories.containsKey(t2)) {
2018-08-26 22:55:21 +00:00
return true;
} else {
search = search._parent;
}
}
return false;
}
2019-04-10 19:20:02 +00:00
/// Determines if the container has a named singleton with the given [name].
bool hasNamed(String name) {
2021-03-18 00:21:42 +00:00
Container? search = this;
2019-04-10 19:20:02 +00:00
while (search != null) {
if (search._namedSingletons.containsKey(name)) {
return true;
} else {
search = search._parent;
}
}
return false;
}
2019-04-17 02:21:51 +00:00
/// Instantiates an instance of [T], asynchronously.
///
/// It is similar to [make], but resolves an injection of either
/// `Future<T>` or `T`.
2021-10-04 05:13:56 +00:00
Future<T> makeAsync<T>([Type? type]) {
var t2 = T;
if (type != null) {
t2 = type;
}
2021-03-18 00:21:42 +00:00
Type? futureType; //.Future<T>.value(null).runtimeType;
2019-04-17 19:31:58 +00:00
if (T == dynamic) {
try {
2021-10-04 05:13:56 +00:00
futureType = reflector.reflectFutureOf(t2).reflectedType;
2019-04-17 19:31:58 +00:00
} on UnsupportedError {
// Ignore this.
}
}
2021-10-04 05:13:56 +00:00
if (has<T>(t2)) {
return Future<T>.value(make(t2));
2019-04-17 19:31:58 +00:00
} else if (has<Future<T>>()) {
2019-04-17 02:21:51 +00:00
return make<Future<T>>();
2019-04-17 19:31:58 +00:00
} else if (futureType != null) {
return make(futureType);
2019-04-17 02:21:51 +00:00
} else {
2019-10-12 13:34:15 +00:00
throw ReflectionException(
2021-10-04 05:13:56 +00:00
'No injection for Future<$t2> or $t2 was found.');
2019-04-17 02:21:51 +00:00
}
}
2018-08-20 04:40:30 +00:00
/// Instantiates an instance of [T].
///
/// In contexts where a static generic type cannot be used, use
/// the [type] argument, instead of [T].
2021-10-04 05:13:56 +00:00
T make<T>([Type? type]) {
2022-12-27 22:59:40 +00:00
Type t2 = T;
2021-10-04 05:13:56 +00:00
if (type != null) {
t2 = type;
}
2018-07-10 18:18:10 +00:00
2021-03-18 00:21:42 +00:00
Container? search = this;
2018-07-10 18:18:10 +00:00
2018-08-20 04:40:30 +00:00
while (search != null) {
2021-10-04 05:13:56 +00:00
if (search._singletons.containsKey(t2)) {
2018-08-26 22:55:21 +00:00
// Find a singleton, if any.
2021-10-04 05:13:56 +00:00
return search._singletons[t2] as T;
} else if (search._factories.containsKey(t2)) {
2018-08-26 22:55:21 +00:00
// Find a factory, if any.
2021-10-04 05:13:56 +00:00
return search._factories[t2]!(this) as T;
2018-08-20 04:40:30 +00:00
} else {
search = search._parent;
}
}
2021-10-04 05:13:56 +00:00
var reflectedType = reflector.reflectType(t2);
2018-08-20 04:40:30 +00:00
var positional = [];
2022-12-27 22:59:40 +00:00
var named = <String, Object>{};
2018-08-20 04:40:30 +00:00
if (reflectedType is ReflectedClass) {
bool isDefault(String name) {
return name.isEmpty || name == reflectedType.name;
2018-07-10 18:18:10 +00:00
}
2018-08-20 04:40:30 +00:00
var constructor = reflectedType.constructors.firstWhere(
(c) => isDefault(c.name),
2021-03-18 00:21:42 +00:00
orElse: (() => throw ReflectionException(
2021-05-18 13:33:20 +00:00
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.')));
2018-08-20 04:40:30 +00:00
for (var param in constructor.parameters) {
var value = make(param.type.reflectedType);
if (param.isNamed) {
named[param.name] = value;
} else {
positional.add(value);
}
}
return reflectedType.newInstance(
isDefault(constructor.name) ? '' : constructor.name,
positional,
2021-10-04 05:13:56 +00:00
named, []).reflectee as T;
2018-08-20 04:40:30 +00:00
} else {
2019-10-12 13:34:15 +00:00
throw ReflectionException(
2021-10-04 05:13:56 +00:00
'$t2 is not a class, and therefore cannot be instantiated.');
2018-07-10 18:18:10 +00:00
}
}
2018-10-22 15:21:58 +00:00
/// Shorthand for registering a factory that injects a singleton when it runs.
2018-10-22 15:35:35 +00:00
///
2018-10-22 15:21:58 +00:00
/// In many cases, you might prefer this to [registerFactory].
2019-10-12 13:47:02 +00:00
///
2019-10-12 13:46:45 +00:00
/// Returns [f].
T Function(Container) registerLazySingleton<T>(T Function(Container) f,
2021-03-18 00:21:42 +00:00
{Type? as}) {
2019-10-12 13:46:45 +00:00
return registerFactory<T>(
2018-10-22 15:21:58 +00:00
(container) {
var r = f(container);
container.registerSingleton<T>(r, as: as);
return r;
},
as: as,
);
}
2019-10-12 13:46:45 +00:00
/// Registers a factory. Any attempt to resolve the
/// type within *this* container will return the result of [f].
2019-10-12 13:47:02 +00:00
///
2019-10-12 13:46:45 +00:00
/// Returns [f].
2021-05-18 13:33:20 +00:00
T Function(Container) registerFactory<T>(T Function(Container) f,
{Type? as}) {
2021-10-04 05:13:56 +00:00
Type t2 = T;
if (as != null) {
t2 = as;
}
2018-08-20 04:40:30 +00:00
2021-10-04 05:13:56 +00:00
if (_factories.containsKey(t2)) {
throw StateError('This container already has a factory for $t2.');
2018-08-20 04:40:30 +00:00
}
2021-10-04 05:13:56 +00:00
_factories[t2] = f;
2019-10-12 13:46:45 +00:00
return f;
2018-08-20 04:40:30 +00:00
}
2019-10-12 13:46:45 +00:00
/// Registers a singleton. Any attempt to resolve the
/// type within *this* container will return [object].
2019-10-12 13:47:02 +00:00
///
2019-10-12 13:46:45 +00:00
/// Returns [object].
2021-03-18 00:21:42 +00:00
T registerSingleton<T>(T object, {Type? as}) {
2021-10-04 05:13:56 +00:00
Type t2 = T;
if (as != null) {
t2 = as;
} else if (T == dynamic) {
t2 = as ?? object.runtimeType;
}
//as ??= T == dynamic ? as : T;
2018-08-20 04:40:30 +00:00
2021-10-04 05:13:56 +00:00
if (_singletons.containsKey(t2)) {
throw StateError('This container already has a singleton for $t2.');
2018-07-10 18:18:10 +00:00
}
2021-10-04 05:13:56 +00:00
_singletons[t2] = object;
2019-10-12 13:46:45 +00:00
return object;
2018-07-10 18:18:10 +00:00
}
2018-10-22 15:35:35 +00:00
/// Finds a named singleton.
2018-10-22 15:35:45 +00:00
///
2018-10-22 15:35:35 +00:00
/// In general, prefer using [registerSingleton] and [registerFactory].
2018-10-22 15:35:45 +00:00
///
2018-10-22 15:35:35 +00:00
/// [findByName] is best reserved for internal logic that end users of code should
/// not see.
2021-10-04 05:13:56 +00:00
T findByName<T>(String name) {
2018-10-22 15:35:35 +00:00
if (_namedSingletons.containsKey(name)) {
2021-10-04 05:13:56 +00:00
return _namedSingletons[name] as T;
2018-10-22 15:35:35 +00:00
} else if (_parent != null) {
2021-03-18 00:21:42 +00:00
return _parent!.findByName<T>(name);
2018-10-22 15:35:35 +00:00
} else {
2019-10-12 13:34:15 +00:00
throw StateError(
2018-10-22 15:35:35 +00:00
'This container does not have a singleton named "$name".');
}
}
/// Registers a *named* singleton.
///
/// Note that this is not related to type-based injections, and exists as a mechanism
/// to enable injecting multiple instances of a type within the same container hierarchy.
2019-10-12 13:46:45 +00:00
T registerNamedSingleton<T>(String name, T object) {
2018-10-22 15:35:35 +00:00
if (_namedSingletons.containsKey(name)) {
2019-10-12 13:34:15 +00:00
throw StateError('This container already has a singleton named "$name".');
2018-10-22 15:35:35 +00:00
}
_namedSingletons[name] = object;
2019-10-12 13:46:45 +00:00
return object;
2018-10-22 15:35:35 +00:00
}
2018-07-10 18:18:10 +00:00
}