package tc.oc.commons.core.inject; import java.lang.reflect.Constructor; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.Key; import com.google.inject.PrivateBinder; import com.google.inject.Scope; import com.google.inject.binder.LinkedBindingBuilder; import com.google.inject.binder.ScopedBindingBuilder; import com.google.inject.internal.Scoping; import com.google.inject.spi.BindingScopingVisitor; import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.ConstructorBinding; import com.google.inject.spi.ConvertedConstantBinding; import com.google.inject.spi.Element; import com.google.inject.spi.ExposedBinding; import com.google.inject.spi.InjectionPoint; 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; public class Scoper<T> implements BindingTargetVisitor<T, Void> { private final Binder binder; private final Scoping scoping; public Scoper(Binder binder, Scoping scoping) { this.binder = binder; this.scoping = scoping; } private LinkedBindingBuilder rebind(Binding binding) { return binder.bind(binding.getKey()); } private void scope(Binding binding, ScopedBindingBuilder builder) { binding.acceptScopingVisitor(new BindingScopingVisitor<Void>() { @Override public Void visitEagerSingleton() { builder.asEagerSingleton(); return null; } @Override public Void visitScope(Scope scope) { builder.in(scope); return null; } @Override public Void visitScopeAnnotation(Class scopeAnnotation) { builder.in(scopeAnnotation); return null; } @Override public Void visitNoScoping() { scoping.applyTo(builder); return null; } }); } @Override public Void visit(ProviderInstanceBinding<? extends T> binding) { scope(binding, rebind(binding).toProvider(binding.getUserSuppliedProvider())); return null; } @Override public Void visit(ProviderKeyBinding<? extends T> binding) { scope(binding, rebind(binding).toProvider(binding.getProviderKey())); return null; } @Override public Void visit(LinkedKeyBinding<? extends T> binding) { scope(binding, rebind(binding).to(binding.getLinkedKey())); return null; } @Override public Void visit(UntargettedBinding<? extends T> binding) { scope(binding, rebind(binding)); return null; } @Override public Void visit(ConstructorBinding<? extends T> binding) { final InjectionPoint point = binding.getConstructor(); scope(binding, rebind(binding).toConstructor((Constructor) point.getMember(), point.getDeclaringType())); return null; } @Override public Void visit(ProviderBinding<? extends T> binding) { // These are only created internally, not sure why we would ever see it scope(binding, rebind(binding).toProvider(binding.getProvider())); return null; } @Override public Void visit(InstanceBinding<? extends T> binding) { // Cannot be scoped binding.applyTo(binder); return null; } @Override public Void visit(ConvertedConstantBinding<? extends T> binding) { // Cannot be scoped binding.applyTo(binder); return null; } @Override public Void visit(ExposedBinding<? extends T> binding) { final PrivateBinder privateBinder = this.binder.newPrivateBinder(); final Scoper scoper = new Scoper(privateBinder, scoping); for(Element element : binding.getPrivateElements().getElements()) { if(element instanceof Binding) { ((Binding) element).acceptTargetVisitor(scoper); } else { element.applyTo(privateBinder); } } for(Key key : binding.getPrivateElements().getExposedKeys()) { privateBinder.expose(key); } return null; } }