All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datakernel.di.core.Binding 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.

The newest version!
package io.datakernel.di.core;

import io.datakernel.di.impl.*;
import io.datakernel.di.util.Constructors.*;
import io.datakernel.di.util.LocationInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.*;
import java.util.stream.Stream;

import static io.datakernel.di.util.Utils.union;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;

/**
 * A binding is one of the main components of DataKernel DI.
 * It boils down to "introspectable function", since it only describes
 * a {@link BindingCompiler function} to create an instance of T from an array of objects and
 * an array of its {@link Dependency dependencies} in known terms.
 * 

* Also it contains a set of {@link io.datakernel.di.module.AbstractModule binding-DSL-like} static factory methods * as well as some functional transformations for the ease of creating immutable binding modifications. */ @SuppressWarnings({"unused", "WeakerAccess", "ArraysAsListWithZeroOrOneArgument", "Convert2Lambda"}) public final class Binding { private final Set dependencies; private final BindingCompiler compiler; @Nullable private LocationInfo location; public Binding(@NotNull Set dependencies, @NotNull BindingCompiler compiler) { this(dependencies, compiler, null); } private Binding(@NotNull Set dependencies, @NotNull BindingCompiler compiler, @Nullable LocationInfo location) { this.dependencies = dependencies; this.compiler = compiler; this.location = location; } public static Binding toInstance(@NotNull T instance) { return new Binding<>(emptySet(), (compiledBindings, threadsafe, scope, slot) -> slot != null ? new CompiledBinding() { @SuppressWarnings("unchecked") @Override public T getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { scopedInstances[scope].lazySet(slot, instance); return instance; } } : new CompiledBinding() { @Override public T getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return instance; } }); } public static Binding toSupplier(@NotNull Key> supplierKey) { return Binding.to(Supplier::get, supplierKey); } public static Binding toSupplier(@NotNull Class> supplierType) { return Binding.to(Supplier::get, supplierType); } // region Various Binding.to(...) overloads public static Binding to(Class key) { return Binding.to(Key.of(key)); } public static Binding to(Key key) { return new Binding<>(singleton(Dependency.toKey(key)), new PlainCompiler<>(key)); } public static Binding to(@NotNull ConstructorN constructor, @NotNull Class[] types) { return Binding.to(constructor, Stream.of(types).map(Key::of).map(Dependency::toKey).toArray(Dependency[]::new)); } public static Binding to(@NotNull ConstructorN constructor, @NotNull Key[] keys) { return Binding.to(constructor, Stream.of(keys).map(Dependency::toKey).toArray(Dependency[]::new)); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull ConstructorN constructor, @NotNull Dependency[] dependencies) { if (dependencies.length == 0) { return to(constructor::create); } return new Binding<>(new HashSet<>(asList(dependencies)), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding[] bindings = Arrays.stream(dependencies) .map(dependency -> compiledBindings.get(dependency.getKey())) .toArray(CompiledBinding[]::new); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } return constructor.create(args); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } return constructor.create(args); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } return constructor.create(args); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } return constructor.create(args); } }; }); } public static Binding to(@NotNull Constructor1 constructor, @NotNull Class dependency1) { return Binding.to(constructor, Key.of(dependency1)); } public static Binding to(@NotNull Constructor2 constructor, @NotNull Class dependency1, @NotNull Class dependency2) { return Binding.to(constructor, Key.of(dependency1), Key.of(dependency2)); } public static Binding to(@NotNull Constructor3 constructor, @NotNull Class dependency1, @NotNull Class dependency2, @NotNull Class dependency3) { return Binding.to(constructor, Key.of(dependency1), Key.of(dependency2), Key.of(dependency3)); } public static Binding to(@NotNull Constructor4 constructor, @NotNull Class dependency1, @NotNull Class dependency2, @NotNull Class dependency3, @NotNull Class dependency4) { return Binding.to(constructor, Key.of(dependency1), Key.of(dependency2), Key.of(dependency3), Key.of(dependency4)); } public static Binding to(@NotNull Constructor5 constructor, @NotNull Class dependency1, @NotNull Class dependency2, @NotNull Class dependency3, @NotNull Class dependency4, @NotNull Class dependency5) { return Binding.to(constructor, Key.of(dependency1), Key.of(dependency2), Key.of(dependency3), Key.of(dependency4), Key.of(dependency5)); } public static Binding to(@NotNull Constructor6 constructor, @NotNull Class dependency1, @NotNull Class dependency2, @NotNull Class dependency3, @NotNull Class dependency4, @NotNull Class dependency5, @NotNull Class dependency6) { return Binding.to(constructor, Key.of(dependency1), Key.of(dependency2), Key.of(dependency3), Key.of(dependency4), Key.of(dependency5), Key.of(dependency6)); } public static Binding to(@NotNull Constructor0 constructor) { return new Binding<>(emptySet(), (compiledBindings, threadsafe, scope, slot) -> slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } } : new AbstractCompiledBinding(scope, slot) { @Nullable @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } }); } public static Binding to(@NotNull Constructor1 constructor, @NotNull Key dependency1) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } }; }); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull Constructor2 constructor, @NotNull Key dependency1, @NotNull Key dependency2) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1), Dependency.toKey(dependency2))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } }; }); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull Constructor3 constructor, @NotNull Key dependency1, @NotNull Key dependency2, @NotNull Key dependency3) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1), Dependency.toKey(dependency2), Dependency.toKey(dependency3))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope)); } }; }); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull Constructor4 constructor, @NotNull Key dependency1, @NotNull Key dependency2, @NotNull Key dependency3, @NotNull Key dependency4) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1), Dependency.toKey(dependency2), Dependency.toKey(dependency3), Dependency.toKey(dependency4))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope)); } }; }); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull Constructor5 constructor, @NotNull Key dependency1, @NotNull Key dependency2, @NotNull Key dependency3, @NotNull Key dependency4, @NotNull Key dependency5) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1), Dependency.toKey(dependency2), Dependency.toKey(dependency3), Dependency.toKey(dependency4), Dependency.toKey(dependency5))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); final CompiledBinding binding5 = compiledBindings.get(dependency5); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope)); } }; }); } @SuppressWarnings("Duplicates") public static Binding to(@NotNull Constructor6 constructor, @NotNull Key dependency1, @NotNull Key dependency2, @NotNull Key dependency3, @NotNull Key dependency4, @NotNull Key dependency5, @NotNull Key dependency6) { return new Binding<>(new HashSet<>(asList(Dependency.toKey(dependency1), Dependency.toKey(dependency2), Dependency.toKey(dependency3), Dependency.toKey(dependency4), Dependency.toKey(dependency5), Dependency.toKey(dependency6))), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); final CompiledBinding binding5 = compiledBindings.get(dependency5); final CompiledBinding binding6 = compiledBindings.get(dependency6); return slot != null ? threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope), binding6.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope), binding6.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, slot) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope), binding6.getInstance(scopedInstances, synchronizedScope)); } } : new CompiledBinding() { @Override public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope), binding4.getInstance(scopedInstances, synchronizedScope), binding5.getInstance(scopedInstances, synchronizedScope), binding6.getInstance(scopedInstances, synchronizedScope)); } }; }); } // endregion public Binding at(@Nullable LocationInfo location) { this.location = location; return this; } public Binding onInstance(@NotNull Consumer consumer) { return mapInstance(null, (args, instance) -> { consumer.accept(instance); return instance; }); } public Binding mapInstance(@NotNull Function fn) { return mapInstance(null, (args, instance) -> fn.apply(instance)); } @SuppressWarnings("Duplicates") public Binding mapInstance(@Nullable List> dependencies, @NotNull BiFunction fn) { if (dependencies != null) { Set> missing = dependencies.stream() .filter(required -> this.dependencies.stream().noneMatch(existing -> existing.getKey().equals(required))) .collect(toSet()); if (!missing.isEmpty()) { throw new DIException(missing.stream() .map(Key::getDisplayString) .collect(joining(", ", "Binding has no dependencies ", " required by mapInstance call"))); } } return new Binding<>(this.dependencies, (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding originalBinding = compiler.compile(compiledBindings, threadsafe, scope, null); final CompiledBinding[] bindings = dependencies != null ? dependencies.stream().map(compiledBindings::get).toArray(CompiledBinding[]::new) : null; return bindings != null ? slot != null ? new AbstractCompiledBinding(scope, slot) { @Override @Nullable protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } T instance = originalBinding.getInstance(scopedInstances, synchronizedScope); return instance != null ? fn.apply(args, instance) : null; } } : new CompiledBinding() { @Override @Nullable public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = new Object[bindings.length]; for (int i = 0; i < bindings.length; i++) { args[i] = bindings[i].getInstance(scopedInstances, synchronizedScope); } T instance = originalBinding.getInstance(scopedInstances, synchronizedScope); return instance != null ? fn.apply(args, instance) : null; } } : slot != null ? new AbstractCompiledBinding(scope, slot) { @Override @Nullable protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { T instance = originalBinding.getInstance(scopedInstances, synchronizedScope); return instance != null ? fn.apply(null, instance) : null; } } : new CompiledBinding() { @Override @Nullable public R getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { T instance = originalBinding.getInstance(scopedInstances, synchronizedScope); return instance != null ? fn.apply(null, instance) : null; } }; }, location); } public Binding onDependency(@NotNull Class dependency, @NotNull Consumer consumer) { return onDependency(Key.of(dependency), consumer); } public Binding onDependency(@NotNull Key dependency, @NotNull Consumer consumer) { return mapDependency(dependency, v -> { consumer.accept(v); return v; }); } public Binding mapDependency(@NotNull Class dependency, @NotNull Function fn) { return mapDependency(Key.of(dependency), fn); } @SuppressWarnings("unchecked") public Binding mapDependency(@NotNull Key dependency, @NotNull Function fn) { return new Binding<>(dependencies, (compiledBindings, threadsafe, scope, slot) -> compiler.compile(new CompiledBindingLocator() { @Override public @NotNull CompiledBinding get(Key key) { CompiledBinding originalBinding = compiledBindings.get(key); if (!key.equals(dependency)) return originalBinding; return new CompiledBinding() { @Nullable @Override public Q getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Q instance = originalBinding.getInstance(scopedInstances, synchronizedScope); return (Q) fn.apply((K) instance); } }; } }, threadsafe, scope, slot), location); } public Binding addDependencies(@NotNull Class... extraDependencies) { return addDependencies(Stream.of(extraDependencies).map(Key::of).map(Dependency::toKey).toArray(Dependency[]::new)); } public Binding addDependencies(@NotNull Key... extraDependencies) { return addDependencies(Stream.of(extraDependencies).map(Dependency::toKey).toArray(Dependency[]::new)); } public Binding addDependencies(@NotNull Dependency... extraDependencies) { return addDependencies(Stream.of(extraDependencies).collect(toSet())); } public Binding addDependencies(@NotNull Set extraDependencies) { return extraDependencies.isEmpty() ? this : new Binding<>(union(dependencies, extraDependencies), (compiledBindings, threadsafe, scope, slot) -> { CompiledBinding compiledBinding = compiler.compile(compiledBindings, threadsafe, scope, slot); CompiledBinding[] compiledExtraBindings = extraDependencies.stream().map(d -> compiledBindings.get(d.getKey())).toArray(CompiledBinding[]::new); return new CompiledBinding() { @Override public T getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { for (CompiledBinding compiledExtraBinding : compiledExtraBindings) { compiledExtraBinding.getInstance(scopedInstances, synchronizedScope); } return compiledBinding.getInstance(scopedInstances, synchronizedScope); } }; }, location); } public Binding rebindDependency(@NotNull Key from, @NotNull Key to) { return rebindDependencies(singletonMap(from, to)); } @SuppressWarnings("unchecked") public Binding rebindDependencies(@NotNull Map, Key> map) { if (map.isEmpty()) { return this; } return rebindDependenciesImpl( map.keySet(), map.values().stream().map(Dependency::toKey).collect(toSet()), key -> (compiledBindings, threadsafe, scope, slot) -> (CompiledBinding) compiledBindings.get(map.getOrDefault(key, key))); } private Binding rebindDependenciesImpl(@NotNull Set> removedDependencies, @NotNull Set addedDependencies, @NotNull Function, BindingCompiler> fn) { Set> missing = removedDependencies.stream() .filter(required -> dependencies.stream().noneMatch(existing -> existing.getKey().equals(required))) .collect(toSet()); if (!missing.isEmpty()) { throw new DIException(missing.stream() .map(Key::getDisplayString) .collect(joining(", ", "Binding has no dependencies [", "] required by the rebind call"))); } HashSet newDependencies = new HashSet<>(dependencies); newDependencies.removeIf(dependency -> removedDependencies.contains(dependency.getKey())); newDependencies.addAll(addedDependencies); return new Binding<>(newDependencies, (compiledBindings, threadsafe, scope, slot) -> compiler.compile( new CompiledBindingLocator() { @SuppressWarnings("unchecked") @Override public @NotNull CompiledBinding get(Key key) { BindingCompiler compiler = (BindingCompiler) fn.apply(key); return compiler.compile(compiledBindings, threadsafe, scope, slot); } }, threadsafe, scope, slot), location); } public Binding initializeWith(BindingInitializer bindingInitializer) { return bindingInitializer == BindingInitializer.noop() ? this : new Binding<>(union(dependencies, bindingInitializer.getDependencies()), (compiledBindings, threadsafe, scope, slot) -> { final CompiledBinding compiledBinding = compiler.compile(compiledBindings, threadsafe, scope, null); final CompiledBindingInitializer consumer = bindingInitializer.getCompiler().compile(compiledBindings); return slot != null ? new AbstractCompiledBinding(scope, slot) { @Override protected T doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { T instance = compiledBinding.getInstance(scopedInstances, synchronizedScope); consumer.initInstance(instance, scopedInstances, synchronizedScope); return instance; } } : new CompiledBinding() { @Override public T getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { T instance = compiledBinding.getInstance(scopedInstances, synchronizedScope); consumer.initInstance(instance, scopedInstances, synchronizedScope); return instance; } }; }, location); } @NotNull public Set getDependencies() { return dependencies; } @NotNull public Set> getDependencyKeys() { return dependencies.stream().map(Dependency::getKey).collect(toSet()); } public boolean hasDependency(Key dependency) { return dependencies.stream().map(Dependency::getKey).anyMatch(Predicate.isEqual(dependency)); } @NotNull public BindingCompiler getCompiler() { return compiler; } @Nullable public LocationInfo getLocation() { return location; } public String getDisplayString() { return dependencies.stream().map(Dependency::getDisplayString).collect(joining(", ", "[", "]")); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Binding binding = (Binding) o; return dependencies.equals(binding.dependencies) && compiler.equals(binding.compiler); } @Override public int hashCode() { return 31 * dependencies.hashCode() + compiler.hashCode(); } @Override public String toString() { return "Binding" + dependencies.toString(); } }