Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.datakernel.di.util.Utils Maven / Gradle / Ivy
Go to download
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.
package io.datakernel.di.util;
import io.datakernel.di.core.*;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import static io.datakernel.di.core.BindingType.EAGER;
import static io.datakernel.di.core.BindingType.TRANSIENT;
import static io.datakernel.di.core.Scope.UNSCOPED;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.*;
public final class Utils {
private static final BiConsumer, BindingSet>>, Map, BindingSet>>> BINDING_MULTIMAP_MERGER =
(into, from) -> from.forEach((k, v) -> into.merge(k, v, (first, second) -> BindingSet.merge(k, first, second)));
public static BiConsumer, BindingSet>>, Map, BindingSet>>> bindingMultimapMerger() {
return BINDING_MULTIMAP_MERGER;
}
public static T[] next(T[] items, T item) {
T[] next = Arrays.copyOf(items, items.length + 1);
next[items.length] = item;
return next;
}
public static String getScopeDisplayString(Scope[] scope) {
return Arrays.stream(scope).map(Scope::getDisplayString).collect(joining("->", "()", ""));
}
public static void mergeMultibinders(Map, Multibinder>> into, Map, Multibinder>> from) {
from.forEach((k, v) -> into.merge(k, v, (oldResolver, newResolver) -> {
if (!oldResolver.equals(newResolver)) {
throw new DIException("More than one multibinder per key");
}
return oldResolver;
}));
}
public static void combineMultimap(Map> accumulator, Map> multimap) {
multimap.forEach((key, set) -> accumulator.computeIfAbsent(key, $ -> new HashSet<>()).addAll(set));
}
public static Set union(Set first, Set second) {
Set result = new HashSet<>((first.size() + second.size()) * 4 / 3 + 1);
result.addAll(first);
result.addAll(second);
return result;
}
public static Map override(Map into, Map from) {
Map result = new HashMap<>((from.size() + into.size()) * 4 / 3 + 1);
result.putAll(from);
result.putAll(into);
return result;
}
public static Collector>> toMultimap(Function super T, ? extends K> keyMapper,
Function super T, ? extends V> valueMapper) {
return toMap(keyMapper, t -> singleton(valueMapper.apply(t)), Utils::union);
}
public static Map> transformMultimapValues(Map> multimap, BiFunction super K, ? super V, ? extends V1> fn) {
return transformMultimap(multimap, Function.identity(), fn);
}
public static Map> transformMultimap(Map> multimap, Function super K, ? extends K1> fnKey, BiFunction super K, ? super V, ? extends V1> fnValue) {
return multimap.entrySet()
.stream()
.collect(toMap(
entry -> fnKey.apply(entry.getKey()),
entry -> entry.getValue()
.stream()
.map(v -> fnValue.apply(entry.getKey(), v))
.collect(toSet())));
}
public static Map, BindingSet>> transformBindingMultimapValues(Map, BindingSet>> multimap, BiFunction, Binding>, Binding>> fn) {
return transformBindingMultimap(multimap, UnaryOperator.identity(), fn);
}
@SuppressWarnings("unchecked")
public static Map, BindingSet>> transformBindingMultimap(Map, BindingSet>> multimap, UnaryOperator> fnKey, BiFunction, Binding>, Binding>> fnValue) {
return multimap.entrySet()
.stream()
.collect(toMap(
entry -> fnKey.apply(entry.getKey()),
entry -> {
BindingSet> bindingSet = entry.getValue();
return new BindingSet(
bindingSet
.getBindings()
.stream()
.map(v -> fnValue.apply(entry.getKey(), v))
.collect(toSet()),
bindingSet.getType());
}));
}
public static Map squash(Map> multimap, BiFunction, V> squasher) {
return multimap.entrySet().stream()
.collect(toMap(Entry::getKey, e -> squasher.apply(e.getKey(), e.getValue())));
}
public static void checkArgument(boolean condition, String message) {
if (!condition) {
throw new IllegalArgumentException(message);
}
}
public static void checkState(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
}
}
public static String getLocation(@Nullable Binding> binding) {
LocationInfo location = binding != null ? binding.getLocation() : null;
return "at " + (location != null ? location.toString() : "");
}
/**
* A shortcut for printing the result of {@link #makeGraphVizGraph} into the standard output.
*/
public static void printGraphVizGraph(Trie, BindingInfo>> trie) {
// System.out.println("https://somegraphvizurl/#" + URLEncoder.encode(makeGraphVizGraph(trie), "utf-8").replaceAll("\\+", "%20"));
System.out.println(makeGraphVizGraph(trie));
}
/**
* Makes a GraphViz graph representation of the binding graph.
* Scopes are grouped nicely into subgraph boxes and dependencies are properly drawn from lower to upper scopes.
*/
public static String makeGraphVizGraph(Trie, BindingInfo>> trie) {
StringBuilder sb = new StringBuilder();
sb.append("digraph {\n rankdir=BT;\n");
Set>> known = new HashSet<>();
writeNodes(UNSCOPED, trie, known, "", new int[]{0}, sb);
writeEdges(UNSCOPED, trie, known, sb);
sb.append("}\n");
return sb.toString();
}
private static void writeNodes(Scope[] scope, Trie, BindingInfo>> trie, Set>> known, String indent, int[] scopeCount, StringBuilder sb) {
if (scope != UNSCOPED) {
sb.append('\n').append(indent)
.append("subgraph cluster_").append(scopeCount[0]++).append(" {\n")
.append(indent).append("\tlabel=\"").append(scope[scope.length - 1].getDisplayString().replace("\"", "\\\"")).append("\"\n");
}
for (Entry, BindingInfo>>> entry : trie.getChildren().entrySet()) {
writeNodes(next(scope, entry.getKey()), entry.getValue(), known, indent + '\t', scopeCount, sb);
}
Set> leafs = new HashSet<>();
for (Entry, BindingInfo> entry : trie.get().entrySet()) {
Key> key = entry.getKey();
BindingInfo bindingInfo = entry.getValue();
if (bindingInfo.getDependencies().size() == 0) {
leafs.add(key);
}
known.add(ScopedValue.of(scope, key));
sb.append(indent)
.append('\t')
.append('"').append(getScopeId(scope)).append(key.toString().replace("\"", "\\\"")).append('"')
.append(" [label=\"").append(key.getDisplayString().replace("\"", "\\\""))
.append("\"")
.append(bindingInfo.getType() == TRANSIENT ? " style=dotted" : bindingInfo.getType() == EAGER ? " style=bold" : "")
.append("];\n");
}
if (!leafs.isEmpty()) {
sb.append(leafs.stream()
.map(key -> '"' + getScopeId(scope) + key.toString().replace("\"", "\\\"") + '"')
.collect(joining(" ", '\n' + indent + "\t{ rank=same; ", " }\n")));
if (scope == UNSCOPED) {
sb.append('\n');
}
}
if (scope != UNSCOPED) {
sb.append(indent).append("}\n\n");
}
}
private static void writeEdges(Scope[] scope, Trie, BindingInfo>> trie, Set>> known, StringBuilder sb) {
String scopePath = getScopeId(scope);
for (Entry, BindingInfo> entry : trie.get().entrySet()) {
String key = "\"" + scopePath + entry.getKey().toString().replace("\"", "\\\"") + "\"";
for (Dependency dependency : entry.getValue().getDependencies()) {
Key> depKey = dependency.getKey();
Scope[] depScope = scope;
while (!known.contains(ScopedValue.of(depScope, depKey)) && depScope.length != 0) {
depScope = Arrays.copyOfRange(depScope, 0, depScope.length - 1);
}
if (depScope.length == 0) {
String dep = "\"" + getScopeId(depScope) + depKey.toString().replace("\"", "\\\"") + '"';
if (known.add(ScopedValue.of(depScope, depKey))) {
sb.append('\t')
.append(dep)
.append(" [label=\"")
.append(depKey.getDisplayString().replace("\"", "\\\""))
.append("\" style=dashed, color=")
.append(dependency.isRequired() ? "red" : "orange")
.append("];\n");
}
sb.append('\t').append(key).append(" -> ").append(dep);
} else {
sb.append('\t').append(key).append(" -> \"").append(getScopeId(depScope)).append(depKey.toString().replace("\"", "\\\"")).append('"');
}
sb.append(" [");
if (!dependency.isRequired()) {
sb.append("style=dashed,");
}
if (dependency.isImplicit()) {
sb.append("color=gray");
}
sb.append("];\n");
}
}
for (Entry, BindingInfo>>> entry : trie.getChildren().entrySet()) {
writeEdges(next(scope, entry.getKey()), entry.getValue(), known, sb);
}
}
private static String getScopeId(Scope[] scope) {
return Arrays.stream(scope).map(Scope::toString).collect(joining("->", "()->", "")).replace("\"", "\\\"");
}
public static int getKeyDisplayCenter(Key> key) {
Name name = key.getName();
int nameOffset = name != null ? name.getDisplayString().length() + 1 : 0;
return nameOffset + (key.getDisplayString().length() - nameOffset) / 2;
}
public static String drawCycle(Key>[] cycle) {
int offset = getKeyDisplayCenter(cycle[0]);
String cycleString = Arrays.stream(cycle).map(Key::getDisplayString).collect(joining(" -> ", "\t", ""));
String indent = new String(new char[offset]).replace('\0', ' ');
String line = new String(new char[cycleString.length() - offset]).replace('\0', '-');
return cycleString + " -,\n\t" + indent + "^" + line + "'";
}
}