package tc.oc.commons.core.inject; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; import tc.oc.commons.core.reflect.ResolvableType; import tc.oc.commons.core.reflect.TypeArgument; /** * Collects {@link Module}s to be used to create child {@link Injector}s * with a {@link ChildInjectorFactory} of the same type {@link T}. The type * is only used as a key to distinguish different configurations. * * The modules are stored in a bound container that the {@link ChildInjectorFactory} * depends on. The modules are installed directly into the child injector, * as if they were passed to {@link Injector#createChildInjector}. * * TODO: Reproduce the original module path in error messages from the child injector. * Guice doesn't seem to have any way to inspect or override module sources, so I have * no idea how this could be done. * * It would also be good to verify the modules up-front, at least partially. * Currently, no verification is done at all until the child injector is created. */ public class ChildConfigurator<T> { private final Key<T> key; private final TypeArgument<T> typeArg; private final Key<ChildModule<T>> childModuleKey; private final Multibinder<ChildModule<T>> deferredModules; public ChildConfigurator(Binder binder, Class<T> type) { this(binder, Key.get(type)); } public ChildConfigurator(Binder binder, TypeLiteral<T> type) { this(binder, Key.get(type)); } public ChildConfigurator(Binder binder, Key<T> key) { this.key = key; this.typeArg = new TypeArgument<T>(this.key.getTypeLiteral()){}; this.childModuleKey = this.key.ofType(new ResolvableType<ChildModule<T>>(){}.with(typeArg)); this.deferredModules = Multibinder.newSetBinder(binder, childModuleKey); } public void install(Module module) { deferredModules.addBinding().toInstance(new ChildModule<>(module)); } } class ChildModule<T> implements Module { private final Module module; ChildModule(Module module) { this.module = module; } @Override public void configure(Binder binder) { // TODO: Figure out how to show the original module path in error messages. // Guice doesn't seem to have any way to override module sources. binder.skipSources(ChildModule.class) .install(module); } @Override public int hashCode() { return module.hashCode(); } @Override public boolean equals(Object that) { return that instanceof ChildModule && this.module.equals(((ChildModule) that).module); } }