package tc.oc.pgm.module; import java.util.Optional; import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; import com.google.common.collect.ImmutableSet; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; import com.google.inject.spi.Dependency; import com.google.inject.spi.HasDependencies; import tc.oc.commons.core.inject.Keys; import tc.oc.commons.core.inject.Transformer; import tc.oc.commons.core.stream.Collectors; import tc.oc.commons.core.util.Streams; public class ModuleDependencyTransformer<M> implements Transformer<Optional<M>>, HasDependencies { private final Set<Key<?>> requiresKeys; private final Set<Key<Optional<?>>> dependsKeys, followsKeys; private final Set<Dependency<?>> dependencies; public ModuleDependencyTransformer(TypeLiteral<M> type) { final ModuleDescription annotation = type.getRawType().getAnnotation(ModuleDescription.class); if(annotation == null) { requiresKeys = ImmutableSet.of(); dependsKeys = followsKeys = ImmutableSet.of(); } else { requiresKeys = Keys.get(annotation.requires()); dependsKeys = Keys.optional(annotation.depends()); followsKeys = Keys.optional(annotation.follows()); } dependencies = Streams.concat(requiresKeys.stream(), dependsKeys.stream(), followsKeys.stream()) .map(Dependency::get) .collect(Collectors.toImmutableSet()); } private Set<Provider<?>> requiresProviders; private Set<Provider<Optional<?>>> dependsProviders; private Set<Provider<Optional<?>>> followsProviders; @Inject void buildProviders(Injector injector) { requiresProviders = getProviders(injector, (Set) requiresKeys); dependsProviders = getProviders(injector, dependsKeys); followsProviders = getProviders(injector, followsKeys); } private <D> Set<Provider<D>> getProviders(Injector injector, Set<Key<D>> keys) { return keys.stream() .map(injector::getProvider) .collect(Collectors.toImmutableSet()); } @Override public Set<Dependency<?>> getDependencies() { return dependencies; } @Override public Optional<M> transform(Provider<Optional<M>> provider) { // Provision these or die trying requiresProviders.forEach(Provider::get); // If any of these are empty, so are we if(!dependsProviders.stream().allMatch(p -> p.get().isPresent())) { return Optional.empty(); } // Try to provision these, but we don't care if they are empty followsProviders.forEach(Provider::get); return provider.get(); } }