package nova.microblock.injection; import nova.core.block.Block; import nova.core.component.Component; import nova.core.component.ComponentProvider; import nova.core.util.registry.Factory; import nova.core.util.registry.FactoryManager; import nova.core.util.registry.Registry; import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; /** * Inject components across containers from the contained block * @author Calclavia */ public class ComponentInjection extends FactoryManager<ComponentInjection, ComponentInjector, ComponentInjection.ComponentInjectionFactory> { public ComponentInjection(Registry<ComponentInjectionFactory> registry) { super(registry); } /** * Injects components from the contained block to the container. * Injection should occur AFTER the contained block is placed. * @param contained The contained * @param container The container */ public void injectToContainer(Block contained, Block container) { contained .components() .stream() .forEach(component -> findInjectors(component.getClass()).forEach(injector -> injector.injectForward(component, contained, container))); //TODO: Test component added after constructor. //When future components are added to the contained, it will auto-inject to the container. contained.events.on(ComponentProvider.ComponentAdded.class).bind(evt -> findInjectors(evt.component.getClass()).forEach(injector -> injector.injectForward(evt.component, contained, container))); contained.events.on(ComponentProvider.ComponentRemoved.class).bind(evt -> container.components.remove(evt.component)); //TODO: Maybe events should not be injected this way. //Forward events to -> from (container -> contained) container.events.on().bind(contained.events::publish); } /** * Injects components from the container block to the contained. * @param contained The contained * @param container The container */ public void injectToContained(Block contained, Block container) { //Inject the special components from the container to the contained (such as BlockTransform). container .components() .stream() .forEach(component -> findInjectors(component.getClass()).forEach(injector -> injector.injectBackward(component, contained, container))); } public <C extends Component> Set<ComponentInjector> findInjectors(Class<C> clazz) { return registry .stream() .map(Factory::build) .filter(injector -> injector.componentType().isAssignableFrom(clazz)) .collect(Collectors.toSet()); } @Override public ComponentInjectionFactory register(String id, Supplier<ComponentInjector> constructor) { return register(new ComponentInjectionFactory(id, constructor)); } @Override public void init() { } public static class ComponentInjectionFactory extends Factory<ComponentInjectionFactory, ComponentInjector> { public ComponentInjectionFactory(String id, Supplier<ComponentInjector> constructor, Function<ComponentInjector, ComponentInjector> processor) { super(id, constructor, processor); } public ComponentInjectionFactory(String id, Supplier<ComponentInjector> constructor) { super(id, constructor); } @Override protected ComponentInjectionFactory selfConstructor(String id, Supplier<ComponentInjector> constructor, Function<ComponentInjector, ComponentInjector> processor) { return new ComponentInjectionFactory(id, constructor, processor); } @Override public ComponentInjector build() { ComponentInjector build = super.build(); build.factory = this; return build; } } }