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

io.datakernel.di.core.Binding Maven / Gradle / Ivy

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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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"}) 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, null, compiler); } public Binding(@NotNull Set dependencies, @Nullable LocationInfo location, @NotNull BindingCompiler compiler) { this.dependencies = dependencies; this.compiler = compiler; this.location = location; } public static Binding toInstance(@NotNull T instance) { return new Binding<>(emptySet(), (compiledBindings, threadsafe, scope, index) -> new CompiledBinding() { @SuppressWarnings("unchecked") @Override public T getInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { scopedInstances[scope].lazySet(index, instance); return instance; } @Override public T createInstance(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)); } 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, index) -> { CompiledBinding[] bindings = new CompiledBinding[dependencies.length]; for (int i = 0; i < dependencies.length; i++) { bindings[i] = compiledBindings.get(dependencies[i].getKey()); } return threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { @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, index) { @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, index) { @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); } }; }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } } : new AbstractCompiledBinding(scope, index) { @Override public R createInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return doCreateInstance(scopedInstances, synchronizedScope); } @Nullable @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create(); } } : new AbstractUnsyncCompiledBinding(scope, index) { @Override protected R doCreateInstance(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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); @Override public R createInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return doCreateInstance(scopedInstances, synchronizedScope); } @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope)); } }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractCompiledBinding(scope, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } } : new AbstractUnsyncCompiledBinding(scope, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope)); } }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); @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, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); @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, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { return constructor.create( binding1.getInstance(scopedInstances, synchronizedScope), binding2.getInstance(scopedInstances, synchronizedScope), binding3.getInstance(scopedInstances, synchronizedScope)); } }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); @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, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); @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, index) { final CompiledBinding binding1 = compiledBindings.get(dependency1); final CompiledBinding binding2 = compiledBindings.get(dependency2); final CompiledBinding binding3 = compiledBindings.get(dependency3); final CompiledBinding binding4 = compiledBindings.get(dependency4); @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)); } }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { 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); @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, index) { 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); @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, index) { 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); @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)); } }); } 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, index) -> threadsafe ? scope == 0 ? new AbstractRootCompiledBinding(index) { 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); @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, index) { 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); @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, index) { 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); @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)); } }); } // 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)); } 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, location, (compiledBindings, threadsafe, scope, index) -> new AbstractCompiledBinding(scope, index) { final CompiledBinding originalBinding = compiler.compile(compiledBindings, threadsafe, scope, index); final CompiledBinding[] bindings = dependencies == null ? null : dependencies.stream().map(compiledBindings::get).toArray(CompiledBinding[]::new); @Nullable @Override protected R doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Object[] args = null; if (bindings != null) { 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; } }); } 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, location, (compiledBindings, threadsafe, scope, index) -> 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); } @Nullable @Override public Q createInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { Q instance = originalBinding.createInstance(scopedInstances, synchronizedScope); return (Q) fn.apply((K) instance); } }; } }, threadsafe, scope, index)); } 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), location, (compiledBindings, threadsafe, scope, index) -> { CompiledBinding compiledBinding = compiler.compile(compiledBindings, threadsafe, scope, index); 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); } @Override public T createInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { for (CompiledBinding compiledExtraBinding : compiledExtraBindings) { compiledExtraBinding.getInstance(scopedInstances, synchronizedScope); } return compiledBinding.createInstance(scopedInstances, synchronizedScope); } }; }); } 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, index) -> (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, location, (compiledBindings, threadsafe, scope, index) -> 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, index); } }, threadsafe, scope, index)); } public Binding initializeWith(BindingInitializer bindingInitializer) { return bindingInitializer == BindingInitializer.noop() ? this : new Binding<>(union(dependencies, bindingInitializer.getDependencies()), location, (compiledBindings, threadsafe, scope, index) -> new AbstractCompiledBinding(scope, index) { final CompiledBinding compiledBinding = compiler.compile(compiledBindings, threadsafe, scope, index); final CompiledBindingInitializer consumer = bindingInitializer.getCompiler().compile(compiledBindings); @Override protected T doCreateInstance(AtomicReferenceArray[] scopedInstances, int synchronizedScope) { T instance = compiledBinding.getInstance(scopedInstances, synchronizedScope); consumer.initInstance(instance, scopedInstances, synchronizedScope); return instance; } }); } @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(); } }