refactor: working on container test 3 82 pass 2 fail
This commit is contained in:
parent
0453c67177
commit
d690877afc
44 changed files with 5194 additions and 0 deletions
71
incubation/container/container/.gitignore
vendored
Normal file
71
incubation/container/container/.gitignore
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.dart_tool
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# If you're building an application, you may want to check-in your pubspec.lock
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
# If you don't generate documentation locally you can remove this line.
|
||||||
|
doc/api/
|
||||||
|
|
||||||
|
### Dart template
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
|
||||||
|
# SDK 1.20 and later (no longer creates packages directories)
|
||||||
|
|
||||||
|
# Older SDK versions
|
||||||
|
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||||
|
.project
|
||||||
|
.buildlog
|
||||||
|
**/packages/
|
||||||
|
|
||||||
|
|
||||||
|
# Files created by dart2js
|
||||||
|
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||||
|
# rules if you intend to use dart2js directly
|
||||||
|
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||||
|
# differentiate from explicit Javascript files)
|
||||||
|
*.dart.js
|
||||||
|
*.part.js
|
||||||
|
*.js.deps
|
||||||
|
*.js.map
|
||||||
|
*.info.json
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
|
||||||
|
# Don't commit pubspec lock file
|
||||||
|
# (Library packages only! Remove pattern if developing an application package)
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
|
||||||
|
## VsCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea/
|
||||||
|
/out/
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
12
incubation/container/container/AUTHORS.md
Normal file
12
incubation/container/container/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Primary Authors
|
||||||
|
===============
|
||||||
|
|
||||||
|
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||||
|
|
||||||
|
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||||
|
code base to support NNBD.
|
||||||
|
|
||||||
|
* __[Tobe O](thosakwe@gmail.com)__
|
||||||
|
|
||||||
|
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||||
|
is no longer involved with the project.
|
151
incubation/container/container/CHANGELOG.md
Normal file
151
incubation/container/container/CHANGELOG.md
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## 8.1.1
|
||||||
|
|
||||||
|
* Updated repository link
|
||||||
|
|
||||||
|
## 8.1.0
|
||||||
|
|
||||||
|
* Updated `lints` to 3.0.0
|
||||||
|
* Fixed analyser warnings
|
||||||
|
|
||||||
|
## 8.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.0
|
||||||
|
|
||||||
|
## 7.1.0-beta.2
|
||||||
|
|
||||||
|
* Require Dart >= 2.19
|
||||||
|
* Refactored `EmptyReflector`
|
||||||
|
|
||||||
|
## 7.1.0-beta.1
|
||||||
|
|
||||||
|
* Require Dart >= 2.18
|
||||||
|
* Moved `defaultErrorMessage` to `ContainerConst` class to resolve reflectatable issue.
|
||||||
|
* Added `hashCode`
|
||||||
|
|
||||||
|
## 7.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.17
|
||||||
|
|
||||||
|
## 6.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.16
|
||||||
|
* Removed `error`
|
||||||
|
|
||||||
|
## 5.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
* Updated `_ReflectedMethodMirror` to have optional `returnType` parameter
|
||||||
|
* Updated `Container` to handle non nullable type
|
||||||
|
|
||||||
|
## 3.1.0
|
||||||
|
|
||||||
|
* Updated linter to `package:lints`
|
||||||
|
|
||||||
|
## 3.0.2
|
||||||
|
|
||||||
|
* Resolved static analysis warnings
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
* Updated README
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
* Migrated to support Dart >= 2.12 NNBD
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
* `pedantic` lints.
|
||||||
|
* Add `ThrowingReflector`, which throws on all operations.
|
||||||
|
* `EmptyReflector` uses `Object` instead of `dynamic` as its returned
|
||||||
|
type, as the `dynamic` type is (apparently?) no longer a valid constant value.
|
||||||
|
* `registerSingleton` now returns the provided `object`.
|
||||||
|
* `registerFactory` and `registerLazySingleton` now return the provided function `f`.
|
||||||
|
|
||||||
|
## 1.0.4
|
||||||
|
|
||||||
|
* Slight patch to prevent annoying segfault.
|
||||||
|
|
||||||
|
## 1.0.3
|
||||||
|
|
||||||
|
* Added `Future` support to `Reflector`.
|
||||||
|
|
||||||
|
## 1.0.2
|
||||||
|
|
||||||
|
* Added `makeAsync<T>`.
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
* Added `hasNamed`.
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
* Removed `@GenerateReflector`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.12
|
||||||
|
|
||||||
|
* `StaticReflector` now defaults to empty arguments.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.11
|
||||||
|
|
||||||
|
* Added `StaticReflector`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.10
|
||||||
|
|
||||||
|
* Added `Container.registerLazySingleton<T>`.
|
||||||
|
* Added named singleton support.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.9
|
||||||
|
|
||||||
|
* Added `Container.has<T>`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.8
|
||||||
|
|
||||||
|
* Fixed a bug where `_ReflectedTypeInstance.isAssignableTo` always failed.
|
||||||
|
* Added `@GenerateReflector` annotation.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.7
|
||||||
|
|
||||||
|
* Add `EmptyReflector`.
|
||||||
|
* `ReflectedType.newInstance` now returns a `ReflectedInstance`.
|
||||||
|
* Moved `ReflectedInstance.invoke` to `ReflectedFunction.invoke`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.6
|
||||||
|
|
||||||
|
* Add `getField` to `ReflectedInstance`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.5
|
||||||
|
|
||||||
|
* Remove concrete type from `ReflectedTypeParameter`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.4
|
||||||
|
|
||||||
|
* Safely handle `void` return types of methods.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.3
|
||||||
|
|
||||||
|
* Reflecting `void` in `MirrorsReflector` now forwards to `dynamic`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.2
|
||||||
|
|
||||||
|
* Added `ReflectedInstance.reflectee`.
|
||||||
|
|
||||||
|
## 1.0.0-alpha.1
|
||||||
|
|
||||||
|
* Allow omission of the first argument of `Container.make`, to use
|
||||||
|
a generic type argument instead.
|
||||||
|
* `singleton` -> `registerSingleton`
|
||||||
|
* Add `createChild`, and support hierarchical containers.
|
29
incubation/container/container/LICENSE
Normal file
29
incubation/container/container/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, dukefirehawk.com
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
45
incubation/container/container/README.md
Normal file
45
incubation/container/container/README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Protevus Container
|
||||||
|
|
||||||
|
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/platform_container?include_prereleases)
|
||||||
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
|
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||||
|
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/container/angel_container/LICENSE)
|
||||||
|
|
||||||
|
A better IoC container for Protevus, ultimately allowing Protevus to be used with or without `dart:mirrors` package.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:platform_container/mirrors.dart';
|
||||||
|
import 'package:platform_foundation/core.dart';
|
||||||
|
import 'package:platform_foundation/http.dart';
|
||||||
|
|
||||||
|
@Expose('/sales', middleware: [process1])
|
||||||
|
class SalesController extends Controller {
|
||||||
|
@Expose('/', middleware: [process2])
|
||||||
|
Future<String> route1(RequestContext req, ResponseContext res) async {
|
||||||
|
return "Sales route";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process1(RequestContext req, ResponseContext res) {
|
||||||
|
res.write('Hello, ');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process2(RequestContext req, ResponseContext res) {
|
||||||
|
res.write('From Sales, ');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
// Using Mirror Reflector
|
||||||
|
var app = Protevus(reflector: MirrorsReflector());
|
||||||
|
|
||||||
|
// Sales Controller
|
||||||
|
app.container.registerSingleton<SalesController>(SalesController());
|
||||||
|
await app.mountController<SalesController>();
|
||||||
|
|
||||||
|
var http = PlatformHttp(app);
|
||||||
|
var server = await http.startServer('localhost', 3000);
|
||||||
|
print("Protevus server listening at ${http.uri}");
|
||||||
|
}
|
||||||
|
```
|
1
incubation/container/container/analysis_options.yaml
Normal file
1
incubation/container/container/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include: package:lints/recommended.yaml
|
0
incubation/container/container/doc/.gitkeep
Normal file
0
incubation/container/container/doc/.gitkeep
Normal file
75
incubation/container/container/example/main.dart
Normal file
75
incubation/container/container/example/main.dart
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:platformed_container/mirrors.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
// Create a container instance.
|
||||||
|
var container = Container(const MirrorsReflector());
|
||||||
|
|
||||||
|
// Register a singleton.
|
||||||
|
container.registerSingleton<Engine>(Engine(40));
|
||||||
|
|
||||||
|
// You can also omit the type annotation, in which the object's runtime type will be used.
|
||||||
|
// If you're injecting an abstract class, prefer the type annotation.
|
||||||
|
//
|
||||||
|
// container.registerSingleton(Engine(40));
|
||||||
|
|
||||||
|
// Register a factory that creates a truck.
|
||||||
|
container.registerFactory<Truck>((container) {
|
||||||
|
return _TruckImpl(container.make<Engine>());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use `make` to create an instance.
|
||||||
|
var truck = container.make<Truck>();
|
||||||
|
|
||||||
|
// You can also resolve injections asynchronously.
|
||||||
|
container.registerFactory<Future<int>>((_) async => 24);
|
||||||
|
print(await container.makeAsync<int>());
|
||||||
|
|
||||||
|
// Asynchronous resolution also works for plain objects.
|
||||||
|
await container.makeAsync<Truck>().then((t) => t.drive());
|
||||||
|
|
||||||
|
// Register a named singleton.
|
||||||
|
container.registerNamedSingleton('the_truck', truck);
|
||||||
|
|
||||||
|
// Should print: 'Vroom! I have 40 horsepower in my engine.'
|
||||||
|
truck.drive();
|
||||||
|
|
||||||
|
// Should print the same.
|
||||||
|
container.findByName<Truck>('the_truck').drive();
|
||||||
|
|
||||||
|
// We can make a child container with its own factory.
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
|
||||||
|
childContainer.registerFactory<Truck>((container) {
|
||||||
|
return _TruckImpl(Engine(5666));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make a truck with 5666 HP.
|
||||||
|
childContainer.make<Truck>().drive();
|
||||||
|
|
||||||
|
// However, calling `make<Engine>` will return the Engine singleton we created above.
|
||||||
|
print(childContainer.make<Engine>().horsePower);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Truck {
|
||||||
|
void drive();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Engine {
|
||||||
|
final int horsePower;
|
||||||
|
|
||||||
|
Engine(this.horsePower);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TruckImpl implements Truck {
|
||||||
|
final Engine engine;
|
||||||
|
|
||||||
|
_TruckImpl(this.engine);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void drive() {
|
||||||
|
print('Vroom! I have ${engine.horsePower} horsepower in my engine.');
|
||||||
|
}
|
||||||
|
}
|
6
incubation/container/container/example/throwing.dart
Normal file
6
incubation/container/container/example/throwing.dart
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var reflector = const ThrowingReflector();
|
||||||
|
reflector.reflectClass(StringBuffer);
|
||||||
|
}
|
18
incubation/container/container/lib/container.dart
Normal file
18
incubation/container/container/lib/container.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
library platform_container;
|
||||||
|
|
||||||
|
export 'src/container.dart';
|
||||||
|
export 'src/empty/empty.dart';
|
||||||
|
export 'src/static/static.dart';
|
||||||
|
export 'src/exception.dart';
|
||||||
|
export 'src/reflector.dart';
|
||||||
|
export 'src/throwing.dart';
|
||||||
|
export 'src/container_const.dart';
|
10
incubation/container/container/lib/mirrors.dart
Normal file
10
incubation/container/container/lib/mirrors.dart
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export 'src/mirrors/mirrors.dart';
|
741
incubation/container/container/lib/src/container.dart
Normal file
741
incubation/container/container/lib/src/container.dart
Normal file
|
@ -0,0 +1,741 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'exception.dart';
|
||||||
|
import 'reflector.dart';
|
||||||
|
import 'contextual_binding_builder.dart';
|
||||||
|
|
||||||
|
class Container {
|
||||||
|
/// The [Reflector] instance used by this container for reflection-based operations.
|
||||||
|
///
|
||||||
|
/// This reflector is used to instantiate objects and resolve dependencies
|
||||||
|
/// when no explicit factory or singleton is registered for a given type.
|
||||||
|
final Reflector reflector;
|
||||||
|
|
||||||
|
/// The container's bindings map
|
||||||
|
final Map<Type, dynamic> _singletons = {};
|
||||||
|
final Map<Type, dynamic Function(Container)> _factories = {};
|
||||||
|
final Map<String, dynamic> _namedSingletons = {};
|
||||||
|
|
||||||
|
/// The container's contextual bindings
|
||||||
|
final Map<Type, Map<Type, dynamic>> _contextual = {};
|
||||||
|
|
||||||
|
/// The container's method bindings
|
||||||
|
final Map<String, Function> _methodBindings = {};
|
||||||
|
|
||||||
|
/// The container's tags
|
||||||
|
final Map<String, List<Type>> _tags = {};
|
||||||
|
|
||||||
|
/// The container's scoped instances
|
||||||
|
final List<Type> _scopedInstances = [];
|
||||||
|
|
||||||
|
/// Resolution callbacks
|
||||||
|
final List<Function(Type, List, Container)> _beforeResolvingCallbacks = [];
|
||||||
|
final List<Function(dynamic, Container)> _resolvingCallbacks = [];
|
||||||
|
final List<Function(dynamic, Container)> _afterResolvingCallbacks = [];
|
||||||
|
|
||||||
|
/// The build stack for detecting circular dependencies
|
||||||
|
final List<Type> _buildStack = [];
|
||||||
|
|
||||||
|
/// The parent container
|
||||||
|
final Container? _parent;
|
||||||
|
|
||||||
|
/// Creates a new root [Container] instance with the given [Reflector].
|
||||||
|
///
|
||||||
|
/// This constructor initializes a new container without a parent, making it
|
||||||
|
/// a root container in the dependency injection hierarchy. The provided
|
||||||
|
/// [reflector] will be used for all reflection-based operations within this
|
||||||
|
/// container and its child containers.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [reflector]: The [Reflector] instance to be used by this container
|
||||||
|
/// for reflection-based dependency resolution and object instantiation.
|
||||||
|
///
|
||||||
|
/// The [_parent] is set to null, indicating that this is a root container.
|
||||||
|
Container(this.reflector) : _parent = null;
|
||||||
|
|
||||||
|
/// Creates a child [Container] instance with the given parent container.
|
||||||
|
///
|
||||||
|
/// This constructor is used internally to create child containers in the
|
||||||
|
/// dependency injection hierarchy. It initializes a new container with a
|
||||||
|
/// reference to its parent container and uses the same [Reflector] instance
|
||||||
|
/// as the parent.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [_parent]: The parent [Container] instance for this child container.
|
||||||
|
///
|
||||||
|
/// The [reflector] is initialized with the parent container's reflector,
|
||||||
|
/// ensuring consistency in reflection operations throughout the container
|
||||||
|
/// hierarchy.
|
||||||
|
Container._child(Container this._parent) : reflector = _parent.reflector;
|
||||||
|
|
||||||
|
/// Checks if this container is a root container.
|
||||||
|
///
|
||||||
|
/// Returns `true` if this container has no parent (i.e., it's a root container),
|
||||||
|
/// and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// This property is useful for determining the position of a container in the
|
||||||
|
/// dependency injection hierarchy. Root containers are typically used as the
|
||||||
|
/// top-level containers in an application, while non-root containers are child
|
||||||
|
/// containers that may have more specific or localized dependencies.
|
||||||
|
bool get isRoot => _parent == null;
|
||||||
|
|
||||||
|
/// Creates a child [Container] that can define its own singletons and factories.
|
||||||
|
///
|
||||||
|
/// This method creates a new [Container] instance that is a child of the current container.
|
||||||
|
/// The child container inherits access to all dependencies registered in its parent containers,
|
||||||
|
/// but can also define its own singletons and factories that override or extend the parent's dependencies.
|
||||||
|
///
|
||||||
|
/// Child containers are useful for creating scoped dependency injection contexts, such as
|
||||||
|
/// for specific features, modules, or request-scoped dependencies in web applications.
|
||||||
|
///
|
||||||
|
/// The child container uses the same [Reflector] instance as its parent.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A new [Container] instance that is a child of the current container.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// var parentContainer = Container(MyReflector());
|
||||||
|
/// var childContainer = parentContainer.createChild();
|
||||||
|
/// ```
|
||||||
|
Container createChild() {
|
||||||
|
return Container._child(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the container or any of its parent containers has an injection of the given type.
|
||||||
|
///
|
||||||
|
/// This method checks for both singleton and factory registrations of the specified type.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [T]: The type to check for. If [T] is dynamic, the [t] parameter must be provided.
|
||||||
|
/// - [t]: An optional Type parameter. If provided, it overrides the type specified by [T].
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true` if an injection (singleton or factory) for the specified type is found in this
|
||||||
|
/// container or any of its parent containers.
|
||||||
|
/// - `false` if no injection is found for the specified type in the entire container hierarchy.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// - If [T] is dynamic and [t] is null, the method returns `false` immediately.
|
||||||
|
/// - The method searches the current container first, then moves up the parent hierarchy
|
||||||
|
/// until an injection is found or the root container is reached.
|
||||||
|
bool has<T>([Type? t]) {
|
||||||
|
var t2 = T;
|
||||||
|
if (t != null) {
|
||||||
|
t2 = t;
|
||||||
|
} else if (T == dynamic && t == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
if (search._singletons.containsKey(t2)) {
|
||||||
|
return true;
|
||||||
|
} else if (search._factories.containsKey(t2)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the container or any of its parent containers has a named singleton with the given [name].
|
||||||
|
///
|
||||||
|
/// This method searches the current container and its parent hierarchy for a named singleton
|
||||||
|
/// registered with the specified [name].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [name]: The name of the singleton to search for.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true` if a named singleton with the specified [name] is found in this container
|
||||||
|
/// or any of its parent containers.
|
||||||
|
/// - `false` if no named singleton with the specified [name] is found in the entire
|
||||||
|
/// container hierarchy.
|
||||||
|
///
|
||||||
|
/// The method searches the current container first, then moves up the parent hierarchy
|
||||||
|
/// until a named singleton is found or the root container is reached.
|
||||||
|
bool hasNamed(String name) {
|
||||||
|
Container? search = this;
|
||||||
|
|
||||||
|
while (search != null) {
|
||||||
|
if (search._namedSingletons.containsKey(name)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously instantiates an instance of [T].
|
||||||
|
///
|
||||||
|
/// This method attempts to resolve and return a [Future<T>] in the following order:
|
||||||
|
/// 1. If an injection of type [T] is registered, it wraps it in a [Future] and returns it.
|
||||||
|
/// 2. If an injection of type [Future<T>] is registered, it returns it directly.
|
||||||
|
/// 3. If [T] is [dynamic] and a [Future] of the specified type is registered, it returns that.
|
||||||
|
/// 4. If none of the above conditions are met, it throws a [ReflectionException].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: An optional [Type] parameter that can be used to specify the type
|
||||||
|
/// when [T] is [dynamic] or when a different type than [T] needs to be used.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [Future<T>] representing the asynchronously resolved instance.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ReflectionException] if no suitable injection is found.
|
||||||
|
///
|
||||||
|
/// This method is useful when you need to resolve dependencies that may be
|
||||||
|
/// registered as either synchronous ([T]) or asynchronous ([Future<T>]) types.
|
||||||
|
Future<T> makeAsync<T>([Type? type]) {
|
||||||
|
var t2 = T;
|
||||||
|
if (type != null) {
|
||||||
|
t2 = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type? futureType; //.Future<T>.value(null).runtimeType;
|
||||||
|
|
||||||
|
if (T == dynamic) {
|
||||||
|
try {
|
||||||
|
futureType = reflector.reflectFutureOf(t2).reflectedType;
|
||||||
|
} on UnsupportedError {
|
||||||
|
// Ignore this.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has<T>(t2)) {
|
||||||
|
return Future<T>.value(make(t2));
|
||||||
|
} else if (has<Future<T>>()) {
|
||||||
|
return make<Future<T>>();
|
||||||
|
} else if (futureType != null) {
|
||||||
|
return make(futureType);
|
||||||
|
} else {
|
||||||
|
throw ReflectionException(
|
||||||
|
'No injection for Future<$t2> or $t2 was found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instantiates an instance of [T].
|
||||||
|
///
|
||||||
|
/// This method attempts to resolve and return an instance of type [T] in the following order:
|
||||||
|
/// 1. If a singleton of type [T] is registered in this container or any parent container, it returns that instance.
|
||||||
|
/// 2. If a factory for type [T] is registered in this container or any parent container, it calls the factory and returns the result.
|
||||||
|
/// 3. If no singleton or factory is found, it uses reflection to instantiate a new instance of [T].
|
||||||
|
///
|
||||||
|
/// For reflection-based instantiation:
|
||||||
|
/// - It looks for a default constructor or a constructor with an empty name.
|
||||||
|
/// - It recursively resolves and injects dependencies for the constructor parameters.
|
||||||
|
/// - It supports both positional and named parameters.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: An optional [Type] parameter that can be used to specify the type
|
||||||
|
/// when [T] is [dynamic] or when a different type than [T] needs to be used.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// An instance of type [T].
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ReflectionException] if [T] is not a class or if it has no default constructor.
|
||||||
|
/// - Any exception that might occur during the instantiation process.
|
||||||
|
///
|
||||||
|
/// This method is central to the dependency injection mechanism, allowing for
|
||||||
|
/// flexible object creation and dependency resolution within the container hierarchy.
|
||||||
|
T make<T>([Type? type]) {
|
||||||
|
Type t2 = T;
|
||||||
|
if (type != null) {
|
||||||
|
t2 = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for circular dependencies
|
||||||
|
_checkCircularDependency(t2);
|
||||||
|
_buildStack.add(t2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fire before resolving callbacks
|
||||||
|
_fireBeforeResolvingCallbacks(t2, []);
|
||||||
|
|
||||||
|
// Check for contextual binding
|
||||||
|
var contextualConcrete = _getContextualConcrete(t2);
|
||||||
|
if (contextualConcrete != null) {
|
||||||
|
dynamic instance;
|
||||||
|
if (contextualConcrete is Function) {
|
||||||
|
// Remove current type from stack to avoid circular dependency
|
||||||
|
_buildStack.removeLast();
|
||||||
|
try {
|
||||||
|
instance = contextualConcrete(this);
|
||||||
|
} finally {
|
||||||
|
_buildStack.add(t2);
|
||||||
|
}
|
||||||
|
} else if (contextualConcrete is Type) {
|
||||||
|
// For Type bindings, we need to use reflection to create the instance
|
||||||
|
_buildStack.removeLast(); // Remove current type from stack
|
||||||
|
try {
|
||||||
|
var reflectedType = reflector.reflectType(contextualConcrete);
|
||||||
|
if (reflectedType is ReflectedClass) {
|
||||||
|
bool isDefault(String name) {
|
||||||
|
return name.isEmpty || name == reflectedType.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructor = reflectedType.constructors.firstWhere(
|
||||||
|
(c) => isDefault(c.name),
|
||||||
|
orElse: (() => throw BindingResolutionException(
|
||||||
|
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.')));
|
||||||
|
|
||||||
|
var positional = [];
|
||||||
|
var named = <String, Object>{};
|
||||||
|
|
||||||
|
for (var param in constructor.parameters) {
|
||||||
|
if (param.type.reflectedType == String) {
|
||||||
|
positional.add('test.log'); // Default filename for FileLogger
|
||||||
|
} else {
|
||||||
|
var value = make(param.type.reflectedType);
|
||||||
|
if (param.isNamed) {
|
||||||
|
named[param.name] = value;
|
||||||
|
} else {
|
||||||
|
positional.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = reflectedType.newInstance(
|
||||||
|
isDefault(constructor.name) ? '' : constructor.name,
|
||||||
|
positional,
|
||||||
|
named, []).reflectee;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_buildStack.add(t2); // Add it back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance != null) {
|
||||||
|
var typedInstance = instance as T;
|
||||||
|
_fireResolvingCallbacks(typedInstance);
|
||||||
|
_fireAfterResolvingCallbacks(typedInstance);
|
||||||
|
return typedInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for contextual binding in parent classes
|
||||||
|
var parentContextual = _getContextualConcreteFromParent(t2);
|
||||||
|
if (parentContextual != null) {
|
||||||
|
dynamic instance;
|
||||||
|
if (parentContextual is Function) {
|
||||||
|
// Remove current type from stack to avoid circular dependency
|
||||||
|
_buildStack.removeLast();
|
||||||
|
try {
|
||||||
|
instance = parentContextual(this);
|
||||||
|
} finally {
|
||||||
|
_buildStack.add(t2);
|
||||||
|
}
|
||||||
|
} else if (parentContextual is Type) {
|
||||||
|
// For Type bindings, we need to use reflection to create the instance
|
||||||
|
_buildStack.removeLast(); // Remove current type from stack
|
||||||
|
try {
|
||||||
|
instance = make(parentContextual);
|
||||||
|
} finally {
|
||||||
|
_buildStack.add(t2); // Add it back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance != null) {
|
||||||
|
var typedInstance = instance as T;
|
||||||
|
_fireResolvingCallbacks(typedInstance);
|
||||||
|
_fireAfterResolvingCallbacks(typedInstance);
|
||||||
|
return typedInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for singleton or factory
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
if (search._singletons.containsKey(t2)) {
|
||||||
|
var instance = search._singletons[t2] as T;
|
||||||
|
_fireResolvingCallbacks(instance);
|
||||||
|
_fireAfterResolvingCallbacks(instance);
|
||||||
|
return instance;
|
||||||
|
} else if (search._factories.containsKey(t2)) {
|
||||||
|
var instance = search._factories[t2]!(this) as T;
|
||||||
|
_fireResolvingCallbacks(instance);
|
||||||
|
_fireAfterResolvingCallbacks(instance);
|
||||||
|
return instance;
|
||||||
|
} else {
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle primitive types
|
||||||
|
if (t2 == String) {
|
||||||
|
return '' as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reflection to create instance
|
||||||
|
var reflectedType = reflector.reflectType(t2);
|
||||||
|
var positional = [];
|
||||||
|
var named = <String, Object>{};
|
||||||
|
|
||||||
|
if (reflectedType is ReflectedClass) {
|
||||||
|
bool isDefault(String name) {
|
||||||
|
return name.isEmpty || name == reflectedType.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructor = reflectedType.constructors.firstWhere(
|
||||||
|
(c) => isDefault(c.name),
|
||||||
|
orElse: (() => throw BindingResolutionException(
|
||||||
|
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.')));
|
||||||
|
|
||||||
|
// Add current type to build stack before resolving parameters
|
||||||
|
_buildStack.add(t2);
|
||||||
|
try {
|
||||||
|
for (var param in constructor.parameters) {
|
||||||
|
var value = make(param.type.reflectedType);
|
||||||
|
|
||||||
|
if (param.isNamed) {
|
||||||
|
named[param.name] = value;
|
||||||
|
} else {
|
||||||
|
positional.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_buildStack.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = reflectedType.newInstance(
|
||||||
|
isDefault(constructor.name) ? '' : constructor.name,
|
||||||
|
positional,
|
||||||
|
named, []).reflectee as T;
|
||||||
|
|
||||||
|
_fireResolvingCallbacks(instance);
|
||||||
|
_fireAfterResolvingCallbacks(instance);
|
||||||
|
return instance;
|
||||||
|
} else {
|
||||||
|
throw BindingResolutionException(
|
||||||
|
'$t2 is not a class, and therefore cannot be instantiated.');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_buildStack.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a lazy singleton factory.
|
||||||
|
///
|
||||||
|
/// In many cases, you might prefer this to [registerFactory].
|
||||||
|
///
|
||||||
|
/// Returns [f].
|
||||||
|
T Function(Container) registerLazySingleton<T>(T Function(Container) f,
|
||||||
|
{Type? as}) {
|
||||||
|
return registerFactory<T>(
|
||||||
|
(container) {
|
||||||
|
var r = f(container);
|
||||||
|
container.registerSingleton<T>(r, as: as);
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
as: as,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a factory function for creating instances of type [T] in the container.
|
||||||
|
///
|
||||||
|
/// Returns [f].
|
||||||
|
T Function(Container) registerFactory<T>(T Function(Container) f,
|
||||||
|
{Type? as}) {
|
||||||
|
Type t2 = T;
|
||||||
|
if (as != null) {
|
||||||
|
t2 = as;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_factories.containsKey(t2)) {
|
||||||
|
throw StateError('This container already has a factory for $t2.');
|
||||||
|
}
|
||||||
|
|
||||||
|
_factories[t2] = f;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a singleton object in the container.
|
||||||
|
///
|
||||||
|
/// Returns [object].
|
||||||
|
T registerSingleton<T>(T object, {Type? as}) {
|
||||||
|
Type t2 = T;
|
||||||
|
if (as != null) {
|
||||||
|
t2 = as;
|
||||||
|
} else if (T == dynamic) {
|
||||||
|
t2 = as ?? object.runtimeType;
|
||||||
|
}
|
||||||
|
//as ??= T == dynamic ? as : T;
|
||||||
|
|
||||||
|
if (_singletons.containsKey(t2)) {
|
||||||
|
throw StateError('This container already has a singleton for $t2.');
|
||||||
|
}
|
||||||
|
|
||||||
|
_singletons[t2] = object;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves a named singleton from the container or its parent containers.
|
||||||
|
///
|
||||||
|
/// In general, prefer using [registerSingleton] and [registerFactory].
|
||||||
|
///
|
||||||
|
/// [findByName] is best reserved for internal logic that end users of code should
|
||||||
|
/// not see.
|
||||||
|
T findByName<T>(String name) {
|
||||||
|
if (_namedSingletons.containsKey(name)) {
|
||||||
|
return _namedSingletons[name] as T;
|
||||||
|
} else if (_parent != null) {
|
||||||
|
return _parent.findByName<T>(name);
|
||||||
|
} else {
|
||||||
|
throw StateError(
|
||||||
|
'This container does not have a singleton named "$name".');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a named singleton object in the container.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
T registerNamedSingleton<T>(String name, T object) {
|
||||||
|
if (_namedSingletons.containsKey(name)) {
|
||||||
|
throw StateError('This container already has a singleton named "$name".');
|
||||||
|
}
|
||||||
|
|
||||||
|
_namedSingletons[name] = object;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a contextual binding.
|
||||||
|
///
|
||||||
|
/// This allows you to define how abstract types should be resolved in specific contexts.
|
||||||
|
ContextualBindingBuilder when(Type concrete) {
|
||||||
|
return ContextualBindingBuilder(this, [concrete]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a contextual binding to the container.
|
||||||
|
///
|
||||||
|
/// This is used internally by [ContextualBindingBuilder] to register the actual binding.
|
||||||
|
void addContextualBinding(
|
||||||
|
Type concrete, Type abstract, dynamic implementation) {
|
||||||
|
_contextual.putIfAbsent(concrete, () => {});
|
||||||
|
_contextual[concrete]![abstract] = implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind a callback to resolve with Container::call.
|
||||||
|
///
|
||||||
|
/// This allows you to register custom resolution logic for specific method calls.
|
||||||
|
void bindMethod(String method, Function callback) {
|
||||||
|
if (_methodBindings.containsKey(method)) {
|
||||||
|
throw StateError(
|
||||||
|
'This container already has a method binding for $method.');
|
||||||
|
}
|
||||||
|
_methodBindings[method] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the given method and inject its dependencies.
|
||||||
|
///
|
||||||
|
/// This method supports both static methods and instance methods.
|
||||||
|
dynamic callMethod(String method, [List<dynamic> arguments = const []]) {
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
if (search._methodBindings.containsKey(method)) {
|
||||||
|
return Function.apply(search._methodBindings[method]!, arguments);
|
||||||
|
}
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
throw StateError('No method binding found for $method.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this container or any parent has a method binding.
|
||||||
|
bool hasMethodBinding(String method) {
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
if (search._methodBindings.containsKey(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign a set of tags to a given binding.
|
||||||
|
///
|
||||||
|
/// This allows you to group related bindings together under a common tag.
|
||||||
|
void tag(List<Type> abstracts, String tag) {
|
||||||
|
_tags[tag] ??= [];
|
||||||
|
_tags[tag]!.addAll(abstracts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve all bindings for a given tag.
|
||||||
|
///
|
||||||
|
/// Returns a list of instances for all bindings tagged with the given tag.
|
||||||
|
List<dynamic> tagged(String tag) {
|
||||||
|
var result = <Type>{}; // Use Set to avoid duplicates
|
||||||
|
|
||||||
|
// Collect tagged types from this container and all parents
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
if (search._tags.containsKey(tag)) {
|
||||||
|
result.addAll(search._tags[tag]!);
|
||||||
|
}
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.map((type) => make(type)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a scoped binding in the container.
|
||||||
|
///
|
||||||
|
/// Scoped bindings are similar to singletons but are cleared when [clearScoped] is called.
|
||||||
|
void scoped<T>(T Function(Container) factory) {
|
||||||
|
_scopedInstances.add(T);
|
||||||
|
registerSingleton<T>(
|
||||||
|
factory(this)); // Use singleton to ensure same instance
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all scoped bindings from the container.
|
||||||
|
void clearScoped() {
|
||||||
|
// Clear this container's scoped instances
|
||||||
|
for (var type in _scopedInstances) {
|
||||||
|
_singletons.remove(type);
|
||||||
|
_factories.remove(type);
|
||||||
|
}
|
||||||
|
_scopedInstances.clear();
|
||||||
|
|
||||||
|
// Clear parent's scoped instances if any
|
||||||
|
if (_parent != null) {
|
||||||
|
_parent.clearScoped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all scoped instances from this container and its parents.
|
||||||
|
List<Type> _getAllScopedInstances() {
|
||||||
|
var result = <Type>{}; // Use Set to avoid duplicates
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
result.addAll(search._scopedInstances);
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
return result.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a callback to be run before resolving a type.
|
||||||
|
void beforeResolving<T>(
|
||||||
|
void Function(Type type, List args, Container container) callback) {
|
||||||
|
_beforeResolvingCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a callback to be run while resolving a type.
|
||||||
|
void resolving<T>(
|
||||||
|
void Function(dynamic instance, Container container) callback) {
|
||||||
|
_resolvingCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a callback to be run after resolving a type.
|
||||||
|
void afterResolving<T>(
|
||||||
|
void Function(dynamic instance, Container container) callback) {
|
||||||
|
_afterResolvingCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fire the "before resolving" callbacks for a type.
|
||||||
|
void _fireBeforeResolvingCallbacks(Type type, List args) {
|
||||||
|
// Fire parent callbacks first
|
||||||
|
if (_parent != null) {
|
||||||
|
_parent._fireBeforeResolvingCallbacks(type, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then fire this container's callbacks
|
||||||
|
for (var callback in _beforeResolvingCallbacks) {
|
||||||
|
callback(type, args, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fire the "resolving" callbacks for an instance.
|
||||||
|
void _fireResolvingCallbacks(dynamic instance) {
|
||||||
|
// Fire parent callbacks first
|
||||||
|
if (_parent != null) {
|
||||||
|
_parent._fireResolvingCallbacks(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then fire this container's callbacks
|
||||||
|
for (var callback in _resolvingCallbacks) {
|
||||||
|
callback(instance, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fire the "after resolving" callbacks for an instance.
|
||||||
|
void _fireAfterResolvingCallbacks(dynamic instance) {
|
||||||
|
// Fire parent callbacks first
|
||||||
|
if (_parent != null) {
|
||||||
|
_parent._fireAfterResolvingCallbacks(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then fire this container's callbacks
|
||||||
|
for (var callback in _afterResolvingCallbacks) {
|
||||||
|
callback(instance, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a contextual concrete binding for the given abstract type.
|
||||||
|
dynamic _getContextualConcrete(Type abstract) {
|
||||||
|
if (_buildStack.isEmpty) return null;
|
||||||
|
|
||||||
|
// Check current container's contextual bindings
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
var building = _buildStack.last;
|
||||||
|
var contextMap = search._contextual[building];
|
||||||
|
if (contextMap != null && contextMap.containsKey(abstract)) {
|
||||||
|
return contextMap[abstract];
|
||||||
|
}
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a contextual binding map for a concrete type.
|
||||||
|
Map<Type, dynamic>? _getContextualBindings(Type concrete) {
|
||||||
|
return _contextual[concrete];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a contextual concrete binding from parent classes in the build stack.
|
||||||
|
dynamic _getContextualConcreteFromParent(Type abstract) {
|
||||||
|
if (_buildStack.isEmpty) return null;
|
||||||
|
|
||||||
|
// Get the parent type from the build stack
|
||||||
|
var parentIndex = _buildStack.length - 2;
|
||||||
|
if (parentIndex < 0) return null;
|
||||||
|
|
||||||
|
var parentType = _buildStack[parentIndex];
|
||||||
|
|
||||||
|
// Check current container's contextual bindings
|
||||||
|
Container? search = this;
|
||||||
|
while (search != null) {
|
||||||
|
var contextMap = search._contextual[parentType];
|
||||||
|
if (contextMap != null && contextMap.containsKey(abstract)) {
|
||||||
|
return contextMap[abstract];
|
||||||
|
}
|
||||||
|
search = search._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if we're in danger of a circular dependency.
|
||||||
|
void _checkCircularDependency(Type type) {
|
||||||
|
if (_buildStack.contains(type)) {
|
||||||
|
throw CircularDependencyException(
|
||||||
|
'Circular dependency detected while building $type. Build stack: ${_buildStack.join(' -> ')}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
incubation/container/container/lib/src/container_const.dart
Normal file
31
incubation/container/container/lib/src/container_const.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// A utility class that contains constant values related to container functionality.
|
||||||
|
///
|
||||||
|
/// This class is not meant to be instantiated and only provides static constants.
|
||||||
|
/// It includes a default error message for reflection-related issues.
|
||||||
|
class ContainerConst {
|
||||||
|
/// The default error message for reflection-related issues.
|
||||||
|
///
|
||||||
|
/// This message is used when an attempt is made to perform a reflective action,
|
||||||
|
/// but the `ThrowingReflector` class is being used, which disables reflection.
|
||||||
|
/// Consider using the `MirrorsReflector` class if reflection is necessary.
|
||||||
|
static const String defaultErrorMessage =
|
||||||
|
'You attempted to perform a reflective action, but you are using `ThrowingReflector`, '
|
||||||
|
'a class which disables reflection. Consider using the `MirrorsReflector` '
|
||||||
|
'class if you need reflection.';
|
||||||
|
|
||||||
|
/// Private constructor to prevent instantiation of this utility class.
|
||||||
|
///
|
||||||
|
/// This constructor is marked as private (with the underscore prefix) to ensure
|
||||||
|
/// that the `ContainerConst` class cannot be instantiated. This is consistent
|
||||||
|
/// with the class's purpose of only providing static constants.
|
||||||
|
ContainerConst._();
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'container.dart';
|
||||||
|
|
||||||
|
/// A builder class for defining contextual bindings in the container.
|
||||||
|
///
|
||||||
|
/// This class provides a fluent interface for defining how abstract types should
|
||||||
|
/// be resolved in specific contexts. It allows for different implementations of
|
||||||
|
/// an interface to be used depending on where they are being injected.
|
||||||
|
class ContextualBindingBuilder {
|
||||||
|
/// The container instance this builder is associated with
|
||||||
|
final Container container;
|
||||||
|
|
||||||
|
/// The concrete type that needs a contextual binding
|
||||||
|
final List<Type> concrete;
|
||||||
|
|
||||||
|
/// Creates a new contextual binding builder
|
||||||
|
ContextualBindingBuilder(this.container, this.concrete);
|
||||||
|
|
||||||
|
/// Define the abstract type that should be bound differently in this context
|
||||||
|
ContextualImplementationBuilder needs<T>() {
|
||||||
|
return ContextualImplementationBuilder(container, concrete, T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder class for defining the implementation for a contextual binding.
|
||||||
|
///
|
||||||
|
/// This class completes the contextual binding definition by specifying what
|
||||||
|
/// implementation should be used for the abstract type in the given context.
|
||||||
|
class ContextualImplementationBuilder {
|
||||||
|
/// The container instance this builder is associated with
|
||||||
|
final Container container;
|
||||||
|
|
||||||
|
/// The concrete type that needs a contextual binding
|
||||||
|
final List<Type> concrete;
|
||||||
|
|
||||||
|
/// The abstract type that needs to be bound
|
||||||
|
final Type abstract;
|
||||||
|
|
||||||
|
/// Creates a new contextual implementation builder
|
||||||
|
ContextualImplementationBuilder(this.container, this.concrete, this.abstract);
|
||||||
|
|
||||||
|
/// Specify the implementation that should be used
|
||||||
|
void give<T>() {
|
||||||
|
for (var concreteType in concrete) {
|
||||||
|
container.addContextualBinding(concreteType, abstract, T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specify a factory function that should be used to create the implementation
|
||||||
|
void giveFactory(dynamic Function(Container container) factory) {
|
||||||
|
for (var concreteType in concrete) {
|
||||||
|
container.addContextualBinding(concreteType, abstract, factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
377
incubation/container/container/lib/src/empty/empty.dart
Normal file
377
incubation/container/container/lib/src/empty/empty.dart
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
|
||||||
|
/// A cache to store symbol names.
|
||||||
|
///
|
||||||
|
/// This map associates [Symbol] objects with their corresponding string representations.
|
||||||
|
/// It's used to avoid repeated parsing of symbol names, improving performance
|
||||||
|
/// when retrieving symbol names multiple times.
|
||||||
|
final Map<Symbol, String?> _symbolNames = <Symbol, String?>{};
|
||||||
|
|
||||||
|
/// A [Reflector] implementation that performs no actual reflection,
|
||||||
|
/// instead returning empty objects on every invocation.
|
||||||
|
///
|
||||||
|
/// Use this in contexts where you know you won't need any reflective capabilities.
|
||||||
|
///
|
||||||
|
/// This class provides a lightweight alternative to full reflection when reflection
|
||||||
|
/// functionality is not required. It returns empty or placeholder objects for all
|
||||||
|
/// reflection operations, which can be useful in scenarios where reflection is
|
||||||
|
/// expected but not actually used, or when you want to minimize the overhead of
|
||||||
|
/// reflection in certain parts of your application.
|
||||||
|
///
|
||||||
|
/// The [EmptyReflector] includes:
|
||||||
|
/// - A static [RegExp] for extracting symbol names without reflection.
|
||||||
|
/// - Methods to return empty implementations of [ReflectedClass], [ReflectedInstance],
|
||||||
|
/// [ReflectedType], and [ReflectedFunction].
|
||||||
|
/// - A [getName] method that uses a cache to store and retrieve symbol names.
|
||||||
|
///
|
||||||
|
/// This implementation can be particularly useful in testing scenarios or in
|
||||||
|
/// production environments where reflection is not needed but the interface
|
||||||
|
/// expecting reflection capabilities needs to be satisfied.
|
||||||
|
class EmptyReflector extends Reflector {
|
||||||
|
/// A [RegExp] that can be used to extract the name of a symbol without reflection.
|
||||||
|
///
|
||||||
|
/// This regular expression pattern matches the string representation of a Dart [Symbol],
|
||||||
|
/// which typically looks like 'Symbol("symbolName")'. It captures the symbol name
|
||||||
|
/// (the part between the quotes) in a capturing group.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```dart
|
||||||
|
/// String symbolString = 'Symbol("exampleSymbol")';
|
||||||
|
/// Match? match = symbolRegex.firstMatch(symbolString);
|
||||||
|
/// String? symbolName = match?.group(1); // Returns "exampleSymbol"
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This is particularly useful in contexts where reflection is not available
|
||||||
|
/// or desired, allowing for symbol name extraction through string manipulation.
|
||||||
|
static final RegExp symbolRegex = RegExp(r'Symbol\("([^"]+)"\)');
|
||||||
|
|
||||||
|
/// Creates an instance of [EmptyReflector].
|
||||||
|
///
|
||||||
|
/// This constructor doesn't take any parameters and creates a lightweight
|
||||||
|
/// reflector that provides empty implementations for all reflection operations.
|
||||||
|
/// It's useful in scenarios where reflection capabilities are expected but not
|
||||||
|
/// actually used, or when you want to minimize the overhead of reflection.
|
||||||
|
const EmptyReflector();
|
||||||
|
|
||||||
|
/// Retrieves the name of a given [Symbol].
|
||||||
|
///
|
||||||
|
/// This method attempts to extract the name of the provided [symbol] using
|
||||||
|
/// the [symbolRegex]. If the name hasn't been cached before, it will be
|
||||||
|
/// computed and stored in the [_symbolNames] cache for future use.
|
||||||
|
///
|
||||||
|
/// The method works as follows:
|
||||||
|
/// 1. It checks if the symbol's name is already in the cache.
|
||||||
|
/// 2. If not found, it uses [putIfAbsent] to compute the name:
|
||||||
|
/// a. It converts the symbol to a string.
|
||||||
|
/// b. It applies the [symbolRegex] to extract the name.
|
||||||
|
/// c. If a match is found, it returns the first captured group (the name).
|
||||||
|
/// 3. The computed name (or null if not found) is stored in the cache and returned.
|
||||||
|
///
|
||||||
|
/// @param symbol The [Symbol] whose name is to be retrieved.
|
||||||
|
/// @return The name of the symbol as a [String], or null if the name couldn't be extracted.
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) {
|
||||||
|
return _symbolNames.putIfAbsent(
|
||||||
|
symbol, () => symbolRegex.firstMatch(symbol.toString())?.group(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an empty [ReflectedClass] instance for any given [Type].
|
||||||
|
///
|
||||||
|
/// This method is part of the [EmptyReflector] implementation and always
|
||||||
|
/// returns a constant instance of [_EmptyReflectedClass], regardless of
|
||||||
|
/// the input [clazz].
|
||||||
|
///
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides non-functional placeholders for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param clazz The [Type] to reflect, which is ignored in this implementation.
|
||||||
|
/// @return A constant [_EmptyReflectedClass] instance.
|
||||||
|
@override
|
||||||
|
ReflectedClass reflectClass(Type clazz) {
|
||||||
|
return const _EmptyReflectedClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an empty [ReflectedInstance] for any given object.
|
||||||
|
///
|
||||||
|
/// This method is part of the [EmptyReflector] implementation and always
|
||||||
|
/// returns a constant instance of [_EmptyReflectedInstance], regardless of
|
||||||
|
/// the input [object].
|
||||||
|
///
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides non-functional placeholders for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param object The object to reflect, which is ignored in this implementation.
|
||||||
|
/// @return A constant [_EmptyReflectedInstance].
|
||||||
|
@override
|
||||||
|
ReflectedInstance reflectInstance(Object object) {
|
||||||
|
return const _EmptyReflectedInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an empty [ReflectedType] for any given [Type].
|
||||||
|
///
|
||||||
|
/// This method is part of the [EmptyReflector] implementation and always
|
||||||
|
/// returns a constant instance of [_EmptyReflectedType], regardless of
|
||||||
|
/// the input [type].
|
||||||
|
///
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides non-functional placeholders for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param type The [Type] to reflect, which is ignored in this implementation.
|
||||||
|
/// @return A constant [_EmptyReflectedType] instance.
|
||||||
|
@override
|
||||||
|
ReflectedType reflectType(Type type) {
|
||||||
|
return const _EmptyReflectedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an empty [ReflectedFunction] for any given [Function].
|
||||||
|
///
|
||||||
|
/// This method is part of the [EmptyReflector] implementation and always
|
||||||
|
/// returns a constant instance of [_EmptyReflectedFunction], regardless of
|
||||||
|
/// the input [function].
|
||||||
|
///
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides non-functional placeholders for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param function The [Function] to reflect, which is ignored in this implementation.
|
||||||
|
/// @return A constant [_EmptyReflectedFunction] instance.
|
||||||
|
@override
|
||||||
|
ReflectedFunction reflectFunction(Function function) {
|
||||||
|
return const _EmptyReflectedFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An empty implementation of [ReflectedClass] used by [EmptyReflector].
|
||||||
|
///
|
||||||
|
/// This class provides a non-functional placeholder for reflection operations
|
||||||
|
/// on classes. It is designed to be used in contexts where reflection capabilities
|
||||||
|
/// are expected but not actually needed or desired.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Extends [ReflectedClass] with minimal implementation.
|
||||||
|
/// - Constructor initializes with empty or default values for all properties.
|
||||||
|
/// - [newInstance] method throws an [UnsupportedError] if called.
|
||||||
|
/// - [isAssignableTo] method only returns true if compared with itself.
|
||||||
|
///
|
||||||
|
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||||
|
class _EmptyReflectedClass extends ReflectedClass {
|
||||||
|
/// Constructs an empty [_EmptyReflectedClass] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes the instance with empty or default values for all properties.
|
||||||
|
///
|
||||||
|
/// @param name The name of the class, set to '(empty)'.
|
||||||
|
/// @param typeParameters The list of type parameters, set to an empty list.
|
||||||
|
/// @param instances The list of instances, set to an empty list.
|
||||||
|
/// @param functions The list of functions, set to an empty list.
|
||||||
|
/// @param declarations The list of declarations, set to an empty list.
|
||||||
|
/// @param type The underlying [Type] of the class, set to [Object].
|
||||||
|
const _EmptyReflectedClass()
|
||||||
|
: super(
|
||||||
|
'(empty)',
|
||||||
|
const <ReflectedTypeParameter>[],
|
||||||
|
const <ReflectedInstance>[],
|
||||||
|
const <ReflectedFunction>[],
|
||||||
|
const <ReflectedDeclaration>[],
|
||||||
|
Object);
|
||||||
|
|
||||||
|
/// Creates a new instance of the reflected class.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedClass] implementation and always
|
||||||
|
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||||
|
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||||
|
/// for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param constructorName The name of the constructor to invoke.
|
||||||
|
/// @param positionalArguments A list of positional arguments for the constructor.
|
||||||
|
/// @param namedArguments An optional map of named arguments for the constructor.
|
||||||
|
/// @param typeArguments An optional list of type arguments for generic classes.
|
||||||
|
/// @throws UnsupportedError Always thrown when this method is called.
|
||||||
|
/// @return This method never returns as it always throws an exception.
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'Classes reflected via an EmptyReflector cannot be instantiated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if this empty reflected class is assignable to another reflected type.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedClass] implementation and always
|
||||||
|
/// returns true only if the [other] type is the same instance as this one.
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides minimal functionality for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param other The [ReflectedType] to check against.
|
||||||
|
/// @return true if [other] is the same instance as this, false otherwise.
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
return other == this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An empty implementation of [ReflectedType] used by [EmptyReflector].
|
||||||
|
///
|
||||||
|
/// This class provides a non-functional placeholder for reflection operations
|
||||||
|
/// on types. It is designed to be used in contexts where reflection capabilities
|
||||||
|
/// are expected but not actually needed or desired.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Extends [ReflectedType] with minimal implementation.
|
||||||
|
/// - Constructor initializes with empty or default values for all properties.
|
||||||
|
/// - [newInstance] method throws an [UnsupportedError] if called.
|
||||||
|
/// - [isAssignableTo] method only returns true if compared with itself.
|
||||||
|
///
|
||||||
|
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||||
|
class _EmptyReflectedType extends ReflectedType {
|
||||||
|
/// Constructs an empty [_EmptyReflectedType] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes the instance with empty or default values for all properties.
|
||||||
|
///
|
||||||
|
/// @param name The name of the type, set to '(empty)'.
|
||||||
|
/// @param typeParameters The list of type parameters, set to an empty list.
|
||||||
|
/// @param type The underlying [Type], set to [Object].
|
||||||
|
const _EmptyReflectedType()
|
||||||
|
: super('(empty)', const <ReflectedTypeParameter>[], Object);
|
||||||
|
|
||||||
|
/// Creates a new instance of the reflected type.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedType] implementation and always
|
||||||
|
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||||
|
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||||
|
/// for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param constructorName The name of the constructor to invoke.
|
||||||
|
/// @param positionalArguments A list of positional arguments for the constructor.
|
||||||
|
/// @param namedArguments An optional map of named arguments for the constructor.
|
||||||
|
/// @param typeArguments An optional list of type arguments for generic types.
|
||||||
|
/// @throws UnsupportedError Always thrown when this method is called.
|
||||||
|
/// @return This method never returns as it always throws an exception.
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'Types reflected via an EmptyReflector cannot be instantiated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if this empty reflected type is assignable to another reflected type.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedType] implementation and always
|
||||||
|
/// returns true only if the [other] type is the same instance as this one.
|
||||||
|
/// This behavior is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// which provides minimal functionality for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param other The [ReflectedType] to check against.
|
||||||
|
/// @return true if [other] is the same instance as this, false otherwise.
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
return other == this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An empty implementation of [ReflectedInstance] used by [EmptyReflector].
|
||||||
|
///
|
||||||
|
/// This class provides a non-functional placeholder for reflection operations
|
||||||
|
/// on instances. It is designed to be used in contexts where reflection capabilities
|
||||||
|
/// are expected but not actually needed or desired.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Extends [ReflectedInstance] with minimal implementation.
|
||||||
|
/// - Constructor initializes with empty or default values for all properties.
|
||||||
|
/// - [getField] method throws an [UnsupportedError] if called.
|
||||||
|
///
|
||||||
|
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||||
|
class _EmptyReflectedInstance extends ReflectedInstance {
|
||||||
|
/// Constructs an empty [_EmptyReflectedInstance] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes the instance with empty or default values for all properties.
|
||||||
|
///
|
||||||
|
/// @param type The reflected type of the instance, set to an empty [_EmptyReflectedType].
|
||||||
|
/// @param reflectedClass The reflected class of the instance, set to an empty [_EmptyReflectedClass].
|
||||||
|
/// @param value The underlying value of the instance, set to null.
|
||||||
|
const _EmptyReflectedInstance()
|
||||||
|
: super(const _EmptyReflectedType(), const _EmptyReflectedClass(), null);
|
||||||
|
|
||||||
|
/// Retrieves the value of a field on this empty reflected instance.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedInstance] implementation and always
|
||||||
|
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||||
|
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||||
|
/// for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param name The name of the field to retrieve.
|
||||||
|
/// @throws UnsupportedError Always thrown when this method is called.
|
||||||
|
/// @return This method never returns as it always throws an exception.
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'Instances reflected via an EmptyReflector cannot call getField().');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An empty implementation of [ReflectedFunction] used by [EmptyReflector].
|
||||||
|
///
|
||||||
|
/// This class provides a non-functional placeholder for reflection operations
|
||||||
|
/// on functions. It is designed to be used in contexts where reflection capabilities
|
||||||
|
/// are expected but not actually needed or desired.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Extends [ReflectedFunction] with minimal implementation.
|
||||||
|
/// - Constructor initializes with empty or default values for all properties.
|
||||||
|
/// - [invoke] method throws an [UnsupportedError] if called.
|
||||||
|
///
|
||||||
|
/// This implementation is consistent with the purpose of [EmptyReflector],
|
||||||
|
/// providing a lightweight alternative when full reflection capabilities are not required.
|
||||||
|
class _EmptyReflectedFunction extends ReflectedFunction {
|
||||||
|
/// Constructs an empty [_EmptyReflectedFunction] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes the instance with empty or default values for all properties.
|
||||||
|
///
|
||||||
|
/// @param name The name of the function, set to an empty string.
|
||||||
|
/// @param typeParameters A list of type parameters for the function, set to an empty list.
|
||||||
|
/// @param enclosingInstance A list of enclosing instances for the function, set to an empty list.
|
||||||
|
/// @param parameters A list of parameters for the function, set to an empty list.
|
||||||
|
/// @param isStatic Indicates whether the function is static, set to false.
|
||||||
|
/// @param isConst Indicates whether the function is constant, set to false.
|
||||||
|
/// @param returnType The return type of the function, set to an empty [_EmptyReflectedType].
|
||||||
|
/// @param isOperator Indicates whether the function is an operator, set to false.
|
||||||
|
/// @param isExtensionMember Indicates whether the function is an extension member, set to false.
|
||||||
|
const _EmptyReflectedFunction()
|
||||||
|
: super(
|
||||||
|
'(empty)',
|
||||||
|
const <ReflectedTypeParameter>[],
|
||||||
|
const <ReflectedInstance>[],
|
||||||
|
const <ReflectedParameter>[],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
returnType: const _EmptyReflectedType());
|
||||||
|
|
||||||
|
/// Invokes this empty reflected function.
|
||||||
|
///
|
||||||
|
/// This method is part of the [_EmptyReflectedFunction] implementation and always
|
||||||
|
/// throws an [UnsupportedError] when called. This behavior is consistent with
|
||||||
|
/// the purpose of [EmptyReflector], which provides non-functional placeholders
|
||||||
|
/// for reflection operations.
|
||||||
|
///
|
||||||
|
/// @param invocation The invocation to execute.
|
||||||
|
/// @throws UnsupportedError Always thrown when this method is called.
|
||||||
|
/// @return This method never returns as it always throws an exception.
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'Instances reflected via an EmptyReflector cannot call invoke().');
|
||||||
|
}
|
||||||
|
}
|
55
incubation/container/container/lib/src/exception.dart
Normal file
55
incubation/container/container/lib/src/exception.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Base exception class for container-related errors.
|
||||||
|
abstract class ContainerException implements Exception {
|
||||||
|
/// The error message
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
/// Optional cause of the exception
|
||||||
|
final Object? cause;
|
||||||
|
|
||||||
|
/// Creates a new container exception
|
||||||
|
ContainerException(this.message, [this.cause]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => cause == null ? message : '$message (Caused by: $cause)';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when reflection operations fail
|
||||||
|
class ReflectionException extends ContainerException {
|
||||||
|
ReflectionException(String message, [Object? cause]) : super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when a binding resolution fails
|
||||||
|
class BindingResolutionException extends ContainerException {
|
||||||
|
BindingResolutionException(String message, [Object? cause])
|
||||||
|
: super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when a circular dependency is detected
|
||||||
|
class CircularDependencyException extends ContainerException {
|
||||||
|
CircularDependencyException(String message, [Object? cause])
|
||||||
|
: super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when an entry is not found in the container
|
||||||
|
class EntryNotFoundException extends ContainerException {
|
||||||
|
/// The identifier that was not found
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
EntryNotFoundException(this.id, [Object? cause])
|
||||||
|
: super('No entry was found for identifier "$id"', cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when there are contextual binding issues
|
||||||
|
class ContextualBindingException extends ContainerException {
|
||||||
|
ContextualBindingException(String message, [Object? cause])
|
||||||
|
: super(message, cause);
|
||||||
|
}
|
10
incubation/container/container/lib/src/mirrors/mirrors.dart
Normal file
10
incubation/container/container/lib/src/mirrors/mirrors.dart
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export 'reflector.dart';
|
904
incubation/container/container/lib/src/mirrors/reflector.dart
Normal file
904
incubation/container/container/lib/src/mirrors/reflector.dart
Normal file
|
@ -0,0 +1,904 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:mirrors' as dart;
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:quiver/core.dart';
|
||||||
|
|
||||||
|
/// A [Reflector] implementation that forwards to `dart:mirrors`.
|
||||||
|
///
|
||||||
|
/// This class provides reflection capabilities by leveraging the `dart:mirrors` library.
|
||||||
|
/// It allows for runtime introspection of classes, functions, types, and instances.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Reflects classes, functions, types, and instances
|
||||||
|
/// - Provides access to class and function metadata
|
||||||
|
/// - Supports reflection of generic types and futures
|
||||||
|
/// - Allows invocation of reflected functions
|
||||||
|
///
|
||||||
|
/// Note: This reflector is primarily useful on the server-side where reflection is fully supported.
|
||||||
|
/// It may not be suitable for client-side Dart applications due to limitations in reflection support.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final classReflection = reflector.reflectClass(MyClass);
|
||||||
|
/// final functionReflection = reflector.reflectFunction(myFunction);
|
||||||
|
/// final typeReflection = reflector.reflectType(int);
|
||||||
|
/// final instanceReflection = reflector.reflectInstance(myObject);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Be aware of the performance implications when using reflection extensively,
|
||||||
|
/// as it can impact runtime performance and increase code size.
|
||||||
|
class MirrorsReflector extends Reflector {
|
||||||
|
/// Creates a new instance of [MirrorsReflector].
|
||||||
|
///
|
||||||
|
/// This constructor initializes the [MirrorsReflector] instance.
|
||||||
|
const MirrorsReflector();
|
||||||
|
|
||||||
|
/// Retrieves the name of a symbol as a string.
|
||||||
|
///
|
||||||
|
/// This method overrides the base implementation to use the `dart:mirrors` library
|
||||||
|
/// for converting a [Symbol] to its corresponding string representation.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [symbol]: The [Symbol] whose name is to be retrieved.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [String] representing the name of the given symbol.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final name = getName(#someSymbol);
|
||||||
|
/// print(name); // Outputs: "someSymbol"
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol);
|
||||||
|
|
||||||
|
/// Reflects a class and returns a [ReflectedClass] instance.
|
||||||
|
///
|
||||||
|
/// This method takes a [Type] parameter [clazz] and uses dart:mirrors to create
|
||||||
|
/// a reflection of the class. It returns a [_ReflectedClassMirror] which
|
||||||
|
/// implements [ReflectedClass].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [clazz]: The [Type] of the class to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedClass] instance representing the reflected class.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError] if the provided [clazz] is not a class.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final classReflection = reflector.reflectClass(MyClass);
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedClass reflectClass(Type clazz) {
|
||||||
|
var mirror = dart.reflectType(clazz);
|
||||||
|
|
||||||
|
if (mirror is dart.ClassMirror) {
|
||||||
|
return _ReflectedClassMirror(mirror);
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('$clazz is not a class.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects a function and returns a [ReflectedFunction] instance.
|
||||||
|
///
|
||||||
|
/// This method takes a [Function] parameter [function] and uses dart:mirrors to create
|
||||||
|
/// a reflection of the function. It returns a [_ReflectedMethodMirror] which
|
||||||
|
/// implements [ReflectedFunction].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [function]: The [Function] to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedFunction] instance representing the reflected function.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final functionReflection = reflector.reflectFunction(myFunction);
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedFunction reflectFunction(Function function) {
|
||||||
|
var closure = dart.reflect(function) as dart.ClosureMirror;
|
||||||
|
return _ReflectedMethodMirror(closure.function, closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects a given type and returns a [ReflectedType] instance.
|
||||||
|
///
|
||||||
|
/// This method takes a [Type] parameter and uses dart:mirrors to create
|
||||||
|
/// a reflection of the type. It returns either a [_ReflectedClassMirror]
|
||||||
|
/// or a [_ReflectedTypeMirror] depending on whether the reflected type
|
||||||
|
/// is a class or not.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: The [Type] to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedType] instance representing the reflected type.
|
||||||
|
///
|
||||||
|
/// If the reflected type doesn't have a reflected type (i.e., [hasReflectedType] is false),
|
||||||
|
/// it returns a reflection of the `dynamic` type instead.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final typeReflection = reflector.reflectType(int);
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedType reflectType(Type type) {
|
||||||
|
var mirror = dart.reflectType(type);
|
||||||
|
|
||||||
|
if (!mirror.hasReflectedType) {
|
||||||
|
return reflectType(dynamic);
|
||||||
|
} else {
|
||||||
|
if (mirror is dart.ClassMirror) {
|
||||||
|
return _ReflectedClassMirror(mirror);
|
||||||
|
} else {
|
||||||
|
return _ReflectedTypeMirror(mirror);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects a Future of a given type and returns a [ReflectedType] instance.
|
||||||
|
///
|
||||||
|
/// This method takes a [Type] parameter and creates a reflection of a Future
|
||||||
|
/// that wraps that type. It first reflects the inner type, then constructs
|
||||||
|
/// a Future type with that inner type as its type argument.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: The [Type] to be wrapped in a Future.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedType] instance representing the reflected Future<Type>.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError] if the provided [type] is not a class or type.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final futureIntReflection = reflector.reflectFutureOf(int);
|
||||||
|
/// // This will reflect Future<int>
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) {
|
||||||
|
var inner = reflectType(type);
|
||||||
|
dart.TypeMirror localMirror;
|
||||||
|
if (inner is _ReflectedClassMirror) {
|
||||||
|
localMirror = inner.mirror;
|
||||||
|
} else if (inner is _ReflectedTypeMirror) {
|
||||||
|
localMirror = inner.mirror;
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('$type is not a class or type.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var future = dart.reflectType(Future, [localMirror.reflectedType]);
|
||||||
|
return _ReflectedClassMirror(future as dart.ClassMirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects an instance of an object and returns a [ReflectedInstance].
|
||||||
|
///
|
||||||
|
/// This method takes an [Object] parameter and uses dart:mirrors to create
|
||||||
|
/// a reflection of the object instance. It returns a [_ReflectedInstanceMirror]
|
||||||
|
/// which implements [ReflectedInstance].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [object]: The object instance to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedInstance] representing the reflected object instance.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final reflector = MirrorsReflector();
|
||||||
|
/// final instanceReflection = reflector.reflectInstance(myObject);
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedInstance reflectInstance(Object object) {
|
||||||
|
return _ReflectedInstanceMirror(dart.reflect(object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected type parameter using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedTypeParameter] and wraps a [dart.TypeVariableMirror]
|
||||||
|
/// to provide reflection capabilities for type parameters in Dart.
|
||||||
|
///
|
||||||
|
/// The class extracts the name of the type parameter from the mirror and passes
|
||||||
|
/// it to the superclass constructor.
|
||||||
|
///
|
||||||
|
/// This is typically used internally by the reflection system to represent
|
||||||
|
/// type parameters of generic classes or methods.
|
||||||
|
class _ReflectedTypeParameter extends ReflectedTypeParameter {
|
||||||
|
/// The [dart.TypeVariableMirror] instance representing the reflected type parameter.
|
||||||
|
///
|
||||||
|
/// This mirror provides access to the details of the type parameter, such as its name,
|
||||||
|
/// bounds, and other metadata. It is used internally by the [_ReflectedTypeParameter]
|
||||||
|
/// class to implement reflection capabilities for type parameters.
|
||||||
|
final dart.TypeVariableMirror mirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedTypeParameter] instance.
|
||||||
|
///
|
||||||
|
/// This constructor takes a [dart.TypeVariableMirror] and initializes the
|
||||||
|
/// [_ReflectedTypeParameter] with the name of the type parameter extracted
|
||||||
|
/// from the mirror.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.TypeVariableMirror] representing the type parameter.
|
||||||
|
///
|
||||||
|
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||||
|
/// type parameter from the mirror's [simpleName] and passes it to the superclass
|
||||||
|
/// constructor.
|
||||||
|
_ReflectedTypeParameter(this.mirror)
|
||||||
|
: super(dart.MirrorSystem.getName(mirror.simpleName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected type using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedType] and wraps a [dart.TypeMirror]
|
||||||
|
/// to provide reflection capabilities for types in Dart.
|
||||||
|
///
|
||||||
|
/// The class extracts the name and type variables from the mirror and passes
|
||||||
|
/// them to the superclass constructor. It also implements type comparison
|
||||||
|
/// through the [isAssignableTo] method.
|
||||||
|
///
|
||||||
|
/// Note that this class represents types that are not classes, and therefore
|
||||||
|
/// cannot be instantiated. Attempting to call [newInstance] will throw a
|
||||||
|
/// [ReflectionException].
|
||||||
|
///
|
||||||
|
/// This is typically used internally by the reflection system to represent
|
||||||
|
/// non-class types like interfaces, mixins, or type aliases.
|
||||||
|
class _ReflectedTypeMirror extends ReflectedType {
|
||||||
|
/// The [dart.TypeMirror] instance representing the reflected type.
|
||||||
|
///
|
||||||
|
/// This mirror provides access to the details of the type, such as its name,
|
||||||
|
/// type variables, and other metadata. It is used internally by the
|
||||||
|
/// [_ReflectedTypeMirror] class to implement reflection capabilities for types.
|
||||||
|
final dart.TypeMirror mirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedTypeMirror] instance.
|
||||||
|
///
|
||||||
|
/// This constructor takes a [dart.TypeMirror] and initializes the
|
||||||
|
/// [_ReflectedTypeMirror] with the following:
|
||||||
|
/// - The name of the type extracted from the mirror's [simpleName].
|
||||||
|
/// - A list of [_ReflectedTypeParameter] objects created from the mirror's type variables.
|
||||||
|
/// - The reflected type of the mirror.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.TypeMirror] representing the type to be reflected.
|
||||||
|
///
|
||||||
|
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||||
|
/// type from the mirror's [simpleName]. It also maps the mirror's type variables
|
||||||
|
/// to [_ReflectedTypeParameter] objects and passes them along with the reflected
|
||||||
|
/// type to the superclass constructor.
|
||||||
|
_ReflectedTypeMirror(this.mirror)
|
||||||
|
: super(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||||
|
mirror.reflectedType,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Checks if this reflected class is assignable to another reflected type.
|
||||||
|
///
|
||||||
|
/// This method determines whether an instance of this class can be assigned
|
||||||
|
/// to a variable of the type represented by [other].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [other]: The [ReflectedType] to check against.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true` if this class is assignable to [other].
|
||||||
|
/// - `false` otherwise, including when [other] is not a [_ReflectedClassMirror]
|
||||||
|
/// or [_ReflectedTypeMirror].
|
||||||
|
///
|
||||||
|
/// The method uses dart:mirrors' [isAssignableTo] to perform the actual check
|
||||||
|
/// when [other] is either a [_ReflectedClassMirror] or [_ReflectedTypeMirror].
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other is _ReflectedClassMirror) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else if (other is _ReflectedTypeMirror) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Throws a [ReflectionException] when attempting to create a new instance.
|
||||||
|
///
|
||||||
|
/// This method is intended to be overridden by classes that represent
|
||||||
|
/// instantiable types. For non-instantiable types (like interfaces or
|
||||||
|
/// abstract classes), this method throws an exception.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [constructorName]: The name of the constructor to invoke.
|
||||||
|
/// - [positionalArguments]: A list of positional arguments for the constructor.
|
||||||
|
/// - [namedArguments]: An optional map of named arguments for the constructor.
|
||||||
|
/// - [typeArguments]: An optional list of type arguments for generic classes.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// [ReflectionException]: Always thrown with a message indicating that
|
||||||
|
/// this type cannot be instantiated.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// // This will always throw a ReflectionException
|
||||||
|
/// reflectedType.newInstance('defaultConstructor', []);
|
||||||
|
/// ```
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||||
|
throw ReflectionException(
|
||||||
|
'$name is not a class, and therefore cannot be instantiated.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected class using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedClass] and wraps a [dart.ClassMirror]
|
||||||
|
/// to provide reflection capabilities for Dart classes.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Reflects class name, type parameters, constructors, and declarations
|
||||||
|
/// - Provides access to class metadata (annotations)
|
||||||
|
/// - Supports type comparison through [isAssignableTo]
|
||||||
|
/// - Allows creation of new instances of the reflected class
|
||||||
|
///
|
||||||
|
/// This class is typically used internally by the reflection system to
|
||||||
|
/// represent classes and their members.
|
||||||
|
class _ReflectedClassMirror extends ReflectedClass {
|
||||||
|
/// The [dart.ClassMirror] representing the reflected class.
|
||||||
|
///
|
||||||
|
/// This mirror is used to extract information about the class, such as
|
||||||
|
/// its name, type parameters, constructors, and declarations.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [dart.ClassMirror] for more details about the mirror system.
|
||||||
|
final dart.ClassMirror mirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedClassMirror] instance.
|
||||||
|
///
|
||||||
|
/// This constructor takes a [dart.ClassMirror] and initializes the
|
||||||
|
/// [_ReflectedClassMirror] with the following:
|
||||||
|
/// - The name of the class extracted from the mirror's [simpleName].
|
||||||
|
/// - A list of [_ReflectedTypeParameter] objects created from the mirror's type variables.
|
||||||
|
/// - Empty lists for constructors and annotations (these are populated elsewhere).
|
||||||
|
/// - A list of declarations obtained from the [_declarationsOf] method.
|
||||||
|
/// - The reflected type of the mirror.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.ClassMirror] representing the class to be reflected.
|
||||||
|
///
|
||||||
|
/// The constructor uses [dart.MirrorSystem.getName] to extract the name of the
|
||||||
|
/// class from the mirror's [simpleName]. It also maps the mirror's type variables
|
||||||
|
/// to [_ReflectedTypeParameter] objects and uses [_declarationsOf] to get the
|
||||||
|
/// class declarations. These are then passed to the superclass constructor.
|
||||||
|
_ReflectedClassMirror(this.mirror)
|
||||||
|
: super(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
mirror.typeVariables.map((m) => _ReflectedTypeParameter(m)).toList(),
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
_declarationsOf(mirror),
|
||||||
|
mirror.reflectedType,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Retrieves a list of reflected constructors from a given [dart.ClassMirror].
|
||||||
|
///
|
||||||
|
/// This static method iterates through the declarations of the provided [mirror],
|
||||||
|
/// identifies the constructor methods, and creates [ReflectedFunction] instances
|
||||||
|
/// for each constructor found.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.ClassMirror] representing the class to examine.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [List] of [ReflectedFunction] objects, each representing a constructor
|
||||||
|
/// of the class.
|
||||||
|
///
|
||||||
|
/// The method specifically looks for [dart.MethodMirror] instances that are
|
||||||
|
/// marked as constructors (i.e., [isConstructor] is true). Each identified
|
||||||
|
/// constructor is wrapped in a [_ReflectedMethodMirror] and added to the
|
||||||
|
/// returned list.
|
||||||
|
static List<ReflectedFunction> _constructorsOf(dart.ClassMirror mirror) {
|
||||||
|
var out = <ReflectedFunction>[];
|
||||||
|
|
||||||
|
for (var key in mirror.declarations.keys) {
|
||||||
|
var value = mirror.declarations[key];
|
||||||
|
|
||||||
|
if (value is dart.MethodMirror && value.isConstructor) {
|
||||||
|
out.add(_ReflectedMethodMirror(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves a list of reflected declarations from a given [dart.ClassMirror].
|
||||||
|
///
|
||||||
|
/// This static method iterates through the declarations of the provided [mirror],
|
||||||
|
/// identifies non-constructor methods, and creates [ReflectedDeclaration] instances
|
||||||
|
/// for each method found.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.ClassMirror] representing the class to examine.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [List] of [ReflectedDeclaration] objects, each representing a non-constructor
|
||||||
|
/// method of the class.
|
||||||
|
///
|
||||||
|
/// The method specifically looks for [dart.MethodMirror] instances that are
|
||||||
|
/// not constructors (i.e., [isConstructor] is false). Each identified
|
||||||
|
/// method is wrapped in a [_ReflectedDeclarationMirror] and added to the
|
||||||
|
/// returned list.
|
||||||
|
static List<ReflectedDeclaration> _declarationsOf(dart.ClassMirror mirror) {
|
||||||
|
var out = <ReflectedDeclaration>[];
|
||||||
|
|
||||||
|
for (var key in mirror.declarations.keys) {
|
||||||
|
var value = mirror.declarations[key];
|
||||||
|
|
||||||
|
if (value is dart.MethodMirror && !value.isConstructor) {
|
||||||
|
out.add(
|
||||||
|
_ReflectedDeclarationMirror(dart.MirrorSystem.getName(key), value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the annotations (metadata) associated with this reflected class.
|
||||||
|
///
|
||||||
|
/// This getter method overrides the base implementation to provide access to
|
||||||
|
/// the class-level annotations using dart:mirrors. It maps each metadata mirror
|
||||||
|
/// to a [_ReflectedInstanceMirror] and returns them as a list.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [List] of [ReflectedInstance] objects, each representing an annotation
|
||||||
|
/// applied to this class.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// @MyAnnotation()
|
||||||
|
/// class MyClass {}
|
||||||
|
///
|
||||||
|
/// // Assuming we have a reflection of MyClass
|
||||||
|
/// final classReflection = reflector.reflectClass(MyClass);
|
||||||
|
/// final annotations = classReflection.annotations;
|
||||||
|
/// // annotations will contain a ReflectedInstance of MyAnnotation
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note: This method relies on the [dart.ClassMirror]'s metadata property
|
||||||
|
/// and creates a new [_ReflectedInstanceMirror] for each annotation.
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations =>
|
||||||
|
mirror.metadata.map((m) => _ReflectedInstanceMirror(m)).toList();
|
||||||
|
|
||||||
|
/// Retrieves a list of reflected constructors for this class.
|
||||||
|
///
|
||||||
|
/// This getter method overrides the base implementation to provide access to
|
||||||
|
/// the constructors of the reflected class using dart:mirrors. It uses the
|
||||||
|
/// static [_constructorsOf] method to extract and wrap each constructor
|
||||||
|
/// in a [ReflectedFunction] object.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [List] of [ReflectedFunction] objects, each representing a constructor
|
||||||
|
/// of this class.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final classReflection = reflector.reflectClass(MyClass);
|
||||||
|
/// final constructors = classReflection.constructors;
|
||||||
|
/// // constructors will contain ReflectedFunction objects for each
|
||||||
|
/// // constructor in MyClass
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note: This method relies on the [dart.ClassMirror]'s declarations and
|
||||||
|
/// the [_constructorsOf] method to identify and create reflections of
|
||||||
|
/// the class constructors.
|
||||||
|
@override
|
||||||
|
List<ReflectedFunction> get constructors => _constructorsOf(mirror);
|
||||||
|
|
||||||
|
/// Checks if this reflected type is assignable to another reflected type.
|
||||||
|
///
|
||||||
|
/// This method determines whether an instance of this type can be assigned
|
||||||
|
/// to a variable of the type represented by [other].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [other]: The [ReflectedType] to check against.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true` if this type is assignable to [other].
|
||||||
|
/// - `false` otherwise, including when [other] is not a [_ReflectedClassMirror]
|
||||||
|
/// or [_ReflectedTypeMirror].
|
||||||
|
///
|
||||||
|
/// The method uses dart:mirrors' [isAssignableTo] to perform the actual check
|
||||||
|
/// when [other] is either a [_ReflectedClassMirror] or [_ReflectedTypeMirror].
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other is _ReflectedClassMirror) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else if (other is _ReflectedTypeMirror) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new instance of the reflected class.
|
||||||
|
///
|
||||||
|
/// This method instantiates a new object of the class represented by this
|
||||||
|
/// [_ReflectedClassMirror] using the specified constructor and arguments.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [constructorName]: The name of the constructor to invoke. Use an empty
|
||||||
|
/// string for the default constructor.
|
||||||
|
/// - [positionalArguments]: A list of positional arguments to pass to the constructor.
|
||||||
|
/// - [namedArguments]: An optional map of named arguments to pass to the constructor.
|
||||||
|
/// - [typeArguments]: An optional list of type arguments for generic classes.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedInstance] representing the newly created instance.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// May throw exceptions if the constructor invocation fails, e.g., due to
|
||||||
|
/// invalid arguments or if the class cannot be instantiated.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// This implementation currently does not use the [namedArguments] or
|
||||||
|
/// [typeArguments] parameters. They are included for API compatibility.
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||||
|
return _ReflectedInstanceMirror(
|
||||||
|
mirror.newInstance(Symbol(constructorName), positionalArguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if this [_ReflectedClassMirror] is equal to another object.
|
||||||
|
///
|
||||||
|
/// This method overrides the default equality operator to provide a custom
|
||||||
|
/// equality check for [_ReflectedClassMirror] instances.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [other]: The object to compare with this [_ReflectedClassMirror].
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true` if [other] is also a [_ReflectedClassMirror] and has the same
|
||||||
|
/// [mirror] as this instance.
|
||||||
|
/// - `false` otherwise.
|
||||||
|
///
|
||||||
|
/// This implementation ensures that two [_ReflectedClassMirror] instances
|
||||||
|
/// are considered equal if and only if they reflect the same class (i.e.,
|
||||||
|
/// their underlying [dart.ClassMirror]s are the same).
|
||||||
|
@override
|
||||||
|
bool operator ==(other) {
|
||||||
|
return other is _ReflectedClassMirror && other.mirror == mirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a hash code for this [_ReflectedClassMirror].
|
||||||
|
///
|
||||||
|
/// This method overrides the default [hashCode] implementation to provide
|
||||||
|
/// a consistent hash code for [_ReflectedClassMirror] instances.
|
||||||
|
///
|
||||||
|
/// The hash code is generated using the [hash2] function from the Quiver
|
||||||
|
/// library, combining the [mirror] object and an empty string. The empty
|
||||||
|
/// string is used as a second parameter to maintain compatibility with
|
||||||
|
/// the [hash2] function, which requires two arguments.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// An [int] representing the hash code of this [_ReflectedClassMirror].
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// This hash code implementation ensures that two [_ReflectedClassMirror]
|
||||||
|
/// instances with the same [mirror] will have the same hash code, which
|
||||||
|
/// is consistent with the equality check implemented in the [operator ==].
|
||||||
|
@override
|
||||||
|
int get hashCode => hash2(mirror, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected declaration using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedDeclaration] and wraps a [dart.MethodMirror]
|
||||||
|
/// to provide reflection capabilities for method declarations in Dart.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Reflects the name and static nature of the declaration
|
||||||
|
/// - Provides access to the underlying method as a [ReflectedFunction]
|
||||||
|
///
|
||||||
|
/// This class is typically used internally by the reflection system to
|
||||||
|
/// represent method declarations within a class.
|
||||||
|
class _ReflectedDeclarationMirror extends ReflectedDeclaration {
|
||||||
|
/// The [dart.MethodMirror] instance representing the reflected method.
|
||||||
|
///
|
||||||
|
/// This mirror provides access to the details of the method, such as its name,
|
||||||
|
/// parameters, return type, and other metadata. It is used internally by the
|
||||||
|
/// [_ReflectedDeclarationMirror] class to implement reflection capabilities
|
||||||
|
/// for method declarations.
|
||||||
|
final dart.MethodMirror mirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedDeclarationMirror] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes a new [_ReflectedDeclarationMirror] with the given [name]
|
||||||
|
/// and [mirror]. It uses the [dart.MethodMirror]'s [isStatic] property to determine
|
||||||
|
/// if the declaration is static, and passes `null` as the initial value for the function.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [name]: A [String] representing the name of the declaration.
|
||||||
|
/// - [mirror]: A [dart.MethodMirror] representing the reflected method.
|
||||||
|
///
|
||||||
|
/// The constructor calls the superclass constructor with the provided [name],
|
||||||
|
/// the [isStatic] property from the [mirror], and `null` for the function parameter.
|
||||||
|
_ReflectedDeclarationMirror(String name, this.mirror)
|
||||||
|
: super(name, mirror.isStatic, null);
|
||||||
|
|
||||||
|
/// Determines if this declaration is static.
|
||||||
|
///
|
||||||
|
/// This getter overrides the base implementation to provide information
|
||||||
|
/// about whether the reflected declaration is static or not. It directly
|
||||||
|
/// accesses the [isStatic] property of the underlying [dart.MethodMirror].
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [bool] value:
|
||||||
|
/// - `true` if the declaration is static.
|
||||||
|
/// - `false` if the declaration is not static (i.e., it's an instance method).
|
||||||
|
///
|
||||||
|
/// This property is useful for determining the nature of the reflected
|
||||||
|
/// declaration, particularly when working with class methods and properties.
|
||||||
|
@override
|
||||||
|
bool get isStatic => mirror.isStatic;
|
||||||
|
|
||||||
|
/// Retrieves a [ReflectedFunction] representation of this declaration.
|
||||||
|
///
|
||||||
|
/// This getter overrides the base implementation to provide a [ReflectedFunction]
|
||||||
|
/// that represents the method associated with this declaration. It creates a new
|
||||||
|
/// [_ReflectedMethodMirror] instance using the underlying [dart.MethodMirror].
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedFunction] object that represents the method of this declaration.
|
||||||
|
///
|
||||||
|
/// This property is useful for accessing detailed information about the method,
|
||||||
|
/// such as its parameters, return type, and other attributes, in a way that's
|
||||||
|
/// consistent with the reflection API.
|
||||||
|
@override
|
||||||
|
ReflectedFunction get function => _ReflectedMethodMirror(mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected instance of an object using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedInstance] and wraps a [dart.InstanceMirror]
|
||||||
|
/// to provide reflection capabilities for object instances in Dart.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Reflects the type and runtime type of the instance
|
||||||
|
/// - Provides access to the underlying object (reflectee)
|
||||||
|
/// - Allows retrieval of field values through reflection
|
||||||
|
///
|
||||||
|
/// This class is typically used internally by the reflection system to
|
||||||
|
/// represent instances of objects and provide reflective access to their fields.
|
||||||
|
class _ReflectedInstanceMirror extends ReflectedInstance {
|
||||||
|
/// The [dart.InstanceMirror] representing the reflected instance.
|
||||||
|
///
|
||||||
|
/// This mirror provides access to the details of the object instance, such as its type,
|
||||||
|
/// fields, and methods. It is used internally by the [_ReflectedInstanceMirror] class
|
||||||
|
/// to implement reflection capabilities for object instances.
|
||||||
|
///
|
||||||
|
/// The mirror allows for dynamic inspection and manipulation of the object's state
|
||||||
|
/// and behavior at runtime, enabling powerful reflection features.
|
||||||
|
final dart.InstanceMirror mirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedInstanceMirror] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes a new [_ReflectedInstanceMirror] with the given [mirror].
|
||||||
|
/// It uses the [dart.InstanceMirror]'s [type] property to create [_ReflectedClassMirror]
|
||||||
|
/// instances for both the type and runtime type of the reflected instance.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.InstanceMirror] representing the reflected instance.
|
||||||
|
///
|
||||||
|
/// The constructor calls the superclass constructor with:
|
||||||
|
/// - A [_ReflectedClassMirror] of the instance's type
|
||||||
|
/// - A [_ReflectedClassMirror] of the instance's runtime type
|
||||||
|
/// - The [reflectee] of the mirror, which is the actual object being reflected
|
||||||
|
///
|
||||||
|
/// This setup allows the [_ReflectedInstanceMirror] to provide access to both
|
||||||
|
/// the compile-time and runtime type information of the reflected instance,
|
||||||
|
/// as well as the underlying object itself.
|
||||||
|
_ReflectedInstanceMirror(this.mirror)
|
||||||
|
: super(_ReflectedClassMirror(mirror.type),
|
||||||
|
_ReflectedClassMirror(mirror.type), mirror.reflectee);
|
||||||
|
|
||||||
|
/// Retrieves the value of a field from the reflected instance.
|
||||||
|
///
|
||||||
|
/// This method allows access to field values of the object represented by this
|
||||||
|
/// [_ReflectedInstanceMirror] through reflection.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [name]: A [String] representing the name of the field to retrieve.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedInstance] representing the value of the specified field.
|
||||||
|
/// This returned instance is wrapped in a [_ReflectedInstanceMirror].
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// May throw exceptions if the field does not exist or if access is not allowed.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// var fieldValue = reflectedInstance.getField('myField');
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// This method uses the underlying [dart.InstanceMirror]'s [getField] method
|
||||||
|
/// to perform the actual field access.
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
return _ReflectedInstanceMirror(mirror.getField(Symbol(name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected method using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This class extends [ReflectedFunction] and wraps a [dart.MethodMirror]
|
||||||
|
/// to provide reflection capabilities for methods in Dart.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Reflects method name, parameters, and return type
|
||||||
|
/// - Provides access to method metadata (annotations)
|
||||||
|
/// - Supports invocation of the reflected method (if a ClosureMirror is available)
|
||||||
|
///
|
||||||
|
/// The class uses both [dart.MethodMirror] and optionally [dart.ClosureMirror]
|
||||||
|
/// to represent and potentially invoke the reflected method.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// - Created internally by the reflection system to represent methods
|
||||||
|
/// - Can be used to inspect method details or invoke the method if a ClosureMirror is provided
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// - Invocation is only possible if a ClosureMirror is provided during construction
|
||||||
|
/// - Throws a StateError if invoke is called without a ClosureMirror
|
||||||
|
class _ReflectedMethodMirror extends ReflectedFunction {
|
||||||
|
/// The [dart.MethodMirror] instance representing the reflected method.
|
||||||
|
///
|
||||||
|
/// This mirror provides access to the details of the method, such as its name,
|
||||||
|
/// parameters, return type, and other metadata. It is used internally by the
|
||||||
|
/// [_ReflectedMethodMirror] class to implement reflection capabilities
|
||||||
|
/// for methods.
|
||||||
|
///
|
||||||
|
/// The [dart.MethodMirror] is a crucial component in the reflection process,
|
||||||
|
/// allowing for introspection of method properties and behavior at runtime.
|
||||||
|
final dart.MethodMirror mirror;
|
||||||
|
|
||||||
|
/// An optional [dart.ClosureMirror] representing the closure of the reflected method.
|
||||||
|
///
|
||||||
|
/// This field is used to store a [dart.ClosureMirror] when the reflected method
|
||||||
|
/// is associated with a callable object (i.e., a closure). The presence of this
|
||||||
|
/// mirror enables the [invoke] method to directly call the reflected method.
|
||||||
|
///
|
||||||
|
/// If this field is null, it indicates that the reflected method cannot be
|
||||||
|
/// directly invoked through this [_ReflectedMethodMirror] instance.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// - This field is crucial for supporting method invocation via reflection.
|
||||||
|
/// - It's typically set when reflecting on instance methods or standalone functions.
|
||||||
|
/// - For class-level method declarations that aren't bound to an instance,
|
||||||
|
/// this field may be null.
|
||||||
|
final dart.ClosureMirror? closureMirror;
|
||||||
|
|
||||||
|
/// Constructs a [_ReflectedMethodMirror] instance.
|
||||||
|
///
|
||||||
|
/// This constructor initializes a new [_ReflectedMethodMirror] with the given [mirror]
|
||||||
|
/// and optional [closureMirror]. It extracts various properties from the [dart.MethodMirror]
|
||||||
|
/// to populate the superclass constructor.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.MethodMirror] representing the reflected method.
|
||||||
|
/// - [closureMirror]: An optional [dart.ClosureMirror] for method invocation.
|
||||||
|
///
|
||||||
|
/// The constructor initializes the following:
|
||||||
|
/// - Method name from the mirror's [simpleName]
|
||||||
|
/// - An empty list of reflected type parameters
|
||||||
|
/// - Metadata (annotations) as [_ReflectedInstanceMirror] objects
|
||||||
|
/// - Reflected parameters using [_reflectParameter]
|
||||||
|
/// - Getter and setter flags from the mirror
|
||||||
|
/// - Return type, using [dynamic] if the mirror doesn't have a reflected type
|
||||||
|
///
|
||||||
|
/// This setup allows the [_ReflectedMethodMirror] to provide comprehensive
|
||||||
|
/// reflection capabilities for the method, including its signature, metadata,
|
||||||
|
/// and potential invocation (if a [closureMirror] is provided).
|
||||||
|
_ReflectedMethodMirror(this.mirror, [this.closureMirror])
|
||||||
|
: super(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
<ReflectedTypeParameter>[],
|
||||||
|
mirror.metadata
|
||||||
|
.map((mirror) => _ReflectedInstanceMirror(mirror))
|
||||||
|
.toList(),
|
||||||
|
mirror.parameters.map(_reflectParameter).toList(),
|
||||||
|
mirror.isGetter,
|
||||||
|
mirror.isSetter,
|
||||||
|
returnType: !mirror.returnType.hasReflectedType
|
||||||
|
? const MirrorsReflector().reflectType(dynamic)
|
||||||
|
: const MirrorsReflector()
|
||||||
|
.reflectType(mirror.returnType.reflectedType));
|
||||||
|
|
||||||
|
/// Reflects a parameter of a method using dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This static method creates a [ReflectedParameter] instance from a given [dart.ParameterMirror].
|
||||||
|
/// It extracts various properties from the mirror to construct a comprehensive reflection of the parameter.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [mirror]: A [dart.ParameterMirror] representing the parameter to be reflected.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedParameter] instance containing the reflected information of the parameter.
|
||||||
|
///
|
||||||
|
/// The method extracts the following information:
|
||||||
|
/// - Parameter name from the mirror's [simpleName]
|
||||||
|
/// - Metadata (annotations) as [_ReflectedInstanceMirror] objects
|
||||||
|
/// - Parameter type, reflected using [MirrorsReflector]
|
||||||
|
/// - Whether the parameter is required (not optional)
|
||||||
|
/// - Whether the parameter is named
|
||||||
|
///
|
||||||
|
/// This method is typically used internally by the reflection system to create
|
||||||
|
/// parameter reflections for method signatures.
|
||||||
|
static ReflectedParameter _reflectParameter(dart.ParameterMirror mirror) {
|
||||||
|
return ReflectedParameter(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
mirror.metadata
|
||||||
|
.map((mirror) => _ReflectedInstanceMirror(mirror))
|
||||||
|
.toList(),
|
||||||
|
const MirrorsReflector().reflectType(mirror.type.reflectedType),
|
||||||
|
!mirror.isOptional,
|
||||||
|
mirror.isNamed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invokes the reflected method with the given invocation details.
|
||||||
|
///
|
||||||
|
/// This method allows for dynamic invocation of the reflected method using the
|
||||||
|
/// provided [Invocation] object. It requires that a [closureMirror] was provided
|
||||||
|
/// during the construction of this [_ReflectedMethodMirror].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [invocation]: An [Invocation] object containing the details of the method call,
|
||||||
|
/// including the method name, positional arguments, and named arguments.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedInstance] representing the result of the method invocation.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [StateError] if this [_ReflectedMethodMirror] was created without a [closureMirror],
|
||||||
|
/// indicating that direct invocation is not possible.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// var result = reflectedMethod.invoke(Invocation.method(#methodName, [arg1, arg2]));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// This method relies on the presence of a [closureMirror] to perform the actual
|
||||||
|
/// invocation. If no [closureMirror] is available, it means the reflected method
|
||||||
|
/// cannot be directly invoked, and an error will be thrown.
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
if (closureMirror == null) {
|
||||||
|
throw StateError(
|
||||||
|
'This object was reflected without a ClosureMirror, and therefore cannot be directly invoked.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return _ReflectedInstanceMirror(closureMirror!.invoke(invocation.memberName,
|
||||||
|
invocation.positionalArguments, invocation.namedArguments));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
298
incubation/container/container/lib/src/reflector.dart
Normal file
298
incubation/container/container/lib/src/reflector.dart
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:quiver/core.dart';
|
||||||
|
|
||||||
|
/// Abstract class representing a reflector for introspection of Dart types and instances.
|
||||||
|
///
|
||||||
|
/// This class provides methods to reflect on various Dart constructs such as classes,
|
||||||
|
/// functions, types, and instances. It allows for runtime inspection and manipulation
|
||||||
|
/// of code elements.
|
||||||
|
///
|
||||||
|
/// The methods in this class are designed to be implemented by concrete reflector
|
||||||
|
/// classes, potentially using different reflection mechanisms (e.g., mirrors, code
|
||||||
|
/// generation).
|
||||||
|
///
|
||||||
|
/// Note: The `reflectFutureOf` method throws an `UnsupportedError` by default and
|
||||||
|
/// requires `dart:mirrors` for implementation.
|
||||||
|
abstract class Reflector {
|
||||||
|
/// Constructs a new [Reflector] instance.
|
||||||
|
///
|
||||||
|
/// This constructor is declared as `const` to allow for compile-time constant creation
|
||||||
|
/// of [Reflector] instances. Subclasses of [Reflector] may override this constructor
|
||||||
|
/// to provide their own initialization logic if needed.
|
||||||
|
const Reflector();
|
||||||
|
|
||||||
|
String? getName(Symbol symbol);
|
||||||
|
|
||||||
|
ReflectedClass? reflectClass(Type clazz);
|
||||||
|
|
||||||
|
ReflectedFunction? reflectFunction(Function function);
|
||||||
|
|
||||||
|
ReflectedType? reflectType(Type type);
|
||||||
|
|
||||||
|
ReflectedInstance? reflectInstance(Object object);
|
||||||
|
|
||||||
|
ReflectedType reflectFutureOf(Type type) {
|
||||||
|
throw UnsupportedError('`reflectFutureOf` requires `dart:mirrors`.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected instance of an object.
|
||||||
|
///
|
||||||
|
/// This abstract class provides a way to introspect and manipulate object instances
|
||||||
|
/// at runtime. It encapsulates information about the object's type, class, and the
|
||||||
|
/// actual object instance (reflectee).
|
||||||
|
///
|
||||||
|
/// The [type] property represents the reflected type of the instance.
|
||||||
|
/// The [clazz] property represents the reflected class of the instance.
|
||||||
|
/// The [reflectee] property holds the actual object instance being reflected.
|
||||||
|
///
|
||||||
|
/// This class also provides methods for comparing instances and accessing fields.
|
||||||
|
///
|
||||||
|
/// Use the [getField] method to retrieve a reflected instance of a specific field.
|
||||||
|
abstract class ReflectedInstance {
|
||||||
|
final ReflectedType type;
|
||||||
|
final ReflectedClass clazz;
|
||||||
|
final Object? reflectee;
|
||||||
|
|
||||||
|
const ReflectedInstance(this.type, this.clazz, this.reflectee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hash2(type, clazz);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedInstance && other.type == type && other.clazz == clazz;
|
||||||
|
|
||||||
|
ReflectedInstance getField(String name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected type in the Dart language.
|
||||||
|
///
|
||||||
|
/// This abstract class encapsulates information about a Dart type, including its name,
|
||||||
|
/// type parameters, and the actual Dart [Type] it represents.
|
||||||
|
///
|
||||||
|
/// The [name] property holds the name of the type.
|
||||||
|
/// The [typeParameters] list contains the type parameters if the type is generic.
|
||||||
|
/// The [reflectedType] property holds the actual Dart [Type] being reflected.
|
||||||
|
///
|
||||||
|
/// This class provides methods for creating new instances of the type, comparing types,
|
||||||
|
/// and checking type assignability.
|
||||||
|
///
|
||||||
|
/// The [newInstance] method allows for dynamic creation of new instances of the type.
|
||||||
|
/// The [isAssignableTo] method checks if this type is assignable to another type.
|
||||||
|
///
|
||||||
|
/// This class also overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||||
|
abstract class ReflectedType {
|
||||||
|
final String name;
|
||||||
|
final List<ReflectedTypeParameter> typeParameters;
|
||||||
|
final Type reflectedType;
|
||||||
|
|
||||||
|
const ReflectedType(this.name, this.typeParameters, this.reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hash3(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedType &&
|
||||||
|
other.name == name &&
|
||||||
|
const ListEquality<ReflectedTypeParameter>()
|
||||||
|
.equals(other.typeParameters, typeParameters) &&
|
||||||
|
other.reflectedType == reflectedType;
|
||||||
|
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]);
|
||||||
|
|
||||||
|
bool isAssignableTo(ReflectedType? other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected class in the Dart language.
|
||||||
|
///
|
||||||
|
/// This abstract class extends [ReflectedType] and provides additional information
|
||||||
|
/// specific to classes, including annotations, constructors, and declarations.
|
||||||
|
///
|
||||||
|
/// The [annotations] list contains reflected instances of annotations applied to the class.
|
||||||
|
/// The [constructors] list contains reflected functions representing the class constructors.
|
||||||
|
/// The [declarations] list contains reflected declarations (fields, methods, etc.) of the class.
|
||||||
|
///
|
||||||
|
/// This class overrides [hashCode] and [operator ==] to include the additional properties
|
||||||
|
/// in equality comparisons and hash code calculations.
|
||||||
|
abstract class ReflectedClass extends ReflectedType {
|
||||||
|
final List<ReflectedInstance> annotations;
|
||||||
|
final List<ReflectedFunction> constructors;
|
||||||
|
final List<ReflectedDeclaration> declarations;
|
||||||
|
|
||||||
|
const ReflectedClass(
|
||||||
|
String name,
|
||||||
|
List<ReflectedTypeParameter> typeParameters,
|
||||||
|
this.annotations,
|
||||||
|
this.constructors,
|
||||||
|
this.declarations,
|
||||||
|
Type reflectedType)
|
||||||
|
: super(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
hash4(super.hashCode, annotations, constructors, declarations);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedClass &&
|
||||||
|
super == other &&
|
||||||
|
const ListEquality<ReflectedInstance>()
|
||||||
|
.equals(other.annotations, annotations) &&
|
||||||
|
const ListEquality<ReflectedFunction>()
|
||||||
|
.equals(other.constructors, constructors) &&
|
||||||
|
const ListEquality<ReflectedDeclaration>()
|
||||||
|
.equals(other.declarations, declarations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected declaration in the Dart language.
|
||||||
|
///
|
||||||
|
/// This class encapsulates information about a declaration within a class or object,
|
||||||
|
/// such as a method, field, or property.
|
||||||
|
///
|
||||||
|
/// The [name] property holds the name of the declaration.
|
||||||
|
/// The [isStatic] property indicates whether the declaration is static.
|
||||||
|
/// The [function] property, if non-null, represents the reflected function associated
|
||||||
|
/// with this declaration (applicable for methods and some properties).
|
||||||
|
///
|
||||||
|
/// This class provides methods for comparing declarations and calculating hash codes.
|
||||||
|
/// It overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||||
|
class ReflectedDeclaration {
|
||||||
|
final String name;
|
||||||
|
final bool isStatic;
|
||||||
|
final ReflectedFunction? function;
|
||||||
|
|
||||||
|
const ReflectedDeclaration(this.name, this.isStatic, this.function);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hash3(name, isStatic, function);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedDeclaration &&
|
||||||
|
other.name == name &&
|
||||||
|
other.isStatic == isStatic &&
|
||||||
|
other.function == function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected function in the Dart language.
|
||||||
|
///
|
||||||
|
/// This abstract class encapsulates information about a function, including its name,
|
||||||
|
/// type parameters, annotations, return type, parameters, and whether it's a getter or setter.
|
||||||
|
///
|
||||||
|
/// The [name] property holds the name of the function.
|
||||||
|
/// The [typeParameters] list contains the type parameters if the function is generic.
|
||||||
|
/// The [annotations] list contains reflected instances of annotations applied to the function.
|
||||||
|
/// The [returnType] property represents the function's return type (if applicable).
|
||||||
|
/// The [parameters] list contains the function's parameters.
|
||||||
|
/// The [isGetter] and [isSetter] properties indicate if the function is a getter or setter.
|
||||||
|
///
|
||||||
|
/// This class provides methods for comparing functions and calculating hash codes.
|
||||||
|
/// It also includes an [invoke] method for dynamically calling the function.
|
||||||
|
///
|
||||||
|
/// This class overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||||
|
abstract class ReflectedFunction {
|
||||||
|
final String name;
|
||||||
|
final List<ReflectedTypeParameter> typeParameters;
|
||||||
|
final List<ReflectedInstance> annotations;
|
||||||
|
final ReflectedType? returnType;
|
||||||
|
final List<ReflectedParameter> parameters;
|
||||||
|
final bool isGetter, isSetter;
|
||||||
|
|
||||||
|
const ReflectedFunction(this.name, this.typeParameters, this.annotations,
|
||||||
|
this.parameters, this.isGetter, this.isSetter,
|
||||||
|
{this.returnType});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashObjects([
|
||||||
|
name,
|
||||||
|
typeParameters,
|
||||||
|
annotations,
|
||||||
|
returnType,
|
||||||
|
parameters,
|
||||||
|
isGetter,
|
||||||
|
isSetter
|
||||||
|
]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedFunction &&
|
||||||
|
other.name == name &&
|
||||||
|
const ListEquality<ReflectedTypeParameter>()
|
||||||
|
.equals(other.typeParameters, typeParameters) &&
|
||||||
|
const ListEquality<ReflectedInstance>()
|
||||||
|
.equals(other.annotations, annotations) &&
|
||||||
|
other.returnType == returnType &&
|
||||||
|
const ListEquality<ReflectedParameter>()
|
||||||
|
.equals(other.parameters, other.parameters) &&
|
||||||
|
other.isGetter == isGetter &&
|
||||||
|
other.isSetter == isSetter;
|
||||||
|
|
||||||
|
ReflectedInstance invoke(Invocation invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reflected parameter in the Dart language.
|
||||||
|
///
|
||||||
|
/// This class encapsulates information about a function or method parameter,
|
||||||
|
/// including its name, annotations, type, and properties such as whether it's
|
||||||
|
/// required or named.
|
||||||
|
///
|
||||||
|
/// Properties:
|
||||||
|
/// - [name]: The name of the parameter.
|
||||||
|
/// - [annotations]: A list of reflected instances of annotations applied to the parameter.
|
||||||
|
/// - [type]: The reflected type of the parameter.
|
||||||
|
/// - [isRequired]: Indicates whether the parameter is required.
|
||||||
|
/// - [isNamed]: Indicates whether the parameter is a named parameter.
|
||||||
|
///
|
||||||
|
/// This class provides methods for comparing parameters and calculating hash codes.
|
||||||
|
/// It overrides [hashCode] and [operator ==] for proper equality comparisons.
|
||||||
|
class ReflectedParameter {
|
||||||
|
final String name;
|
||||||
|
final List<ReflectedInstance> annotations;
|
||||||
|
final ReflectedType type;
|
||||||
|
final bool isRequired;
|
||||||
|
final bool isNamed;
|
||||||
|
|
||||||
|
const ReflectedParameter(
|
||||||
|
this.name, this.annotations, this.type, this.isRequired, this.isNamed);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
hashObjects([name, annotations, type, isRequired, isNamed]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedParameter &&
|
||||||
|
other.name == name &&
|
||||||
|
const ListEquality<ReflectedInstance>()
|
||||||
|
.equals(other.annotations, annotations) &&
|
||||||
|
other.type == type &&
|
||||||
|
other.isRequired == isRequired &&
|
||||||
|
other.isNamed == isNamed;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReflectedTypeParameter {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
const ReflectedTypeParameter(this.name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashObjects([name]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ReflectedTypeParameter && other.name == name;
|
||||||
|
}
|
179
incubation/container/container/lib/src/static/static.dart
Normal file
179
incubation/container/container/lib/src/static/static.dart
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
|
||||||
|
/// A static implementation of the [Reflector] class that performs simple [Map] lookups.
|
||||||
|
///
|
||||||
|
/// `package:platform_container_generator` uses this to create reflectors from analysis metadata.
|
||||||
|
class StaticReflector extends Reflector {
|
||||||
|
/// A map that associates [Symbol] objects with their corresponding string names.
|
||||||
|
///
|
||||||
|
/// This map is used to store and retrieve the string representations of symbols,
|
||||||
|
/// which can be useful for reflection and debugging purposes.
|
||||||
|
final Map<Symbol, String> names;
|
||||||
|
|
||||||
|
/// A map that associates [Type] objects with their corresponding [ReflectedType] objects.
|
||||||
|
///
|
||||||
|
/// This map is used to store and retrieve reflection information for different types,
|
||||||
|
/// allowing for runtime introspection of type metadata and structure.
|
||||||
|
final Map<Type, ReflectedType> types;
|
||||||
|
|
||||||
|
/// A map that associates [Function] objects with their corresponding [ReflectedFunction] objects.
|
||||||
|
///
|
||||||
|
/// This map is used to store and retrieve reflection information for functions,
|
||||||
|
/// enabling runtime introspection of function metadata, parameters, and return types.
|
||||||
|
final Map<Function, ReflectedFunction> functions;
|
||||||
|
|
||||||
|
/// A map that associates [Object] instances with their corresponding [ReflectedInstance] objects.
|
||||||
|
///
|
||||||
|
/// This map is used to store and retrieve reflection information for specific object instances,
|
||||||
|
/// allowing for runtime introspection of object properties, methods, and metadata.
|
||||||
|
final Map<Object, ReflectedInstance> instances;
|
||||||
|
|
||||||
|
/// Creates a new [StaticReflector] instance with optional parameters.
|
||||||
|
///
|
||||||
|
/// The [StaticReflector] constructor allows you to initialize the reflector
|
||||||
|
/// with pre-populated maps for names, types, functions, and instances.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [names]: A map of [Symbol] to [String] for symbol name lookups. Defaults to an empty map.
|
||||||
|
/// - [types]: A map of [Type] to [ReflectedType] for type reflection. Defaults to an empty map.
|
||||||
|
/// - [functions]: A map of [Function] to [ReflectedFunction] for function reflection. Defaults to an empty map.
|
||||||
|
/// - [instances]: A map of [Object] to [ReflectedInstance] for instance reflection. Defaults to an empty map.
|
||||||
|
///
|
||||||
|
/// All parameters are optional and default to empty constant maps if not provided.
|
||||||
|
const StaticReflector(
|
||||||
|
{this.names = const {},
|
||||||
|
this.types = const {},
|
||||||
|
this.functions = const {},
|
||||||
|
this.instances = const {}});
|
||||||
|
|
||||||
|
/// Returns the string name associated with the given [Symbol].
|
||||||
|
///
|
||||||
|
/// This method looks up the string representation of the provided [symbol]
|
||||||
|
/// in the [names] map. If the symbol is found, its corresponding string
|
||||||
|
/// name is returned. If the symbol is not found in the map, an [ArgumentError]
|
||||||
|
/// is thrown.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [symbol]: The [Symbol] for which to retrieve the string name.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The string name associated with the given [symbol], or null if not found.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError]: If the provided [symbol] is not found in the [names] map.
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) {
|
||||||
|
if (!names.containsKey(symbol)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'The value of $symbol is unknown - it was not generated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[symbol];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects a class based on its [Type].
|
||||||
|
///
|
||||||
|
/// This method attempts to reflect the given class [Type] by calling [reflectType]
|
||||||
|
/// and casting the result to [ReflectedClass]. If the reflection is successful
|
||||||
|
/// and the result is a [ReflectedClass], it is returned. Otherwise, null is returned.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [clazz]: The [Type] of the class to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedClass] instance if the reflection is successful and the result
|
||||||
|
/// is a [ReflectedClass], or null otherwise.
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) =>
|
||||||
|
reflectType(clazz) as ReflectedClass?;
|
||||||
|
|
||||||
|
/// Reflects a function based on its [Function] object.
|
||||||
|
///
|
||||||
|
/// This method attempts to retrieve reflection information for the given [function]
|
||||||
|
/// from the [functions] map. If the function is found in the map, its corresponding
|
||||||
|
/// [ReflectedFunction] object is returned. If the function is not found, an
|
||||||
|
/// [ArgumentError] is thrown.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [function]: The [Function] object to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedFunction] object containing reflection information about the
|
||||||
|
/// given function, or null if not found.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError]: If there is no reflection information available for
|
||||||
|
/// the given [function].
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) {
|
||||||
|
if (!functions.containsKey(function)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'There is no reflection information available about $function.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return functions[function];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects an object instance to retrieve its reflection information.
|
||||||
|
///
|
||||||
|
/// This method attempts to retrieve reflection information for the given [object]
|
||||||
|
/// from the [instances] map. If the object is found in the map, its corresponding
|
||||||
|
/// [ReflectedInstance] object is returned. If the object is not found, an
|
||||||
|
/// [ArgumentError] is thrown.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [object]: The object instance to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedInstance] object containing reflection information about the
|
||||||
|
/// given object instance, or null if not found.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError]: If there is no reflection information available for
|
||||||
|
/// the given [object].
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object object) {
|
||||||
|
if (!instances.containsKey(object)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'There is no reflection information available about $object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return instances[object];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflects a type to retrieve its reflection information.
|
||||||
|
///
|
||||||
|
/// This method attempts to retrieve reflection information for the given [type]
|
||||||
|
/// from the [types] map. If the type is found in the map, its corresponding
|
||||||
|
/// [ReflectedType] object is returned. If the type is not found, an
|
||||||
|
/// [ArgumentError] is thrown.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: The [Type] to reflect.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [ReflectedType] object containing reflection information about the
|
||||||
|
/// given type, or null if not found.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [ArgumentError]: If there is no reflection information available for
|
||||||
|
/// the given [type].
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) {
|
||||||
|
if (!types.containsKey(type)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'There is no reflection information available about $type.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return types[type];
|
||||||
|
}
|
||||||
|
}
|
93
incubation/container/container/lib/src/throwing.dart
Normal file
93
incubation/container/container/lib/src/throwing.dart
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import 'package:platformed_container/src/container_const.dart';
|
||||||
|
import 'empty/empty.dart';
|
||||||
|
import 'reflector.dart';
|
||||||
|
|
||||||
|
/// A [Reflector] implementation that throws exceptions on all attempts
|
||||||
|
/// to perform reflection.
|
||||||
|
///
|
||||||
|
/// Use this in contexts where you know you won't need any reflective capabilities.
|
||||||
|
class ThrowingReflector extends Reflector {
|
||||||
|
/// The error message to give the end user when an [UnsupportedError] is thrown.
|
||||||
|
final String errorMessage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
static const String defaultErrorMessage =
|
||||||
|
'You attempted to perform a reflective action, but you are using `ThrowingReflector`, '
|
||||||
|
'a class which disables reflection. Consider using the `MirrorsReflector` '
|
||||||
|
'class if you need reflection.';
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Creates a [ThrowingReflector] instance.
|
||||||
|
///
|
||||||
|
/// [errorMessage] is the message to be used when throwing an [UnsupportedError].
|
||||||
|
/// If not provided, it defaults to [ContainerConst.defaultErrorMessage].
|
||||||
|
const ThrowingReflector(
|
||||||
|
{this.errorMessage = ContainerConst.defaultErrorMessage});
|
||||||
|
|
||||||
|
/// Retrieves the name associated with the given [symbol].
|
||||||
|
///
|
||||||
|
/// This method delegates the task to an instance of [EmptyReflector].
|
||||||
|
/// It returns the name as a [String] if found, or `null` if not found.
|
||||||
|
///
|
||||||
|
/// [symbol] is the [Symbol] for which to retrieve the name.
|
||||||
|
///
|
||||||
|
/// Returns a [String] representing the name of the symbol, or `null` if not found.
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => const EmptyReflector().getName(symbol);
|
||||||
|
|
||||||
|
/// Creates and returns an [UnsupportedError] with the specified [errorMessage].
|
||||||
|
///
|
||||||
|
/// This method is used internally to generate consistent error messages
|
||||||
|
/// when reflection operations are attempted on this [ThrowingReflector].
|
||||||
|
///
|
||||||
|
/// Returns an [UnsupportedError] instance with the configured error message.
|
||||||
|
UnsupportedError _error() => UnsupportedError(errorMessage);
|
||||||
|
|
||||||
|
/// Reflects on a given class type and throws an [UnsupportedError].
|
||||||
|
///
|
||||||
|
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||||
|
/// to prevent reflective operations. When called, it throws an [UnsupportedError]
|
||||||
|
/// with the configured error message.
|
||||||
|
///
|
||||||
|
/// [clazz] is the [Type] of the class to reflect on.
|
||||||
|
///
|
||||||
|
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||||
|
@override
|
||||||
|
ReflectedClass reflectClass(Type clazz) => throw _error();
|
||||||
|
|
||||||
|
/// Reflects on a given object instance and throws an [UnsupportedError].
|
||||||
|
///
|
||||||
|
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||||
|
/// to prevent reflective operations on object instances. When called, it throws
|
||||||
|
/// an [UnsupportedError] with the configured error message.
|
||||||
|
///
|
||||||
|
/// [object] is the object instance to reflect on.
|
||||||
|
///
|
||||||
|
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||||
|
@override
|
||||||
|
ReflectedInstance reflectInstance(Object object) => throw _error();
|
||||||
|
|
||||||
|
/// Reflects on a given type and throws an [UnsupportedError].
|
||||||
|
///
|
||||||
|
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||||
|
/// to prevent reflective operations on types. When called, it throws an
|
||||||
|
/// [UnsupportedError] with the configured error message.
|
||||||
|
///
|
||||||
|
/// [type] is the [Type] to reflect on.
|
||||||
|
///
|
||||||
|
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||||
|
@override
|
||||||
|
ReflectedType reflectType(Type type) => throw _error();
|
||||||
|
|
||||||
|
/// Reflects on a given function and throws an [UnsupportedError].
|
||||||
|
///
|
||||||
|
/// This method is part of the [ThrowingReflector] implementation and is designed
|
||||||
|
/// to prevent reflective operations on functions. When called, it throws an
|
||||||
|
/// [UnsupportedError] with the configured error message.
|
||||||
|
///
|
||||||
|
/// [function] is the [Function] to reflect on.
|
||||||
|
///
|
||||||
|
/// Throws an [UnsupportedError] when invoked, as reflection is not supported.
|
||||||
|
@override
|
||||||
|
ReflectedFunction reflectFunction(Function function) => throw _error();
|
||||||
|
}
|
14
incubation/container/container/pubspec.yaml
Normal file
14
incubation/container/container/pubspec.yaml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: platformed_container
|
||||||
|
version: 9.0.0
|
||||||
|
description: Protevus Platform hierarchical DI container, and pluggable backends for reflection.
|
||||||
|
homepage: https://protevus.com
|
||||||
|
documentation: https://docs.protevus.com
|
||||||
|
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/container/container
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
dependencies:
|
||||||
|
collection: ^1.19.1
|
||||||
|
quiver: ^3.2.2
|
||||||
|
dev_dependencies:
|
||||||
|
test: ^1.25.8
|
||||||
|
lints: ^4.0.0
|
122
incubation/container/container/test/common.dart
Normal file
122
incubation/container/container/test/common.dart
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void returnVoidFromAFunction(int x) {}
|
||||||
|
|
||||||
|
void testReflector(Reflector reflector) {
|
||||||
|
var blaziken = Pokemon('Blaziken', PokemonType.fire);
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(reflector);
|
||||||
|
container.registerSingleton(blaziken);
|
||||||
|
container.registerFactory<Future<int>>((_) async => 46);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get field', () {
|
||||||
|
var blazikenMirror = reflector.reflectInstance(blaziken)!;
|
||||||
|
expect(blazikenMirror.getField('type').reflectee, blaziken.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectFunction', () {
|
||||||
|
var mirror = reflector.reflectFunction(returnVoidFromAFunction);
|
||||||
|
|
||||||
|
test('void return type returns dynamic', () {
|
||||||
|
expect(mirror!.returnType, reflector.reflectType(dynamic));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts parameters', () {
|
||||||
|
expect(mirror!.parameters, hasLength(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts types parameters', () {
|
||||||
|
expect(mirror!.typeParameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('correctly reflects parameter types', () {
|
||||||
|
var p = mirror!.parameters[0];
|
||||||
|
expect(p.name, 'x');
|
||||||
|
expect(p.isRequired, true);
|
||||||
|
expect(p.isNamed, false);
|
||||||
|
expect(p.annotations, isEmpty);
|
||||||
|
expect(p.type, reflector.reflectType(int));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make on singleton type returns singleton', () {
|
||||||
|
expect(container.make(Pokemon), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make with generic returns same as make with explicit type', () {
|
||||||
|
expect(container.make<Pokemon>(), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make async returns async object', () async {
|
||||||
|
expect(container.makeAsync<int>(), completion(46));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make async returns sync object', () async {
|
||||||
|
expect(container.makeAsync<Pokemon>(), completion(blaziken));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make on aliased singleton returns singleton', () {
|
||||||
|
container.registerSingleton(blaziken, as: StateError);
|
||||||
|
expect(container.make(StateError), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('constructor injects singleton', () {
|
||||||
|
var lower = container.make<LowerPokemon>();
|
||||||
|
expect(lower.lowercaseName, blaziken.name.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('newInstance works', () {
|
||||||
|
var type = container.reflector.reflectType(Pokemon)!;
|
||||||
|
var instance =
|
||||||
|
type.newInstance('changeName', [blaziken, 'Charizard']).reflectee
|
||||||
|
as Pokemon;
|
||||||
|
print(instance);
|
||||||
|
expect(instance.name, 'Charizard');
|
||||||
|
expect(instance.type, PokemonType.fire);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssignableTo', () {
|
||||||
|
var pokemonType = container.reflector.reflectType(Pokemon);
|
||||||
|
var kantoPokemonType = container.reflector.reflectType(KantoPokemon)!;
|
||||||
|
|
||||||
|
expect(kantoPokemonType.isAssignableTo(pokemonType), true);
|
||||||
|
expect(
|
||||||
|
kantoPokemonType
|
||||||
|
.isAssignableTo(container.reflector.reflectType(String)),
|
||||||
|
false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LowerPokemon {
|
||||||
|
final Pokemon pokemon;
|
||||||
|
|
||||||
|
LowerPokemon(this.pokemon);
|
||||||
|
|
||||||
|
String get lowercaseName => pokemon.name.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pokemon {
|
||||||
|
final String name;
|
||||||
|
final PokemonType type;
|
||||||
|
|
||||||
|
Pokemon(this.name, this.type);
|
||||||
|
|
||||||
|
factory Pokemon.changeName(Pokemon other, String name) {
|
||||||
|
return Pokemon(name, other.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'NAME: $name, TYPE: $type';
|
||||||
|
}
|
||||||
|
|
||||||
|
class KantoPokemon extends Pokemon {
|
||||||
|
KantoPokemon(super.name, super.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PokemonType { water, fire, grass, ice, poison, flying }
|
257
incubation/container/container/test/contextual_binding_test.dart
Normal file
257
incubation/container/container/test/contextual_binding_test.dart
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
// Test interfaces and implementations
|
||||||
|
abstract class Logger {
|
||||||
|
void log(String message);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileLogger implements Logger {
|
||||||
|
final String filename;
|
||||||
|
FileLogger(this.filename);
|
||||||
|
@override
|
||||||
|
void log(String message) => print('File($filename): $message');
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsoleLogger implements Logger {
|
||||||
|
@override
|
||||||
|
void log(String message) => print('Console: $message');
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggerClient {
|
||||||
|
final Logger logger;
|
||||||
|
LoggerClient(this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpecialLoggerClient {
|
||||||
|
final Logger logger;
|
||||||
|
SpecialLoggerClient(this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Contextual Binding Tests', () {
|
||||||
|
test('basic contextual binding resolves correctly', () {
|
||||||
|
// Register default binding
|
||||||
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
|
||||||
|
// Register contextual binding
|
||||||
|
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
||||||
|
|
||||||
|
// The default binding should be used here
|
||||||
|
var logger = container.make<Logger>();
|
||||||
|
expect(logger, isA<ConsoleLogger>());
|
||||||
|
|
||||||
|
// The contextual binding should be used here
|
||||||
|
var client = container.make<LoggerClient>();
|
||||||
|
expect(client.logger, isA<FileLogger>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('multiple contextual bindings work independently', () {
|
||||||
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
|
||||||
|
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
||||||
|
container.when(SpecialLoggerClient).needs<Logger>().give<ConsoleLogger>();
|
||||||
|
|
||||||
|
var client1 = container.make<LoggerClient>();
|
||||||
|
var client2 = container.make<SpecialLoggerClient>();
|
||||||
|
|
||||||
|
expect(client1.logger, isA<FileLogger>());
|
||||||
|
expect(client2.logger, isA<ConsoleLogger>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('contextual binding with factory function works', () {
|
||||||
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
|
||||||
|
container
|
||||||
|
.when(LoggerClient)
|
||||||
|
.needs<Logger>()
|
||||||
|
.giveFactory((container) => FileLogger('test.log'));
|
||||||
|
|
||||||
|
var client = container.make<LoggerClient>();
|
||||||
|
expect(client.logger, isA<FileLogger>());
|
||||||
|
expect((client.logger as FileLogger).filename, equals('test.log'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('contextual binding throws when implementation not found', () {
|
||||||
|
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => container.make<LoggerClient>(),
|
||||||
|
throwsA(isA<BindingResolutionException>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('contextual bindings are inherited by child containers', () {
|
||||||
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
var client = childContainer.make<LoggerClient>();
|
||||||
|
|
||||||
|
expect(client.logger, isA<FileLogger>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can override parent contextual binding', () {
|
||||||
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer
|
||||||
|
.when(LoggerClient)
|
||||||
|
.needs<Logger>()
|
||||||
|
.giveFactory((container) => FileLogger('child.log'));
|
||||||
|
|
||||||
|
var client = childContainer.make<LoggerClient>();
|
||||||
|
expect(client.logger, isA<FileLogger>());
|
||||||
|
expect((client.logger as FileLogger).filename, equals('child.log'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock reflector implementation for testing
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) {
|
||||||
|
if (type == LoggerClient) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'LoggerClient',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('logger', Logger)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => LoggerClient(positional[0]),
|
||||||
|
);
|
||||||
|
} else if (type == SpecialLoggerClient) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'SpecialLoggerClient',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('logger', Logger)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) =>
|
||||||
|
SpecialLoggerClient(positional[0]),
|
||||||
|
);
|
||||||
|
} else if (type == FileLogger) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'FileLogger',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('filename', String)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => FileLogger(positional[0]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedClass extends ReflectedClass {
|
||||||
|
final Function instanceBuilder;
|
||||||
|
|
||||||
|
MockReflectedClass(
|
||||||
|
String name,
|
||||||
|
List<ReflectedTypeParameter> typeParameters,
|
||||||
|
List<ReflectedInstance> annotations,
|
||||||
|
List<ReflectedFunction> constructors,
|
||||||
|
List<ReflectedDeclaration> declarations,
|
||||||
|
Type reflectedType,
|
||||||
|
this.instanceBuilder,
|
||||||
|
) : super(name, typeParameters, annotations, constructors, declarations,
|
||||||
|
reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
var instance = instanceBuilder(
|
||||||
|
constructorName, positionalArguments, namedArguments, typeArguments);
|
||||||
|
return MockReflectedInstance(this, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedInstance extends ReflectedInstance {
|
||||||
|
MockReflectedInstance(ReflectedClass clazz, Object? reflectee)
|
||||||
|
: super(clazz, clazz, reflectee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockConstructor extends ReflectedFunction {
|
||||||
|
final List<ReflectedParameter> params;
|
||||||
|
|
||||||
|
MockConstructor(this.params)
|
||||||
|
: super('', [], [], params, false, false,
|
||||||
|
returnType: MockReflectedType('void', [], dynamic));
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockParameter extends ReflectedParameter {
|
||||||
|
MockParameter(String name, Type type)
|
||||||
|
: super(name, [], MockReflectedType(type.toString(), [], type), true,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedType extends ReflectedType {
|
||||||
|
MockReflectedType(String name, List<ReflectedTypeParameter> typeParameters,
|
||||||
|
Type reflectedType)
|
||||||
|
: super(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
138
incubation/container/container/test/empty_reflector_test.dart
Normal file
138
incubation/container/container/test/empty_reflector_test.dart
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var reflector = const EmptyReflector();
|
||||||
|
|
||||||
|
test('getName', () {
|
||||||
|
expect(reflector.getName(#foo), 'foo');
|
||||||
|
expect(reflector.getName(#==), '==');
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectClass', () {
|
||||||
|
var mirror = reflector.reflectClass(Truck);
|
||||||
|
|
||||||
|
test('name returns empty', () {
|
||||||
|
expect(mirror.name, '(empty)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('annotations returns empty', () {
|
||||||
|
expect(mirror.annotations, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('typeParameters returns empty', () {
|
||||||
|
expect(mirror.typeParameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('declarations returns empty', () {
|
||||||
|
expect(mirror.declarations, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('constructors returns empty', () {
|
||||||
|
expect(mirror.constructors, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectedType returns Object', () {
|
||||||
|
expect(mirror.reflectedType, Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot call newInstance', () {
|
||||||
|
expect(() => mirror.newInstance('', []), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssignableTo self', () {
|
||||||
|
expect(mirror.isAssignableTo(mirror), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectType', () {
|
||||||
|
var mirror = reflector.reflectType(Truck);
|
||||||
|
|
||||||
|
test('name returns empty', () {
|
||||||
|
expect(mirror.name, '(empty)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('typeParameters returns empty', () {
|
||||||
|
expect(mirror.typeParameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectedType returns Object', () {
|
||||||
|
expect(mirror.reflectedType, Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot call newInstance', () {
|
||||||
|
expect(() => mirror.newInstance('', []), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssignableTo self', () {
|
||||||
|
expect(mirror.isAssignableTo(mirror), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectFunction', () {
|
||||||
|
void doIt(int x) {}
|
||||||
|
|
||||||
|
var mirror = reflector.reflectFunction(doIt);
|
||||||
|
|
||||||
|
test('name returns empty', () {
|
||||||
|
expect(mirror.name, '(empty)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('annotations returns empty', () {
|
||||||
|
expect(mirror.annotations, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('typeParameters returns empty', () {
|
||||||
|
expect(mirror.typeParameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parameters returns empty', () {
|
||||||
|
expect(mirror.parameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('return type is dynamic', () {
|
||||||
|
expect(mirror.returnType, reflector.reflectType(dynamic));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isGetter returns false', () {
|
||||||
|
expect(mirror.isGetter, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isSetter returns false', () {
|
||||||
|
expect(mirror.isSetter, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot invoke', () {
|
||||||
|
var invocation = Invocation.method(#drive, []);
|
||||||
|
expect(() => mirror.invoke(invocation), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectInstance', () {
|
||||||
|
var mirror = reflector.reflectInstance(Truck());
|
||||||
|
|
||||||
|
test('reflectee returns null', () {
|
||||||
|
expect(mirror.reflectee, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('type returns empty', () {
|
||||||
|
expect(mirror.type.name, '(empty)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clazz returns empty', () {
|
||||||
|
expect(mirror.clazz.name, '(empty)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot getField', () {
|
||||||
|
expect(() => mirror.getField('wheelCount'), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Truck {
|
||||||
|
int get wheelCount => 4;
|
||||||
|
|
||||||
|
void drive() {
|
||||||
|
print('Vroom!!!');
|
||||||
|
}
|
||||||
|
}
|
51
incubation/container/container/test/has_test.dart
Normal file
51
incubation/container/container/test/has_test.dart
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(const EmptyReflector())
|
||||||
|
..registerSingleton<Song>(Song(title: 'I Wish'))
|
||||||
|
..registerNamedSingleton('foo', 1)
|
||||||
|
..registerFactory<Artist>((container) {
|
||||||
|
return Artist(
|
||||||
|
name: 'Stevie Wonder',
|
||||||
|
song: container.make<Song>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hasNamed', () {
|
||||||
|
var child = container.createChild()..registerNamedSingleton('bar', 2);
|
||||||
|
expect(child.hasNamed('foo'), true);
|
||||||
|
expect(child.hasNamed('bar'), true);
|
||||||
|
expect(child.hasNamed('baz'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has on singleton', () {
|
||||||
|
var result = container.has<Song>();
|
||||||
|
expect(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has on factory', () {
|
||||||
|
expect(container.has<Artist>(), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false if neither', () {
|
||||||
|
expect(container.has<bool>(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Artist {
|
||||||
|
final String? name;
|
||||||
|
final Song? song;
|
||||||
|
|
||||||
|
Artist({this.name, this.song});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Song {
|
||||||
|
final String? title;
|
||||||
|
|
||||||
|
Song({this.title});
|
||||||
|
}
|
18
incubation/container/container/test/lazy_test.dart
Normal file
18
incubation/container/container/test/lazy_test.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('returns the same instance', () {
|
||||||
|
var container = Container(const EmptyReflector())
|
||||||
|
..registerLazySingleton<Dummy>((_) => Dummy('a'));
|
||||||
|
|
||||||
|
var first = container.make<Dummy>();
|
||||||
|
expect(container.make<Dummy>(), first);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dummy {
|
||||||
|
final String s;
|
||||||
|
|
||||||
|
Dummy(this.s);
|
||||||
|
}
|
94
incubation/container/container/test/method_binding_test.dart
Normal file
94
incubation/container/container/test/method_binding_test.dart
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
class Calculator {
|
||||||
|
int add(int a, int b) => a + b;
|
||||||
|
int multiply(int a, int b) => a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Method Binding Tests', () {
|
||||||
|
test('can bind and call method', () {
|
||||||
|
var calculator = Calculator();
|
||||||
|
container.bindMethod('add', calculator.add);
|
||||||
|
|
||||||
|
var result = container.callMethod('add', [5, 3]);
|
||||||
|
expect(result, equals(8));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can bind multiple methods', () {
|
||||||
|
var calculator = Calculator();
|
||||||
|
container.bindMethod('add', calculator.add);
|
||||||
|
container.bindMethod('multiply', calculator.multiply);
|
||||||
|
|
||||||
|
expect(container.callMethod('add', [5, 3]), equals(8));
|
||||||
|
expect(container.callMethod('multiply', [5, 3]), equals(15));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when method not found', () {
|
||||||
|
expect(
|
||||||
|
() => container.callMethod('nonexistent'),
|
||||||
|
throwsA(isA<StateError>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when binding duplicate method', () {
|
||||||
|
var calculator = Calculator();
|
||||||
|
container.bindMethod('add', calculator.add);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => container.bindMethod('add', calculator.add),
|
||||||
|
throwsA(isA<StateError>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container inherits parent methods', () {
|
||||||
|
var calculator = Calculator();
|
||||||
|
container.bindMethod('add', calculator.add);
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
expect(childContainer.callMethod('add', [5, 3]), equals(8));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can override parent methods', () {
|
||||||
|
var calculator = Calculator();
|
||||||
|
container.bindMethod('add', calculator.add);
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.bindMethod('add', (a, b) => a * b); // Override to multiply
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.callMethod('add', [5, 3]), equals(8)); // Parent unchanged
|
||||||
|
expect(childContainer.callMethod('add', [5, 3]),
|
||||||
|
equals(15)); // Child overridden
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal mock reflector for method binding tests
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
}
|
26
incubation/container/container/test/mirrors_test.dart
Normal file
26
incubation/container/container/test/mirrors_test.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:platformed_container/mirrors.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testReflector(const MirrorsReflector());
|
||||||
|
|
||||||
|
test('futureOf', () {
|
||||||
|
var r = MirrorsReflector();
|
||||||
|
var fStr = r.reflectFutureOf(String);
|
||||||
|
expect(fStr.reflectedType.toString(), 'Future<String>');
|
||||||
|
// expect(fStr.reflectedType, Future<String>.value(null).runtimeType);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('concrete future make', () async {
|
||||||
|
var c = Container(MirrorsReflector());
|
||||||
|
c.registerFactory<Future<String>>((_) async => 'hey');
|
||||||
|
var fStr = c.reflector.reflectFutureOf(String);
|
||||||
|
var s1 = await c.make(fStr.reflectedType);
|
||||||
|
var s2 = await c.makeAsync(String);
|
||||||
|
print([s1, s2]);
|
||||||
|
expect(s1, s2);
|
||||||
|
});
|
||||||
|
}
|
34
incubation/container/container/test/named_test.dart
Normal file
34
incubation/container/container/test/named_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(const EmptyReflector());
|
||||||
|
container.registerNamedSingleton('foo', Foo(bar: 'baz'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fetch by name', () {
|
||||||
|
expect(container.findByName<Foo>('foo').bar, 'baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot redefine', () {
|
||||||
|
expect(() => container.registerNamedSingleton('foo', Foo(bar: 'quux')),
|
||||||
|
throwsStateError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws on unknown name', () {
|
||||||
|
expect(() => container.findByName('bar'), throwsStateError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws on incorrect type', () {
|
||||||
|
expect(() => container.findByName<List<String>>('foo'), throwsA(anything));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
final String? bar;
|
||||||
|
|
||||||
|
Foo({this.bar});
|
||||||
|
}
|
322
incubation/container/container/test/scoped_test.dart
Normal file
322
incubation/container/container/test/scoped_test.dart
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
class RequestScope {
|
||||||
|
final String id;
|
||||||
|
RequestScope(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserService {
|
||||||
|
final RequestScope scope;
|
||||||
|
UserService(this.scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
final RequestScope scope;
|
||||||
|
OrderService(this.scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Scoped Instance Tests', () {
|
||||||
|
test('scoped instances are shared within scope', () {
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-1'));
|
||||||
|
container.registerFactory<UserService>(
|
||||||
|
(c) => UserService(c.make<RequestScope>()));
|
||||||
|
container.registerFactory<OrderService>(
|
||||||
|
(c) => OrderService(c.make<RequestScope>()));
|
||||||
|
|
||||||
|
var userService = container.make<UserService>();
|
||||||
|
var orderService = container.make<OrderService>();
|
||||||
|
|
||||||
|
expect(userService.scope, same(orderService.scope));
|
||||||
|
expect(userService.scope.id, equals('request-1'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scoped instances are cleared after clearScoped', () {
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-1'));
|
||||||
|
var scope1 = container.make<RequestScope>();
|
||||||
|
|
||||||
|
container.clearScoped();
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-2'));
|
||||||
|
var scope2 = container.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(scope1.id, equals('request-1'));
|
||||||
|
expect(scope2.id, equals('request-2'));
|
||||||
|
expect(scope1, isNot(same(scope2)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container inherits parent scoped instances', () {
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-1'));
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
|
||||||
|
var parentScope = container.make<RequestScope>();
|
||||||
|
var childScope = childContainer.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(parentScope, same(childScope));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can override parent scoped instances', () {
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('parent-request'));
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.scoped<RequestScope>((c) => RequestScope('child-request'));
|
||||||
|
|
||||||
|
var parentScope = container.make<RequestScope>();
|
||||||
|
var childScope = childContainer.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(parentScope.id, equals('parent-request'));
|
||||||
|
expect(childScope.id, equals('child-request'));
|
||||||
|
expect(parentScope, isNot(same(childScope)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clearing parent scoped instances affects child containers', () {
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-1'));
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
|
||||||
|
var beforeClear = childContainer.make<RequestScope>();
|
||||||
|
container.clearScoped();
|
||||||
|
container.scoped<RequestScope>((c) => RequestScope('request-2'));
|
||||||
|
var afterClear = childContainer.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(beforeClear.id, equals('request-1'));
|
||||||
|
expect(afterClear.id, equals('request-2'));
|
||||||
|
expect(beforeClear, isNot(same(afterClear)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Resolution Lifecycle Tests', () {
|
||||||
|
test('before resolving callbacks are called', () {
|
||||||
|
var callLog = <String>[];
|
||||||
|
container.beforeResolving<RequestScope>((type, args, container) {
|
||||||
|
callLog.add('before:${type.toString()}');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.registerSingleton(RequestScope('test'));
|
||||||
|
container.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callLog, contains('before:RequestScope'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolving callbacks are called', () {
|
||||||
|
var callLog = <String>[];
|
||||||
|
container.resolving<RequestScope>((instance, container) {
|
||||||
|
callLog.add('resolving:${(instance as RequestScope).id}');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.registerSingleton(RequestScope('test'));
|
||||||
|
container.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callLog, contains('resolving:test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('after resolving callbacks are called', () {
|
||||||
|
var callLog = <String>[];
|
||||||
|
container.afterResolving<RequestScope>((instance, container) {
|
||||||
|
callLog.add('after:${(instance as RequestScope).id}');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.registerSingleton(RequestScope('test'));
|
||||||
|
container.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callLog, contains('after:test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('callbacks are called in correct order', () {
|
||||||
|
var callOrder = <String>[];
|
||||||
|
|
||||||
|
container.beforeResolving<RequestScope>((type, args, container) {
|
||||||
|
callOrder.add('before');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.resolving<RequestScope>((instance, container) {
|
||||||
|
callOrder.add('resolving');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.afterResolving<RequestScope>((instance, container) {
|
||||||
|
callOrder.add('after');
|
||||||
|
});
|
||||||
|
|
||||||
|
container.registerSingleton(RequestScope('test'));
|
||||||
|
container.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callOrder, orderedEquals(['before', 'resolving', 'after']));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container inherits parent callbacks', () {
|
||||||
|
var callLog = <String>[];
|
||||||
|
container.beforeResolving<RequestScope>((type, args, container) {
|
||||||
|
callLog.add('parent-before');
|
||||||
|
});
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.registerSingleton(RequestScope('test'));
|
||||||
|
childContainer.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callLog, contains('parent-before'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can add its own callbacks', () {
|
||||||
|
var callLog = <String>[];
|
||||||
|
|
||||||
|
container.beforeResolving<RequestScope>((type, args, container) {
|
||||||
|
callLog.add('parent-before');
|
||||||
|
});
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.beforeResolving<RequestScope>((type, args, container) {
|
||||||
|
callLog.add('child-before');
|
||||||
|
});
|
||||||
|
|
||||||
|
childContainer.registerSingleton(RequestScope('test'));
|
||||||
|
childContainer.make<RequestScope>();
|
||||||
|
|
||||||
|
expect(callLog, containsAll(['parent-before', 'child-before']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal mock reflector for scoped and lifecycle tests
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) {
|
||||||
|
if (type == RequestScope) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'RequestScope',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('id', String)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => RequestScope(positional[0]),
|
||||||
|
);
|
||||||
|
} else if (type == UserService) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'UserService',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('scope', RequestScope)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => UserService(positional[0]),
|
||||||
|
);
|
||||||
|
} else if (type == OrderService) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
'OrderService',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
MockConstructor([MockParameter('scope', RequestScope)])
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => OrderService(positional[0]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedClass extends ReflectedClass {
|
||||||
|
final Function instanceBuilder;
|
||||||
|
|
||||||
|
MockReflectedClass(
|
||||||
|
String name,
|
||||||
|
List<ReflectedTypeParameter> typeParameters,
|
||||||
|
List<ReflectedInstance> annotations,
|
||||||
|
List<ReflectedFunction> constructors,
|
||||||
|
List<ReflectedDeclaration> declarations,
|
||||||
|
Type reflectedType,
|
||||||
|
this.instanceBuilder,
|
||||||
|
) : super(name, typeParameters, annotations, constructors, declarations,
|
||||||
|
reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
var instance = instanceBuilder(
|
||||||
|
constructorName, positionalArguments, namedArguments, typeArguments);
|
||||||
|
return MockReflectedInstance(this, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedInstance extends ReflectedInstance {
|
||||||
|
MockReflectedInstance(ReflectedClass clazz, Object? reflectee)
|
||||||
|
: super(clazz, clazz, reflectee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockConstructor extends ReflectedFunction {
|
||||||
|
final List<ReflectedParameter> params;
|
||||||
|
|
||||||
|
MockConstructor(this.params)
|
||||||
|
: super('', [], [], params, false, false,
|
||||||
|
returnType: MockReflectedType('void', [], dynamic));
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockParameter extends ReflectedParameter {
|
||||||
|
MockParameter(String name, Type type)
|
||||||
|
: super(name, [], MockReflectedType(type.toString(), [], type), true,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedType extends ReflectedType {
|
||||||
|
MockReflectedType(String name, List<ReflectedTypeParameter> typeParameters,
|
||||||
|
Type reflectedType)
|
||||||
|
: super(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
241
incubation/container/container/test/tag_test.dart
Normal file
241
incubation/container/container/test/tag_test.dart
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
// Test interfaces and implementations
|
||||||
|
abstract class Repository {
|
||||||
|
String getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserRepository implements Repository {
|
||||||
|
@override
|
||||||
|
String getName() => 'users';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductRepository implements Repository {
|
||||||
|
@override
|
||||||
|
String getName() => 'products';
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderRepository implements Repository {
|
||||||
|
@override
|
||||||
|
String getName() => 'orders';
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Tag Tests', () {
|
||||||
|
test('can tag and resolve multiple bindings', () {
|
||||||
|
container.registerSingleton<Repository>(UserRepository(),
|
||||||
|
as: UserRepository);
|
||||||
|
container.registerSingleton<Repository>(ProductRepository(),
|
||||||
|
as: ProductRepository);
|
||||||
|
container.registerSingleton<Repository>(OrderRepository(),
|
||||||
|
as: OrderRepository);
|
||||||
|
|
||||||
|
container.tag([UserRepository, ProductRepository], 'basic');
|
||||||
|
container.tag([OrderRepository], 'advanced');
|
||||||
|
container.tag([UserRepository, OrderRepository], 'critical');
|
||||||
|
|
||||||
|
var basicRepos = container.tagged('basic');
|
||||||
|
expect(basicRepos, hasLength(2));
|
||||||
|
expect(basicRepos.map((r) => r.getName()),
|
||||||
|
containsAll(['users', 'products']));
|
||||||
|
|
||||||
|
var advancedRepos = container.tagged('advanced');
|
||||||
|
expect(advancedRepos, hasLength(1));
|
||||||
|
expect(advancedRepos.first.getName(), equals('orders'));
|
||||||
|
|
||||||
|
var criticalRepos = container.tagged('critical');
|
||||||
|
expect(criticalRepos, hasLength(2));
|
||||||
|
expect(criticalRepos.map((r) => r.getName()),
|
||||||
|
containsAll(['users', 'orders']));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can tag same binding with multiple tags', () {
|
||||||
|
container.registerSingleton<Repository>(UserRepository(),
|
||||||
|
as: UserRepository);
|
||||||
|
|
||||||
|
container.tag([UserRepository], 'tag1');
|
||||||
|
container.tag([UserRepository], 'tag2');
|
||||||
|
|
||||||
|
expect(container.tagged('tag1'), hasLength(1));
|
||||||
|
expect(container.tagged('tag2'), hasLength(1));
|
||||||
|
expect(container.tagged('tag1').first, isA<UserRepository>());
|
||||||
|
expect(container.tagged('tag2').first, isA<UserRepository>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns empty list for unknown tag', () {
|
||||||
|
var repos = container.tagged('nonexistent');
|
||||||
|
expect(repos, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container inherits parent tags', () {
|
||||||
|
container.registerSingleton<Repository>(UserRepository(),
|
||||||
|
as: UserRepository);
|
||||||
|
container.registerSingleton<Repository>(ProductRepository(),
|
||||||
|
as: ProductRepository);
|
||||||
|
container.tag([UserRepository, ProductRepository], 'basic');
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
var basicRepos = childContainer.tagged('basic');
|
||||||
|
expect(basicRepos, hasLength(2));
|
||||||
|
expect(basicRepos.map((r) => r.getName()),
|
||||||
|
containsAll(['users', 'products']));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can add new tags', () {
|
||||||
|
container.registerSingleton<Repository>(UserRepository(),
|
||||||
|
as: UserRepository);
|
||||||
|
container.tag([UserRepository], 'parent-tag');
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.registerSingleton<Repository>(ProductRepository(),
|
||||||
|
as: ProductRepository);
|
||||||
|
childContainer.tag([ProductRepository], 'child-tag');
|
||||||
|
|
||||||
|
expect(childContainer.tagged('parent-tag'), hasLength(1));
|
||||||
|
expect(childContainer.tagged('child-tag'), hasLength(1));
|
||||||
|
expect(childContainer.tagged('parent-tag').first, isA<UserRepository>());
|
||||||
|
expect(
|
||||||
|
childContainer.tagged('child-tag').first, isA<ProductRepository>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('child container can extend parent tags', () {
|
||||||
|
container.registerSingleton<Repository>(UserRepository(),
|
||||||
|
as: UserRepository);
|
||||||
|
container.tag([UserRepository], 'repositories');
|
||||||
|
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
childContainer.registerSingleton<Repository>(ProductRepository(),
|
||||||
|
as: ProductRepository);
|
||||||
|
childContainer.tag([ProductRepository], 'repositories');
|
||||||
|
|
||||||
|
var repos = childContainer.tagged('repositories');
|
||||||
|
expect(repos, hasLength(2));
|
||||||
|
expect(repos.map((r) => r.getName()), containsAll(['users', 'products']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal mock reflector for tag tests
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) {
|
||||||
|
if (type == UserRepository ||
|
||||||
|
type == ProductRepository ||
|
||||||
|
type == OrderRepository) {
|
||||||
|
return MockReflectedClass(
|
||||||
|
type.toString(),
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[MockConstructor([])],
|
||||||
|
[],
|
||||||
|
type,
|
||||||
|
(name, positional, named, typeArgs) => _createInstance(type),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _createInstance(Type type) {
|
||||||
|
if (type == UserRepository) return UserRepository();
|
||||||
|
if (type == ProductRepository) return ProductRepository();
|
||||||
|
if (type == OrderRepository) return OrderRepository();
|
||||||
|
throw StateError('Unknown type: $type');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedClass extends ReflectedClass {
|
||||||
|
final Function instanceBuilder;
|
||||||
|
|
||||||
|
MockReflectedClass(
|
||||||
|
String name,
|
||||||
|
List<ReflectedTypeParameter> typeParameters,
|
||||||
|
List<ReflectedInstance> annotations,
|
||||||
|
List<ReflectedFunction> constructors,
|
||||||
|
List<ReflectedDeclaration> declarations,
|
||||||
|
Type reflectedType,
|
||||||
|
this.instanceBuilder,
|
||||||
|
) : super(name, typeParameters, annotations, constructors, declarations,
|
||||||
|
reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
var instance = instanceBuilder(
|
||||||
|
constructorName, positionalArguments, namedArguments, typeArguments);
|
||||||
|
return MockReflectedInstance(this, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedInstance extends ReflectedInstance {
|
||||||
|
MockReflectedInstance(ReflectedClass clazz, Object? reflectee)
|
||||||
|
: super(clazz, clazz, reflectee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockConstructor extends ReflectedFunction {
|
||||||
|
final List<ReflectedParameter> params;
|
||||||
|
|
||||||
|
MockConstructor(this.params)
|
||||||
|
: super('', [], [], params, false, false,
|
||||||
|
returnType: MockReflectedType('void', [], dynamic));
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedType extends ReflectedType {
|
||||||
|
MockReflectedType(String name, List<ReflectedTypeParameter> typeParameters,
|
||||||
|
Type reflectedType)
|
||||||
|
: super(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
return reflectedType == other.reflectedType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var reflector = const ThrowingReflector();
|
||||||
|
|
||||||
|
test('getName', () {
|
||||||
|
expect(reflector.getName(#foo), 'foo');
|
||||||
|
expect(reflector.getName(#==), '==');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectClass fails', () {
|
||||||
|
expect(() => reflector.reflectClass(Truck), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectType fails', () {
|
||||||
|
expect(() => reflector.reflectType(Truck), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectFunction throws', () {
|
||||||
|
void doIt(int x) {}
|
||||||
|
expect(() => reflector.reflectFunction(doIt), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reflectInstance throws', () {
|
||||||
|
expect(() => reflector.reflectInstance(Truck()), throwsUnsupportedError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Truck {
|
||||||
|
int get wheelCount => 4;
|
||||||
|
|
||||||
|
void drive() {
|
||||||
|
print('Vroom!!!');
|
||||||
|
}
|
||||||
|
}
|
16
incubation/container/container_generator/.gitignore
vendored
Normal file
16
incubation/container/container_generator/.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# See https://www.dartlang.org/guides/libraries/private-files
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
# If you're building an application, you may want to check-in your pubspec.lock
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
# If you don't generate documentation locally you can remove this line.
|
||||||
|
doc/api/
|
||||||
|
|
||||||
|
test/*.reflectable.dart
|
||||||
|
example/*.reflectable.dart
|
58
incubation/container/container_generator/CHANGELOG.md
Normal file
58
incubation/container/container_generator/CHANGELOG.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## 8.1.1
|
||||||
|
|
||||||
|
* Updated repository link
|
||||||
|
|
||||||
|
## 8.1.0
|
||||||
|
|
||||||
|
* Updated `lints` to 3.0.0
|
||||||
|
* Fixed analyser warnings
|
||||||
|
|
||||||
|
## 8.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.0
|
||||||
|
|
||||||
|
## 7.1.0-beta.1
|
||||||
|
|
||||||
|
* Require Dart >= 2.19
|
||||||
|
* Upgraded `relectable` to 4.x.x
|
||||||
|
|
||||||
|
## 7.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.17
|
||||||
|
|
||||||
|
## 6.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.16
|
||||||
|
|
||||||
|
## 5.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
* Updated `package:angel3_container`
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
* Fixed NNBD issues
|
||||||
|
* All 9 test cases passed
|
||||||
|
|
||||||
|
## 3.0.0-beta.1
|
||||||
|
|
||||||
|
* Migrated to support Dart >= 2.12 NNBD
|
||||||
|
* Updated linter to `package:lints`
|
||||||
|
* Updated to use `platform_` packages
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
* Update for `pkg:angel_container@1.0.3`.
|
29
incubation/container/container_generator/LICENSE
Normal file
29
incubation/container/container_generator/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, dukefirehawk.com
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
incubation/container/container_generator/README.md
Normal file
32
incubation/container/container_generator/README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Protevus Container Generator
|
||||||
|
|
||||||
|
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_container_generator?include_prereleases)
|
||||||
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
|
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||||
|
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/container/angel3_container_generator/LICENSE)
|
||||||
|
|
||||||
|
An alternative container for Protevus that uses `reflectable` package instead of `dart:mirrors` for reflection. However, `reflectable` has more limited relfection capabilities when compared to `dart:mirrors`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* Annotable the class with `@contained`.
|
||||||
|
* Run `dart run build_runner build <Your class directory>`
|
||||||
|
* Alternatively create a `build.xml` file with the following content
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
reflectable:
|
||||||
|
generate_for:
|
||||||
|
- bin/**_controller.dart
|
||||||
|
options:
|
||||||
|
formatted: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known limitation
|
||||||
|
|
||||||
|
* `analyser` 6.x is not supported due to `reflectable`
|
||||||
|
* Reflection on functions/closures is not supported
|
||||||
|
* Reflection on private declarations is not supported
|
||||||
|
* Reflection on generic type is not supported
|
|
@ -0,0 +1 @@
|
||||||
|
include: package:lints/recommended.yaml
|
0
incubation/container/container_generator/doc/.gitkeep
Normal file
0
incubation/container/container_generator/doc/.gitkeep
Normal file
75
incubation/container/container_generator/example/main.dart
Normal file
75
incubation/container/container_generator/example/main.dart
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:platform_container/container.dart';
|
||||||
|
import 'package:platform_container_generator/generator.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
// Create a container instance.
|
||||||
|
Container container = Container(GeneratedReflector());
|
||||||
|
|
||||||
|
// Register a singleton.
|
||||||
|
container.registerSingleton<Engine>(Engine(40));
|
||||||
|
|
||||||
|
// You can also omit the type annotation, in which the object's runtime type will be used.
|
||||||
|
// If you're injecting an abstract class, prefer the type annotation.
|
||||||
|
//
|
||||||
|
// container.registerSingleton(Engine(40));
|
||||||
|
|
||||||
|
// Register a factory that creates a truck.
|
||||||
|
container.registerFactory<Truck>((container) {
|
||||||
|
return _TruckImpl(container.make<Engine>());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use `make` to create an instance.
|
||||||
|
var truck = container.make<Truck>();
|
||||||
|
|
||||||
|
// You can also resolve injections asynchronously.
|
||||||
|
container.registerFactory<Future<int>>((_) async => 24);
|
||||||
|
print(await container.makeAsync<int>());
|
||||||
|
|
||||||
|
// Asynchronous resolution also works for plain objects.
|
||||||
|
await container.makeAsync<Truck>().then((t) => t.drive());
|
||||||
|
|
||||||
|
// Register a named singleton.
|
||||||
|
container.registerNamedSingleton('the_truck', truck);
|
||||||
|
|
||||||
|
// Should print: 'Vroom! I have 40 horsepower in my engine.'
|
||||||
|
truck.drive();
|
||||||
|
|
||||||
|
// Should print the same.
|
||||||
|
container.findByName<Truck>('the_truck').drive();
|
||||||
|
|
||||||
|
// We can make a child container with its own factory.
|
||||||
|
var childContainer = container.createChild();
|
||||||
|
|
||||||
|
childContainer.registerFactory<Truck>((container) {
|
||||||
|
return _TruckImpl(Engine(5666));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make a truck with 5666 HP.
|
||||||
|
childContainer.make<Truck>().drive();
|
||||||
|
|
||||||
|
// However, calling `make<Engine>` will return the Engine singleton we created above.
|
||||||
|
print(childContainer.make<Engine>().horsePower);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Truck {
|
||||||
|
void drive();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Engine {
|
||||||
|
final int horsePower;
|
||||||
|
|
||||||
|
Engine(this.horsePower);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TruckImpl implements Truck {
|
||||||
|
final Engine engine;
|
||||||
|
|
||||||
|
_TruckImpl(this.engine);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void drive() {
|
||||||
|
print('Vroom! I have ${engine.horsePower} horsepower in my engine.');
|
||||||
|
}
|
||||||
|
}
|
255
incubation/container/container_generator/lib/generator.dart
Normal file
255
incubation/container/container_generator/lib/generator.dart
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
import 'package:platform_container/container.dart';
|
||||||
|
import 'package:reflectable/reflectable.dart';
|
||||||
|
|
||||||
|
/// A [Reflectable] instance that can be used as an annotation on types to generate metadata for them.
|
||||||
|
const Reflectable contained = ContainedReflectable();
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class ContainedReflectable extends Reflectable {
|
||||||
|
const ContainedReflectable()
|
||||||
|
: super(
|
||||||
|
topLevelInvokeCapability,
|
||||||
|
typeAnnotationQuantifyCapability,
|
||||||
|
superclassQuantifyCapability,
|
||||||
|
libraryCapability,
|
||||||
|
invokingCapability,
|
||||||
|
metadataCapability,
|
||||||
|
reflectedTypeCapability,
|
||||||
|
typeCapability,
|
||||||
|
typingCapability);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Reflector] instance that uses a [Reflectable] to reflect upon data.
|
||||||
|
class GeneratedReflector extends Reflector {
|
||||||
|
final Reflectable reflectable;
|
||||||
|
|
||||||
|
const GeneratedReflector([this.reflectable = contained]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getName(Symbol symbol) {
|
||||||
|
return symbol.toString().substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass reflectClass(Type clazz) {
|
||||||
|
return reflectType(clazz) as ReflectedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction reflectFunction(Function function) {
|
||||||
|
if (!reflectable.canReflect(function)) {
|
||||||
|
throw UnsupportedError('Cannot reflect $function.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var mirror = reflectable.reflect(function);
|
||||||
|
|
||||||
|
if (mirror is ClosureMirror) {
|
||||||
|
return _GeneratedReflectedFunction(mirror.function, this, mirror);
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('$function is not a Function.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance reflectInstance(Object object) {
|
||||||
|
if (!reflectable.canReflect(object)) {
|
||||||
|
throw UnsupportedError('Cannot reflect $object.');
|
||||||
|
} else {
|
||||||
|
var mirror = reflectable.reflect(object);
|
||||||
|
return _GeneratedReflectedInstance(mirror, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectType(Type type) {
|
||||||
|
if (!reflectable.canReflectType(type)) {
|
||||||
|
throw UnsupportedError('Cannot reflect $type.');
|
||||||
|
} else {
|
||||||
|
var mirror = reflectable.reflectType(type);
|
||||||
|
return mirror is ClassMirror
|
||||||
|
? _GeneratedReflectedClass(mirror, this)
|
||||||
|
: _GeneratedReflectedType(mirror);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeneratedReflectedInstance extends ReflectedInstance {
|
||||||
|
final InstanceMirror mirror;
|
||||||
|
final GeneratedReflector reflector;
|
||||||
|
|
||||||
|
_GeneratedReflectedInstance(this.mirror, this.reflector)
|
||||||
|
: super(_GeneratedReflectedType(mirror.type),
|
||||||
|
_GeneratedReflectedClass(mirror.type, reflector), mirror.reflectee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType get type => clazz;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) {
|
||||||
|
var result = mirror.invokeGetter(name)!;
|
||||||
|
var instance = reflector.reflectable.reflect(result);
|
||||||
|
return _GeneratedReflectedInstance(instance, reflector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeneratedReflectedClass extends ReflectedClass {
|
||||||
|
final ClassMirror mirror;
|
||||||
|
final Reflector reflector;
|
||||||
|
|
||||||
|
_GeneratedReflectedClass(this.mirror, this.reflector)
|
||||||
|
: super(mirror.simpleName, [], [], [], [], mirror.reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters =>
|
||||||
|
mirror.typeVariables.map(_convertTypeVariable).toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedFunction> get constructors =>
|
||||||
|
_constructorsOf(mirror.declarations, reflector);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedDeclaration> get declarations =>
|
||||||
|
_declarationsOf(mirror.declarations, reflector);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => mirror.metadata
|
||||||
|
.map(reflector.reflectInstance)
|
||||||
|
.whereType<ReflectedInstance>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other is _GeneratedReflectedClass) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else if (other is _GeneratedReflectedType) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) {
|
||||||
|
namedArguments ??= {};
|
||||||
|
var result = mirror.newInstance(constructorName, positionalArguments,
|
||||||
|
namedArguments.map((k, v) => MapEntry(Symbol(k), v)));
|
||||||
|
return reflector.reflectInstance(result)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeneratedReflectedType extends ReflectedType {
|
||||||
|
final TypeMirror mirror;
|
||||||
|
|
||||||
|
_GeneratedReflectedType(this.mirror)
|
||||||
|
: super(mirror.simpleName, [], mirror.reflectedType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters =>
|
||||||
|
mirror.typeVariables.map(_convertTypeVariable).toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) {
|
||||||
|
if (other is _GeneratedReflectedClass) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else if (other is _GeneratedReflectedType) {
|
||||||
|
return mirror.isAssignableTo(other.mirror);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) {
|
||||||
|
throw UnsupportedError('Cannot create a new instance of $reflectedType.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeneratedReflectedFunction extends ReflectedFunction {
|
||||||
|
final MethodMirror mirror;
|
||||||
|
final Reflector reflector;
|
||||||
|
final ClosureMirror? closure;
|
||||||
|
|
||||||
|
_GeneratedReflectedFunction(this.mirror, this.reflector, [this.closure])
|
||||||
|
: super(
|
||||||
|
mirror.simpleName,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
mirror.parameters
|
||||||
|
.map((p) => _convertParameter(p, reflector))
|
||||||
|
.toList(),
|
||||||
|
mirror.isGetter,
|
||||||
|
mirror.isSetter,
|
||||||
|
returnType: !mirror.isRegularMethod
|
||||||
|
? null
|
||||||
|
: _GeneratedReflectedType(mirror.returnType));
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => mirror.metadata
|
||||||
|
.map(reflector.reflectInstance)
|
||||||
|
.whereType<ReflectedInstance>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) {
|
||||||
|
if (closure != null) {
|
||||||
|
throw UnsupportedError('Only closures can be invoked directly.');
|
||||||
|
} else {
|
||||||
|
var result = closure!.delegate(invocation)!;
|
||||||
|
return reflector.reflectInstance(result)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReflectedFunction> _constructorsOf(
|
||||||
|
Map<String, DeclarationMirror> map, Reflector reflector) {
|
||||||
|
return map.entries.fold<List<ReflectedFunction>>([], (out, entry) {
|
||||||
|
var v = entry.value;
|
||||||
|
|
||||||
|
if (v is MethodMirror && v.isConstructor) {
|
||||||
|
return out..add(_GeneratedReflectedFunction(v, reflector));
|
||||||
|
} else {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReflectedDeclaration> _declarationsOf(
|
||||||
|
Map<String, DeclarationMirror> map, Reflector reflector) {
|
||||||
|
return map.entries.fold<List<ReflectedDeclaration>>([], (out, entry) {
|
||||||
|
var v = entry.value;
|
||||||
|
|
||||||
|
if (v is VariableMirror) {
|
||||||
|
var decl = ReflectedDeclaration(v.simpleName, v.isStatic, null);
|
||||||
|
return out..add(decl);
|
||||||
|
}
|
||||||
|
if (v is MethodMirror) {
|
||||||
|
var decl = ReflectedDeclaration(
|
||||||
|
v.simpleName, v.isStatic, _GeneratedReflectedFunction(v, reflector));
|
||||||
|
return out..add(decl);
|
||||||
|
} else {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ReflectedTypeParameter _convertTypeVariable(TypeVariableMirror mirror) {
|
||||||
|
return ReflectedTypeParameter(mirror.simpleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReflectedParameter _convertParameter(
|
||||||
|
ParameterMirror mirror, Reflector reflector) {
|
||||||
|
return ReflectedParameter(
|
||||||
|
mirror.simpleName,
|
||||||
|
mirror.metadata
|
||||||
|
.map(reflector.reflectInstance)
|
||||||
|
.whereType<ReflectedInstance>()
|
||||||
|
.toList(),
|
||||||
|
reflector.reflectType(mirror.type.reflectedType)!,
|
||||||
|
!mirror.isOptional,
|
||||||
|
mirror.isNamed);
|
||||||
|
}
|
19
incubation/container/container_generator/pubspec.yaml
Normal file
19
incubation/container/container_generator/pubspec.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
name: platformed_container_generator
|
||||||
|
version: 9.0.0
|
||||||
|
description: Protevus Platform Codegen support for using pkg:reflectable with pkg:platform_container.
|
||||||
|
homepage: https://protevus.com
|
||||||
|
documentation: https://docs.protevus.com
|
||||||
|
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/container/container_generator
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
dependencies:
|
||||||
|
platform_container: ^9.0.0
|
||||||
|
reflectable: ^4.0.12
|
||||||
|
dev_dependencies:
|
||||||
|
build_runner: ^2.4.13
|
||||||
|
build_test: ^2.2.2
|
||||||
|
test: ^1.25.8
|
||||||
|
lints: ^4.0.0
|
||||||
|
# dependency_overrides:
|
||||||
|
# platform_container:
|
||||||
|
# path: ../platform_container
|
|
@ -0,0 +1,179 @@
|
||||||
|
import 'package:platform_container/container.dart';
|
||||||
|
import 'package:platform_container_generator/generator.dart';
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'reflector_test.reflectable.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
initializeReflectable();
|
||||||
|
|
||||||
|
var reflector = const GeneratedReflector();
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(reflector);
|
||||||
|
container.registerSingleton(Artist(name: 'Stevie Wonder'));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectClass', () {
|
||||||
|
var mirror = reflector.reflectClass(Artist);
|
||||||
|
|
||||||
|
test('name', () {
|
||||||
|
expect(mirror.name, 'Artist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inject constructor parameters', () {
|
||||||
|
var album = container.make<Album>();
|
||||||
|
print(album.title);
|
||||||
|
expect(album.title, 'flowers by stevie wonder');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skip as pkg:reflectable cannot reflect on closures at all (yet)
|
||||||
|
//testReflector(reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
void returnVoidFromAFunction(int x) {}
|
||||||
|
|
||||||
|
void testReflector(Reflector reflector) {
|
||||||
|
var blaziken = Pokemon('Blaziken', PokemonType.fire);
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(reflector);
|
||||||
|
container.registerSingleton(blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get field', () {
|
||||||
|
var blazikenMirror = reflector.reflectInstance(blaziken)!;
|
||||||
|
expect(blazikenMirror.getField('type').reflectee, blaziken.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reflectFunction', () {
|
||||||
|
var mirror = reflector.reflectFunction(returnVoidFromAFunction);
|
||||||
|
|
||||||
|
test('void return type returns dynamic', () {
|
||||||
|
expect(mirror?.returnType, reflector.reflectType(dynamic));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts parameters', () {
|
||||||
|
expect(mirror?.parameters, hasLength(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts types parameters', () {
|
||||||
|
expect(mirror?.typeParameters, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('correctly reflects parameter types', () {
|
||||||
|
var p = mirror?.parameters[0];
|
||||||
|
expect(p?.name, 'x');
|
||||||
|
expect(p?.isRequired, true);
|
||||||
|
expect(p?.isNamed, false);
|
||||||
|
expect(p?.annotations, isEmpty);
|
||||||
|
expect(p?.type, reflector.reflectType(int));
|
||||||
|
});
|
||||||
|
}, skip: 'pkg:reflectable cannot reflect on closures at all (yet)');
|
||||||
|
|
||||||
|
test('make on singleton type returns singleton', () {
|
||||||
|
expect(container.make(Pokemon), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make with generic returns same as make with explicit type', () {
|
||||||
|
expect(container.make<Pokemon>(), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make on aliased singleton returns singleton', () {
|
||||||
|
container.registerSingleton(blaziken, as: StateError);
|
||||||
|
expect(container.make(StateError), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('constructor injects singleton', () {
|
||||||
|
var lower = container.make<LowerPokemon>();
|
||||||
|
expect(lower.lowercaseName, blaziken.name.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('newInstance works', () {
|
||||||
|
var type = container.reflector.reflectType(Pokemon)!;
|
||||||
|
var instance =
|
||||||
|
type.newInstance('changeName', [blaziken, 'Charizard']).reflectee
|
||||||
|
as Pokemon;
|
||||||
|
print(instance);
|
||||||
|
expect(instance.name, 'Charizard');
|
||||||
|
expect(instance.type, PokemonType.fire);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssignableTo', () {
|
||||||
|
var pokemonType = container.reflector.reflectType(Pokemon);
|
||||||
|
var kantoPokemonType = container.reflector.reflectType(KantoPokemon)!;
|
||||||
|
|
||||||
|
expect(kantoPokemonType.isAssignableTo(pokemonType), true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
kantoPokemonType
|
||||||
|
.isAssignableTo(container.reflector.reflectType(String)),
|
||||||
|
false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class LowerPokemon {
|
||||||
|
final Pokemon pokemon;
|
||||||
|
|
||||||
|
LowerPokemon(this.pokemon);
|
||||||
|
|
||||||
|
String get lowercaseName => pokemon.name.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class Pokemon {
|
||||||
|
final String name;
|
||||||
|
final PokemonType type;
|
||||||
|
|
||||||
|
Pokemon(this.name, this.type);
|
||||||
|
|
||||||
|
factory Pokemon.changeName(Pokemon other, String name) {
|
||||||
|
return Pokemon(name, other.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'NAME: $name, TYPE: $type';
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class KantoPokemon extends Pokemon {
|
||||||
|
KantoPokemon(super.name, super.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
enum PokemonType { water, fire, grass, ice, poison, flying }
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class Artist {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
Artist({required this.name});
|
||||||
|
|
||||||
|
String get lowerName {
|
||||||
|
return name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class Album {
|
||||||
|
final Artist artist;
|
||||||
|
|
||||||
|
Album(this.artist);
|
||||||
|
|
||||||
|
String get title => 'flowers by ${artist.lowerName}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@contained
|
||||||
|
class AlbumLength {
|
||||||
|
final Artist artist;
|
||||||
|
final Album album;
|
||||||
|
|
||||||
|
AlbumLength(this.artist, this.album);
|
||||||
|
|
||||||
|
int get totalLength => artist.name.length + album.title.length;
|
||||||
|
}
|
Loading…
Reference in a new issue