| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- using Invercargill;
- using Invercargill.DataStructures;
- namespace Inversion {
- private Dictionary<Thread, Lifo<InjectionContext>> injection_contexts_by_thread;
- private Mutex global_injection_context_lock;
- public class InjectionContext {
- public Registration registration { get; set; }
- public Type requested_type { get; set; }
- public Scope scope { get; set; }
- public Error? error { get; set;}
- public HashSet<Registration> stack_registrations { get; set; }
- public InjectionContext? previous_context { get; set; }
- public InjectionContext(Scope scope, Type requested_type, Registration registration, InjectionContext? previous = null) {
- this.scope = scope;
- this.registration = registration;
- this.previous_context = previous;
- this.requested_type = requested_type;
- stack_registrations = new HashSet<Registration>();
- stack_registrations.add(registration);
- if(previous != null) {
- stack_registrations.union_with(previous.stack_registrations);
- }
- }
- }
- public InjectionContext enter_new_injection_context(Scope scope, Type requested_type, Registration registration) throws Error {
- if(injection_contexts_by_thread == null) {
- global_injection_context_lock.lock();
- if(injection_contexts_by_thread == null) {
- injection_contexts_by_thread = new Dictionary<Thread, Lifo<InjectionContext>>();
- }
- global_injection_context_lock.unlock();
- }
- var thread = Thread.self<int>();
- Lifo<InjectionContext> stack;
- if(!injection_contexts_by_thread.try_get(thread, out stack)) {
- stack = new Lifo<InjectionContext>();
- stack.unblock();
- injection_contexts_by_thread[thread] = stack;
- }
- InjectionContext current = null;
- if(stack.try_peek(out current)) {
- if(current.error != null) {
- throw current.error;
- }
- if(current.stack_registrations.has(registration)) {
- // Cycle detected
- var cycle = registration.implementation_type.name();
- if(requested_type != registration.implementation_type) {
- cycle += @" (as $(requested_type.name()))";
- }
- var ctx = current;
- while(ctx != null && ctx.stack_registrations.has(registration)) {
- var label = ctx.registration.implementation_type.name();
- if(ctx.requested_type != ctx.registration.implementation_type) {
- label += @" (as $(ctx.requested_type.name()))";
- }
- cycle = @"$label => $cycle";
- ctx = ctx.previous_context;
- }
- throw new ContainerError.CYCLE_DETECTED(@"Cycle detected during dependency injection: $cycle ad infinitum.");
- }
- }
- var context = new InjectionContext(scope, requested_type, registration, current);
- stack.push(context);
- return context;
- }
- public void exit_injection_context() throws Error {
- var thread = Thread.self<int>();
- var stack = injection_contexts_by_thread[thread];
- var result = stack.pop();
- if(stack.peek_count() == 0) {
- injection_contexts_by_thread.remove(thread);
- }
- if(result.error != null) {
- throw result.error;
- }
- }
- public Object create_with_injection_context(Scope scope, Type requested_type, Registration registration) throws Error {
- enter_new_injection_context(scope, requested_type, registration);
- Error? error = null;
- Object object = null;
- try {
- object = registration.factory.create(scope);
- }
- catch(Error e) {
- error = e;
- }
- exit_injection_context();
- if(error != null) {
- throw error;
- }
- return object;
- }
- public InjectionContext get_injection_context() {
- if(injection_contexts_by_thread == null) {
- error("Injection context has not been initialised");
- }
- var thread = Thread.self<int>();
- var result = injection_contexts_by_thread.get_or_default(thread);
- if(result == null) {
- error("Injection context has not been initialised for this thread");
- }
- try {
- return result.peek();
- }
- catch {
- error("Empty injection context stack");
- }
- }
- public InjectionContext? try_get_injection_context() {
- if(injection_contexts_by_thread == null) {
- return null;
- }
- var thread = Thread.self<int>();
- var result = injection_contexts_by_thread.get_or_default(thread);
- if(result == null) {
- return null;
- }
- try {
- return result.peek();
- }
- catch {
- return null;
- }
- }
- public T inject<T>() {
- var context = get_injection_context();
- if(context.error != null) {
- return null;
- }
- try {
- return context.scope.resolve<T>();
- }
- catch(Error e) {
- context.error = e;
- return null;
- }
- }
- public Lot<T> inject_all<T>() {
- var context = get_injection_context();
- if(context.error != null) {
- return Iterate.nothing<T>().to_immutable_buffer();
- }
- try {
- return context.scope.resolve_all<T>()
- .to_immutable_buffer();
- }
- catch(Error e) {
- context.error = e;
- return Iterate.nothing<T>().to_immutable_buffer();
- }
- }
- public T? try_inject<T>() {
- var context = try_get_injection_context();
- if(context == null || context.error != null) {
- return null;
- }
- try {
- return context.scope.resolve<T>();
- }
- catch(Error e) {
- return null;
- }
- }
- public Lot<T> try_inject_all<T>() {
- var context = try_get_injection_context();
- if(context == null || context.error != null) {
- return Iterate.nothing<T>().to_immutable_buffer();
- }
- try {
- return context.scope.resolve_all<T>()
- .to_immutable_buffer();
- }
- catch(Error e) {
- return Iterate.nothing<T>().to_immutable_buffer();
- }
- }
- }
|