Environment constructor

Environment({
  1. Type? forModule,
  2. Environment? parent,
  3. List<String>? features,
})

Create a new Environment forModule the module class that determines the classes which will be manged parent optional parent environment, whose objects will be inherited features list of feature that this environment defines. See Conditional

Implementation

Environment({Type? forModule, this.parent, List<String>? features})  : module = forModule, features = features ?? []{
  if ( parent == null )
    if ( module == Boot) {
      lifecycleProcessors[0].add(OnInjectProcessor());
      lifecycleProcessors[1].add(OnInitProcessor());
      lifecycleProcessors[2].add(OnRunningProcessor());
      lifecycleProcessors[3].add(OnDestroyProcessor());
    }
    else parent = Boot.getEnvironment();

  final Stopwatch stopwatch = Stopwatch()..start();

  if ( Tracer.enabled )
    Tracer.trace('di', TraceLevel.low, 'create environment for module $module');

  // inherit parent providers

  if (parent != null) {
    parent!.providers.forEach((providerType, inheritedProvider) {
      var provider = inheritedProvider;
      if (inheritedProvider.scope == 'environment') {
        providers[providerType] = (inheritedProvider as EnvironmentInstanceProvider).copy(this);
      }
      else providers[providerType] = provider;
    });

    // inherit lifecycle processors

    for (int lifecycle = 0; lifecycle < 4; lifecycle++)
      for (final processor in parent!.lifecycleProcessors[lifecycle]) {
        if (providers[processor.runtimeType]?.scope != 'environment') {
          lifecycleProcessors[lifecycle].add(processor);
        }
        else {
          get(type: processor.runtimeType); // automatically appends
        }
    }
  }
  else {
    // add bootstrap provider for Boot

    providers[SingletonScope]   = SingletonScopeInstanceProvider();
    providers[RequestScope]     = RequestScopeInstanceProvider();
    providers[EnvironmentScope] = EnvironmentScopeInstanceProvider();
  }

  final Set<TypeDescriptor> loadedModules = {};
  final List<RegExp> filter = [];

  // filter by module prefix

  bool filterProvider(AbstractInstanceProvider provider) {
    final hostModule = TypeDescriptor.forType(provider.host).module;

    //print("filter $provider");
    for (final fileFilter in filter) {
      if (fileFilter.hasMatch(hostModule)) {
        //print("ok");
        return true;
      }
    }

    //print("na");
    return false;
  }

  RegExp buildModuleRegex(String modulePath, {bool includeChildren = false, bool includeSiblings = false}) {
    final lastSlash = modulePath.lastIndexOf('/');
    final dir = lastSlash >= 0 ? modulePath.substring(0, lastSlash) : '';
    //final fileOrDir = lastSlash >= 0 ? modulePath.substring(lastSlash + 1) : modulePath;

    String pattern;

    if (!includeChildren && !includeSiblings) {
      pattern = '^' + RegExp.escape(modulePath) + r'$';
    }
    else if (includeSiblings && !includeChildren) {
      pattern = '^' + (dir.isNotEmpty ? RegExp.escape(dir) + '/' : '') + r'[^/]+$';
    }
    else if (!includeSiblings && includeChildren) {
      pattern = '^' + (dir.isNotEmpty ? RegExp.escape(dir) + '/' : '') + r'.+$';
    }
    else {
      // both children and siblings
      pattern = '^' + (dir.isNotEmpty ? RegExp.escape(dir) + '/' : '') + r'.+$';
    }

    return RegExp(pattern);
  }

  // recursively load environment and its dependencies

  void loadModule(TypeDescriptor moduleDescriptor) {
    if (!loadedModules.contains(moduleDescriptor)) {
      if ( Tracer.enabled )
        Tracer.trace('di', TraceLevel.low, 'load environment $moduleDescriptor');

      loadedModules.add(moduleDescriptor);

      final decorator = moduleDescriptor.findAnnotation<Module>();
      if (decorator == null) {
        throw DIRegistrationException('$moduleDescriptor is not an module class');
      }

      // load dependencies recursively

      for (final import in  decorator.imports) {
        loadModule(TypeDescriptor.forType(import));
      }

      // determine package prefix

      final module = moduleDescriptor.getAnnotation<Module>()!;

      final packageName = moduleDescriptor.module;
      if (packageName.isNotEmpty) {
        filter.add(buildModuleRegex(packageName, includeChildren: module.includeSubdirectories, includeSiblings: module.includeSiblings));
      }
    }
  }

  if ( module != null) {
    loadModule(TypeDescriptor.forType(module));

    // filter providers according to prefix list

    providers.addAll(Providers.filter(this, filterProvider));

    // construct eager objects for local providers

    for (final provider in providers.values.toSet()) {
      if (provider.eager) {
        provider.create(this);
      }
    }
  } // if

  // run lifecycle callbacks

  for (final instance in instances) {
    executeProcessors(null, Lifecycle.onRunning, instance);
  }

  stopwatch.stop();

  if ( Tracer.enabled )
    Tracer.trace('di', TraceLevel.high, 'created environment for module $module in ${stopwatch.elapsedMilliseconds} ms, created ${instances.length} instances');
}