io.datakernel.di.module.AbstractModule Maven / Gradle / Ivy
package io.datakernel.di.module;
import io.datakernel.di.core.*;
import io.datakernel.di.util.ReflectionUtils;
import io.datakernel.di.util.Trie;
import io.datakernel.di.util.Types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
import static io.datakernel.di.util.Utils.checkState;
/**
* This class is an abstract module wrapper around {@link ModuleBuilder}.
* It provides functionality that is similar to some other DI frameworks for the ease of transition.
*/
@SuppressWarnings({"SameParameterValue", "UnusedReturnValue"})
public abstract class AbstractModule implements Module {
private Trie, Set>>> bindings;
private Map>> bindingTransformers;
private Map, Set>> bindingGenerators;
private Map, Multibinder>> multibinders;
private final AtomicBoolean configured = new AtomicBoolean();
@Nullable
private ModuleBuilder builder = null;
@Nullable
private final StackTraceElement location;
public AbstractModule() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
StackTraceElement found = null;
Class> cls = getClass();
for (int i = 2; i < trace.length; i++) {
StackTraceElement element = trace[i];
try {
String className = element.getClassName();
Class> traceCls = Class.forName(className);
if (!traceCls.isAssignableFrom(cls) && !className.startsWith("sun.reflect") && !className.startsWith("java.lang")) {
found = element;
break;
}
} catch (ClassNotFoundException ignored) {
break;
}
}
location = found;
}
/**
* This method is meant to be overridden to call all the bind(...) methods.
*/
protected void configure() {
}
/**
* @see ModuleBuilder#bind(Key)
*/
protected final ModuleBuilderBinder bind(@NotNull Key key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bind(Key.ofType(Types.resolveTypeVariables(key.getType(), getClass()), key.getName()));
}
/**
* @see ModuleBuilder#bind(Key)
*/
protected final ModuleBuilderBinder bind(Class type) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bind(type);
}
/**
* @see ModuleBuilder#bind(Key)
*/
protected final ModuleBuilderBinder bind(Class type, Name name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bind(type, name);
}
/**
* @see ModuleBuilder#bind(Key)
*/
protected final ModuleBuilderBinder bind(Class type, String name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bind(type, name);
}
/**
* @see ModuleBuilder#bind(Key)
*/
protected final ModuleBuilderBinder bind(Class type, Class extends Annotation> annotationType) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bind(type, annotationType);
}
protected final ModuleBuilder bindInstanceProvider(@NotNull Class key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceProvider(key);
}
protected final ModuleBuilder bindInstanceProvider(@NotNull Key key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceProvider(key);
}
protected final ModuleBuilder bindInstanceFactory(@NotNull Class key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceFactory(key);
}
protected final ModuleBuilder bindInstanceFactory(@NotNull Key key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceFactory(key);
}
protected final ModuleBuilder bindInstanceInjector(@NotNull Class key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceInjector(key);
}
protected final ModuleBuilder bindInstanceInjector(@NotNull Key key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
return builder.bindInstanceInjector(Key.ofType(Types.parameterized(InstanceInjector.class, key.getType()), key.getName()));
}
/**
* @see ModuleBuilder#install
*/
protected final void install(Module module) {
checkState(builder != null, "Cannot install modules before or after configure() call");
builder.install(module);
}
/**
* @see ModuleBuilder#transform
*/
protected final void transform(int priority, BindingTransformer bindingTransformer) {
checkState(builder != null, "Cannot add transformers before or after configure() call");
builder.transform(priority, bindingTransformer);
}
/**
* @see ModuleBuilder#generate
*/
protected final void generate(Class> pattern, BindingGenerator bindingGenerator) {
checkState(builder != null, "Cannot add generators before or after configure() call");
builder.generate(pattern, bindingGenerator);
}
/**
* @see ModuleBuilder#multibind
*/
protected final void multibind(Key key, Multibinder multibinder) {
checkState(builder != null, "Cannot add multibinders before or after configure() call");
builder.multibind(key, multibinder);
}
protected final void multibindToSet(Class type) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToSet(type);
}
protected final void multibindToSet(Class type, String name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToSet(type, name);
}
protected final void multibindToSet(Class type, Name name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToSet(type, name);
}
protected final void multibindToSet(Key key) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToSet(key);
}
protected final void multibindToMap(Class keyType, Class valueType) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToMap(keyType, valueType);
}
protected final void multibindToMap(Class keyType, Class valueType, String name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToMap(keyType, valueType, name);
}
protected final void multibindToMap(Class keyType, Class valueType, Name name) {
checkState(builder != null, "Cannot add bindings before or after configure() call");
builder.multibindToMap(keyType, valueType, name);
}
/**
* @see ModuleBuilder#bindIntoSet(Key, Binding)
*/
protected final void bindIntoSet(Key setOf, Binding binding) {
checkState(builder != null, "Cannot bind into set before or after configure() call");
builder.bindIntoSet(setOf, binding);
}
/**
* @see ModuleBuilder#bindIntoSet(Key, Binding)
*/
protected final void bindIntoSet(Key setOf, Key item) {
bindIntoSet(setOf, Binding.to(item));
}
/**
* @see ModuleBuilder#bindIntoSet(Key, Binding)
*/
protected final void bindIntoSet(@NotNull Key setOf, @NotNull T element) {
bindIntoSet(setOf, Binding.toInstance(element));
}
/**
* @see ModuleBuilder#postInjectInto(Key)
*/
protected final void postInjectInto(Key key) {
checkState(builder != null, "Cannot post inject into something before or after configure() call");
builder.postInjectInto(key);
}
/**
* @see ModuleBuilder#postInjectInto(Key)
*/
protected final void postInjectInto(Class type) {
postInjectInto(Key.of(type));
}
protected final void scan(Object object) {
checkState(builder != null, "Cannot add declarative bindings before or after configure() call");
builder.scan(object);
}
protected final void scan(Class> cls) {
checkState(builder != null, "Cannot add declarative bindings before or after configure() call");
builder.scan(cls);
}
private void finish() {
if (!configured.compareAndSet(false, true)) {
return;
}
ModuleBuilder b = Module.create().scan(getClass().getSuperclass(), this);
ReflectionUtils.scanClassInto(getClass(), this, b); // so that provider methods and dsl bindings are in one 'export area'
builder = b;
configure();
builder = null;
bindings = b.getBindings();
bindingTransformers = b.getBindingTransformers();
bindingGenerators = b.getBindingGenerators();
multibinders = b.getMultibinders();
}
@Override
public final Trie, Set>>> getBindings() {
finish();
return bindings;
}
@Override
public final Map>> getBindingTransformers() {
finish();
return bindingTransformers;
}
@Override
public final Map, Set>> getBindingGenerators() {
finish();
return bindingGenerators;
}
@Override
public final Map, Multibinder>> getMultibinders() {
finish();
return multibinders;
}
// region forbid overriding default module methods
@Override
public Module combineWith(Module another) {
return Module.super.combineWith(another);
}
@Override
public Module overrideWith(Module another) {
return Module.super.overrideWith(another);
}
@Override
public Module transformWith(UnaryOperator fn) {
return Module.super.transformWith(fn);
}
@Override
public Module export(Key> key, Key>... keys) {
return Module.super.export(key, keys);
}
@Override
public Module export(Set> keys) {
return Module.super.export(keys);
}
@Override
public Module rebindExport(Key from, Key extends V> to) {
return Module.super.rebindExport(from, to);
}
@Override
public Module rebindImport(Key from, Key extends V> to) {
return Module.super.rebindImport(from, to);
}
@Override
public Module rebindImport(Key from, Binding extends V> binding) {
return Module.super.rebindImport(from, binding);
}
@Override
public Module rebindExports(@NotNull Map, Key>> mapping) {
return Module.super.rebindExports(mapping);
}
@Override
public Module rebindImports(@NotNull Map, Binding>> mapping) {
return Module.super.rebindImports(mapping);
}
@Override
public Module rebindImportKeys(@NotNull Map, Key>> mapping) {
return Module.super.rebindImportKeys(mapping);
}
@Override
public Module rebindImportDependencies(Key key, Key dependency, Key extends V> to) {
return Module.super.rebindImportDependencies(key, dependency, to);
}
@Override
public Module rebindImportDependencies(Key key, @NotNull Map, Key>> dependencyMapping) {
return Module.super.rebindImportDependencies(key, dependencyMapping);
}
@Override
public Module rebindImports(BiFunction, Binding>, Binding>> rebinder) {
return Module.super.rebindImports(rebinder);
}
// endregion
@Override
public String toString() {
Class> cls = getClass();
return ReflectionUtils.getShortName(cls.isAnonymousClass() ? cls.getGenericSuperclass() : cls) +
"(at " + (location != null ? location : "") + ')';
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy