ProjectAres/PGM/src/main/java/tc/oc/pgm/module/ModuleContext.java

163 lines
5.0 KiB
Java

package tc.oc.pgm.module;
import com.google.inject.Injector;
import com.google.inject.Key;
import tc.oc.commons.core.inject.Injection;
import tc.oc.commons.core.inject.InjectionScopable;
import tc.oc.commons.core.inject.InjectionScope;
import tc.oc.commons.core.inject.InjectionStore;
import tc.oc.commons.core.inject.Keys;
import tc.oc.commons.core.logging.Loggers;
import tc.oc.commons.core.util.ThrowingRunnable;
import tc.oc.commons.core.util.ThrowingSupplier;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
/**
* Loads and stores modules descended from some base type {@link Base}, as well as the
* {@link InjectionStore} for a scope with annotation {@link Scope}.
*
* This is mostly a relic of the old module system and doesn't do much anymore. It
* just provisions all known modules at load time and keeps a list of the ones
* that loaded.
*
* The module getter methods in this class should NOT be used by new code. All they
* do is delegate to the {@link Injector}, and anything they provide can just be
* injected directly.
*/
public abstract class ModuleContext<Base, Scope extends Annotation> implements ModuleExceptionHandler, InjectionScopable<Scope> {
@Inject protected Injector injector;
@Inject protected InjectionScope<Scope> injectionScope;
@Inject protected InjectionStore<Scope> injectionStore;
@Inject protected Collection<Provider<ProvisionWrapper<? extends Base>>> moduleProviders;
protected Logger logger;
@Inject void initLogger(Loggers loggers) {
logger = loggers.get(getClass());
}
private final List<Base> loadedModules = new ArrayList<>();
private final List<ModuleLoadException> errors = new ArrayList<>();
public Logger logger() {
return logger;
}
@Override
public InjectionScope<Scope> injectionScope() {
return injectionScope;
}
@Override
public InjectionStore<Scope> injectionStore() {
return injectionStore;
}
public void load() {
asCurrentScope(() -> moduleProviders.forEach(Provider::get));
}
/**
* Return all modules in dependency order
*/
public Collection<Base> loadedModules() {
return loadedModules;
}
private <T> T getInstance(Key<T> key) {
// Check the store directly first, so we can avoid a scope change most of the time
return injectionStore.provide(key, () ->
this.<T, RuntimeException>asCurrentScope(
() -> injector.getInstance(key)
)
);
}
public boolean hasModule(Class<? extends Base> type) {
return module(type).isPresent();
}
public <M extends Base> Optional<M> module(Class<M> type) {
return getInstance(Keys.optional(type));
}
public @Nullable <M extends Base> M getModule(Class<M> type) {
return module(type).orElse(null);
}
public <M extends Base> M needModule(Class<M> type) {
return getInstance(Key.get(type));
}
@Override
public void propagatingFailures(ThrowingRunnable<ModuleLoadException> block) {
try {
Injection.unwrappingExceptions(ModuleLoadException.class, block);
} catch(ModuleLoadException e) {
addError(e);
throw new UpstreamProvisionFailure();
}
}
@Override
public <T> T propagatingFailures(ThrowingSupplier<T, ModuleLoadException> block) {
try {
return Injection.unwrappingExceptions(ModuleLoadException.class, block);
} catch(ModuleLoadException e) {
addError(e);
throw new UpstreamProvisionFailure();
}
}
@Override
public void ignoringFailures(ThrowingRunnable<ModuleLoadException> block) {
try {
propagatingFailures(block);
} catch(UpstreamProvisionFailure ignored) {}
}
@Override
public <T> Optional<T> ignoringFailures(ThrowingSupplier<T, ModuleLoadException> block) {
try {
return propagatingFailures(() -> Optional.of(block.getThrows()));
} catch(UpstreamProvisionFailure ignored) {
return Optional.empty();
}
}
<M extends Base> Optional<M> loadModule(ThrowingSupplier<Optional<M>, ModuleLoadException> loader) {
final Optional<M> module = propagatingFailures(loader);
module.ifPresent(this::addModule);
return module;
}
protected void addModule(Base module) {
loadedModules.add(module);
}
public List<ModuleLoadException> getErrors() {
return errors;
}
public boolean hasErrors() {
return !errors.isEmpty();
}
protected void addError(ModuleLoadException e) {
this.errors.add(e);
}
protected void addErrors(Collection<? extends ModuleLoadException> errors) {
this.errors.addAll(errors);
}
}