io.datakernel.di.module.Modules Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-di Show documentation
Show all versions of datakernel-di Show documentation
DataKernel has an extremely lightweight DI with ground-breaking design principles.
It supports nested scopes, singletons, object factories, modules and plugins which
allow to transform graph of dependencies at startup time without any reflection.
The newest version!
package io.datakernel.di.module;
import io.datakernel.di.core.*;
import io.datakernel.di.impl.BindingLocator;
import io.datakernel.di.util.Trie;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import static io.datakernel.di.core.BindingType.COMMON;
import static io.datakernel.di.core.Name.uniqueName;
import static io.datakernel.di.core.Scope.UNSCOPED;
import static io.datakernel.di.util.Utils.*;
import static java.util.Collections.emptyMap;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
/**
* This class contains a set of utilities for working with {@link Module modules}.
*/
public final class Modules {
static final Module EMPTY = new SimpleModule(Trie.leaf(emptyMap()), emptyMap(), emptyMap(), emptyMap());
/**
* Combines multiple modules into one.
*/
public static Module combine(Collection modules) {
if (modules.size() == 1) {
return modules.iterator().next();
}
Trie, BindingSet>>> bindings = Trie.merge(bindingMultimapMerger(), new HashMap<>(), modules.stream().map(Module::getBindings));
Map>> bindingTransformers = new HashMap<>();
Map, Set>> bindingGenerators = new HashMap<>();
Map, Multibinder>> multibinders = new HashMap<>();
for (Module module : modules) {
combineMultimap(bindingTransformers, module.getBindingTransformers());
combineMultimap(bindingGenerators, module.getBindingGenerators());
mergeMultibinders(multibinders, module.getMultibinders());
}
return new SimpleModule(bindings, bindingTransformers, bindingGenerators, multibinders);
}
/**
* @see #combine(Collection)
*/
public static Module combine(Module... modules) {
return modules.length == 0 ? Module.empty() : modules.length == 1 ? modules[0] : combine(Arrays.asList(modules));
}
/**
* Consecutively overrides each of the given modules with the next one after it and returns the accumulated result.
*/
public static Module override(List modules) {
return modules.stream().reduce(Module.empty(), Modules::override);
}
/**
* @see #combine(Collection)
*/
public static Module override(Module... modules) {
return override(Arrays.asList(modules));
}
/**
* This method creates a module that has bindings, transformers, generators and multibinders from first module
* replaced with bindings, transformers, generators and multibinders from the second module.
*/
public static Module override(Module into, Module replacements) {
Trie, BindingSet>>> bindings = Trie.merge(Map::putAll, new HashMap<>(), into.getBindings(), replacements.getBindings());
Map>> bindingTransformers = new HashMap<>(into.getBindingTransformers());
bindingTransformers.putAll(replacements.getBindingTransformers());
Map, Set>> bindingGenerators = new HashMap<>(into.getBindingGenerators());
bindingGenerators.putAll(replacements.getBindingGenerators());
Map, Multibinder>> multibinders = new HashMap<>(into.getMultibinders());
multibinders.putAll(replacements.getMultibinders());
return new SimpleModule(bindings, bindingTransformers, bindingGenerators, multibinders);
}
/**
* Creates a module with all trie nodes merged into one and placed at root.
* Basically, any scopes are ignored.
* This is useful for some tests.
*/
public static Module ignoreScopes(Module from) {
Map, BindingSet>> bindings = new HashMap<>();
Map, Scope[]> scopes = new HashMap<>();
from.getBindings().dfs(UNSCOPED, (scope, localBindings) ->
localBindings.forEach((k, b) -> {
bindings.merge(k, b, ($, $2) -> {
Scope[] alreadyThere = scopes.get(k);
String where = alreadyThere.length == 0 ? "in root" : "in scope " + getScopeDisplayString(alreadyThere);
throw new IllegalStateException("Duplicate key " + k + ", already defined " + where + " and in scope " + getScopeDisplayString(scope));
});
scopes.put(k, scope);
}));
return new SimpleModule(Trie.leaf(bindings), from.getBindingTransformers(), from.getBindingGenerators(), from.getMultibinders());
}
static Module export(Module module, Set> exportedKeys) {
Set> originalKeys = new HashSet<>();
module.getBindings().dfs(multimap -> originalKeys.addAll(multimap.keySet()));
Set> missing = new HashSet<>(exportedKeys);
missing.removeAll(originalKeys);
if (!missing.isEmpty()) {
throw new DIException(missing.stream()
.map(Key::getDisplayString)
.collect(joining(", ", "Exporting keys ", " that were not provided by the module")));
}
return doRebind(module,
originalKeys.stream()
.filter(originalKey -> !exportedKeys.contains(originalKey))
.collect(toMap(identity(), originalKey ->
Key.ofType(originalKey.getType(), uniqueName(originalKey.getName())))));
}
static Module rebindExports(Module module, Map, Key>> originalToNew) {
Set> originalKeys = new HashSet<>();
module.getBindings().dfs(multimap -> originalKeys.addAll(multimap.keySet()));
if (originalToNew.keySet().stream().noneMatch(originalKeys::contains)) {
return module;
}
return doRebind(module, originalToNew);
}
@SuppressWarnings("unchecked")
static Module rebindImports(Module module, Map, Binding>> rebinds) {
Map, Key>> originalToNew = new HashMap<>();
rebinds.forEach((k, b) -> {
Key © 2015 - 2025 Weber Informatics LLC | Privacy Policy