package tc.oc.commons.core.inject; import java.lang.annotation.Annotation; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; import javax.inject.Inject; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Scope; import com.google.inject.TypeLiteral; import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.internal.Scoping; import com.google.inject.matcher.Matcher; import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.OptionalBinder; import com.google.inject.spi.Element; import com.google.inject.spi.Elements; import com.google.inject.spi.ProvisionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; import tc.oc.commons.core.reflect.ResolvableType; import tc.oc.commons.core.reflect.TypeArgument; import tc.oc.commons.core.reflect.TypeParameter; import tc.oc.commons.core.reflect.TypeResolver; import tc.oc.commons.core.reflect.Types; import tc.oc.commons.core.util.Functions; import tc.oc.commons.core.util.ProxyUtils; import tc.oc.inject.ForwardingBinder; public interface Binders extends ForwardingBinder { static Binders wrap(Binder binder) { if(binder instanceof Binders) { return (Binders) binder; } final Binder skipped = binder.skipSources(Binders.class, ForwardingBinder.class); return () -> skipped; } /** * Adapt a {@link Consumer} to be a {@link ProvisionListener} (which is not lambda compatible) */ static ProvisionListener provisionListener(Consumer> consumer) { return consumer::accept; } static Predicate> bindingsForSubtypesOf(TypeLiteral type) { return binding -> Types.isAssignable(type, binding.getKey().getTypeLiteral()); } static Predicate> bindingsForSubtypesOf(Class type) { return bindingsForSubtypesOf(TypeLiteral.get(type)); } default void bindSubtypesOfListener(Class type, SubtypeListener listener) { bindSubtypesOfListener(TypeLiteral.get(type), listener); } default void bindSubtypesOfListener(TypeLiteral type, SubtypeListener listener) { bindListener((Matcher>) Matchers.subtypesOf(type), new TypeListener() { @Override public void hear(TypeLiteral type, TypeEncounter encounter) { listener.hear((TypeLiteral) type, (TypeEncounter) encounter); } }); } /** * Adapt the given matcher and listener and pass them to {@link Binder#bindListener(Matcher, ProvisionListener...)} */ default void bindProvisionListener(Predicate> matcher, Consumer> listener) { bindListener(Matchers.predicate(matcher), provisionListener(listener)); } default void bindProvisionSubtypesOfListener(TypeLiteral type, Consumer> listener) { bindProvisionListener(bindingsForSubtypesOf(type), (Consumer) listener); } default void bindProvisionSubtypesOfListener(Class type, Consumer> listener) { bindProvisionSubtypesOfListener(TypeLiteral.get(type), listener); } default void bindToOwnClass(Object obj) { bind((Class) obj.getClass()).toInstance(obj); } default Multibinder inSet(Key key) { return Multibinder.newSetBinder(forwardedBinder(), key); } default Multibinder inSet(TypeLiteral type) { return inSet(Key.get(type)); } default Multibinder inSet(Class type) { return inSet(Key.get(type)); } default OptionalBinder forOptional(Key key) { return OptionalBinder.newOptionalBinder(forwardedBinder(), key); } default OptionalBinder forOptional(TypeLiteral type) { return forOptional(Key.get(type)); } default OptionalBinder forOptional(Class type) { return forOptional(Key.get(type)); } default void installFactory(Key key) { install(new FactoryModuleBuilder().build(key)); } default void installFactory(TypeLiteral type) { installFactory(Key.get(type)); } default void installFactory(Class type) { installFactory(Key.get(type)); } default void installInnerClassFactory(Key key) { install(InnerFactoryManifest.forInnerClass(key)); } default void installInnerClassFactory(TypeLiteral type) { installInnerClassFactory(Key.get(type)); } default void installInnerClassFactory(Class type) { installInnerClassFactory(Key.get(type)); } default T getProxy(Key key) { return ProxyUtils.newProviderProxy(key.getTypeLiteral(), getProvider(key)); } default T getProxy(TypeLiteral type) { return getProxy(Key.get(type)); } default T getProxy(Class type) { return getProxy(Key.get(type)); } default void bindProxy(TypeLiteral type) { install(new ProxiedManifest<>(type)); } default void bindProxy(Class type) { bindProxy(TypeLiteral.get(type)); } default void linkOptional(Key key) { final TypeResolver resolver = new TypeResolver().where(new TypeParameter(){}, key.getTypeLiteral()); bind(Keys.optional(key)) .toProvider(key.ofType(resolver.resolve(new TypeLiteral>(){}))); } default void linkOptional(TypeLiteral type) { linkOptional(Key.get(type)); } default void linkOptional(Class type) { linkOptional(Key.get(type)); } default void installIn(Scoping scoping, Module... modules) { final Scoper scoper = new Scoper(this, scoping); for(Element element : Elements.getElements(modules)) { if(element instanceof Binding) { ((Binding) element).acceptTargetVisitor(scoper); } else { element.applyTo(this); } } } default void installIn(Scope scope, Module... modules) { installIn(Scoping.forInstance(scope), modules); } default void installIn(Class scope, Module... modules) { installIn(Scoping.forAnnotation(scope), modules); } default T memberInjected(TypeLiteral type, T instance) { requestInjection(type, instance); return instance; } default T memberInjected(T instance) { requestInjection(instance); return instance; } default UnaryOperator membersInjector(TypeLiteral type) { return Functions.tapUnlessNull(getMembersInjector(type)::injectMembers); } default UnaryOperator membersInjector(Class type) { return membersInjector(TypeLiteral.get(type)); } class EagerProvisioner { @Inject EagerProvisioner(T t) {} } default void provisionEagerly(Key key) { bind(key.ofType(new ResolvableType>(){}.with(new TypeArgument(key.getTypeLiteral()){}))) .asEagerSingleton(); } default void provisionEagerly(TypeLiteral type) { provisionEagerly(Key.get(type)); } default void provisionEagerly(Class type) { provisionEagerly(Key.get(type)); } @Override default Binders withSource(Object source) { return wrap(ForwardingBinder.super.withSource(source)); } @Override default Binders skipSources(Class... classesToSkip) { return wrap(ForwardingBinder.super.skipSources(classesToSkip)); } @Override default PrivateBinders newPrivateBinder() { return PrivateBinders.wrap(ForwardingBinder.super.newPrivateBinder()); } }