package tc.oc.commons.core.inject;
import java.util.Optional;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.spi.UntargettedBinding;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
/**
* Try to determine the actual type that will be provisioned for a binding.
* This should succeed for everything except provider bindings.
*/
public class BindingTargetTypeResolver implements BindingTargetVisitor<Object, Optional<TypeLiteral<?>>> {
private final Injector injector;
public BindingTargetTypeResolver(Injector injector) {
this.injector = injector;
}
@Override
public Optional<TypeLiteral<?>> visit(InstanceBinding<?> binding) {
// Return the provisioned object's type
return of(TypeLiteral.get(binding.getInstance().getClass()));
}
@Override
public Optional<TypeLiteral<?>> visit(ConvertedConstantBinding<?> binding) {
// Return the provisioned object's type
return of(TypeLiteral.get(binding.getValue().getClass()));
}
@Override
public Optional<TypeLiteral<?>> visit(UntargettedBinding<?> binding) {
// Return the key type
return of(binding.getKey().getTypeLiteral());
}
@Override
public Optional<TypeLiteral<?>> visit(ConstructorBinding<?> binding) {
// Return the owning type of the constructor
return of(binding.getConstructor().getDeclaringType());
}
@Override
public Optional<TypeLiteral<?>> visit(LinkedKeyBinding<?> binding) {
// Delegate to the binding for the target type
return injector.getBinding(binding.getLinkedKey()).acceptTargetVisitor(this);
}
@Override
public Optional<TypeLiteral<?>> visit(ProviderBinding<?> binding) {
// Delegate to the binding for the provided type
return injector.getBinding(binding.getProvidedKey()).acceptTargetVisitor(this);
}
@Override
public Optional<TypeLiteral<?>> visit(ExposedBinding<?> binding) {
// Lookup the exposed key in the private environment.
// Since this visitor can only be used on an injector binding,
// the private child injector should always be present too.
return ofNullable(binding.getPrivateElements().getInjector())
.flatMap(child -> child.getBinding(binding.getKey())
.acceptTargetVisitor(new BindingTargetTypeResolver(child)));
}
@Override
public Optional<TypeLiteral<?>> visit(ProviderInstanceBinding<?> binding) {
// We don't know what the provider will return
return empty();
}
@Override
public Optional<TypeLiteral<?>> visit(ProviderKeyBinding<?> binding) {
// We don't know what the provider will return
return empty();
}
}