| /** |
| * Copyright (C) 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject.internal; |
| |
| import com.google.inject.AbstractModule; |
| import com.google.inject.Binder; |
| import com.google.inject.Binding; |
| import com.google.inject.Injector; |
| import com.google.inject.Key; |
| import com.google.inject.MembersInjector; |
| import com.google.inject.Module; |
| import com.google.inject.Provider; |
| import com.google.inject.Scope; |
| import com.google.inject.TypeLiteral; |
| import com.google.inject.internal.util.ImmutableSet; |
| import com.google.inject.internal.util.Lists; |
| 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.InjectionPoint; |
| import com.google.inject.spi.InstanceBinding; |
| import com.google.inject.spi.LinkedKeyBinding; |
| import com.google.inject.spi.PrivateElements; |
| 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 java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Handles {@link Binder#bind} and {@link Binder#bindConstant} elements. |
| * |
| * @author crazybob@google.com (Bob Lee) |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| final class BindingProcessor extends AbstractProcessor { |
| |
| private final List<CreationListener> creationListeners = Lists.newArrayList(); |
| private final Initializer initializer; |
| private final List<Runnable> uninitializedBindings = Lists.newArrayList(); |
| |
| BindingProcessor(Errors errors, Initializer initializer) { |
| super(errors); |
| this.initializer = initializer; |
| } |
| |
| @Override public <T> Boolean visit(Binding<T> command) { |
| final Object source = command.getSource(); |
| |
| if (Void.class.equals(command.getKey().getTypeLiteral().getRawType())) { |
| if (command instanceof ProviderInstanceBinding |
| && ((ProviderInstanceBinding) command).getProviderInstance() instanceof ProviderMethod) { |
| errors.voidProviderMethod(); |
| } else { |
| errors.missingConstantValues(); |
| } |
| return true; |
| } |
| |
| final Key<T> key = command.getKey(); |
| Class<? super T> rawType = key.getTypeLiteral().getRawType(); |
| |
| if (rawType == Provider.class) { |
| errors.bindingToProvider(); |
| return true; |
| } |
| |
| validateKey(command.getSource(), command.getKey()); |
| |
| final Scoping scoping = Scoping.makeInjectable( |
| ((BindingImpl<?>) command).getScoping(), injector, errors); |
| |
| command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() { |
| public Void visit(ConstructorBinding<? extends T> binding) { |
| try { |
| ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, |
| binding.getConstructor(), source, scoping, errors, false); |
| scheduleInitialization(onInjector); |
| putBinding(onInjector); |
| } catch (ErrorsException e) { |
| errors.merge(e.getErrors()); |
| putBinding(invalidBinding(injector, key, source)); |
| } |
| return null; |
| } |
| |
| public Void visit(InstanceBinding<? extends T> binding) { |
| Set<InjectionPoint> injectionPoints = binding.getInjectionPoints(); |
| T instance = binding.getInstance(); |
| Initializable<T> ref = initializer.requestInjection( |
| injector, instance, source, injectionPoints); |
| ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref); |
| InternalFactory<? extends T> scopedFactory |
| = Scoping.scope(key, injector, factory, source, scoping); |
| putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints, |
| instance)); |
| return null; |
| } |
| |
| public Void visit(ProviderInstanceBinding<? extends T> binding) { |
| Provider<? extends T> provider = binding.getProviderInstance(); |
| Set<InjectionPoint> injectionPoints = binding.getInjectionPoints(); |
| Initializable<Provider<? extends T>> initializable = initializer |
| .<Provider<? extends T>>requestInjection(injector, provider, source, injectionPoints); |
| InternalFactory<T> factory = new InternalFactoryToProviderAdapter<T>(initializable, source); |
| InternalFactory<? extends T> scopedFactory |
| = Scoping.scope(key, injector, factory, source, scoping); |
| putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping, |
| provider, injectionPoints)); |
| return null; |
| } |
| |
| public Void visit(ProviderKeyBinding<? extends T> binding) { |
| Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey(); |
| BoundProviderFactory<T> boundProviderFactory |
| = new BoundProviderFactory<T>(injector, providerKey, source); |
| creationListeners.add(boundProviderFactory); |
| InternalFactory<? extends T> scopedFactory = Scoping.scope( |
| key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping); |
| putBinding(new LinkedProviderBindingImpl<T>( |
| injector, key, source, scopedFactory, scoping, providerKey)); |
| return null; |
| } |
| |
| public Void visit(LinkedKeyBinding<? extends T> binding) { |
| Key<? extends T> linkedKey = binding.getLinkedKey(); |
| if (key.equals(linkedKey)) { |
| errors.recursiveBinding(); |
| } |
| |
| FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source); |
| creationListeners.add(factory); |
| InternalFactory<? extends T> scopedFactory |
| = Scoping.scope(key, injector, factory, source, scoping); |
| putBinding( |
| new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey)); |
| return null; |
| } |
| |
| public Void visit(UntargettedBinding<? extends T> untargetted) { |
| // Error: Missing implementation. |
| // Example: bind(Date.class).annotatedWith(Red.class); |
| // We can't assume abstract types aren't injectable. They may have an |
| // @ImplementedBy annotation or something. |
| if (key.getAnnotationType() != null) { |
| errors.missingImplementation(key); |
| putBinding(invalidBinding(injector, key, source)); |
| return null; |
| } |
| |
| // This cast is safe after the preceeding check. |
| try { |
| BindingImpl<T> binding = injector.createUninitializedBinding( |
| key, scoping, source, errors, false); |
| scheduleInitialization(binding); |
| putBinding(binding); |
| } catch (ErrorsException e) { |
| errors.merge(e.getErrors()); |
| putBinding(invalidBinding(injector, key, source)); |
| } |
| |
| return null; |
| } |
| |
| public Void visit(ExposedBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Cannot apply a non-module element"); |
| } |
| |
| public Void visit(ConvertedConstantBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Cannot apply a non-module element"); |
| } |
| |
| public Void visit(ProviderBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Cannot apply a non-module element"); |
| } |
| |
| private void scheduleInitialization(final BindingImpl<?> binding) { |
| uninitializedBindings.add(new Runnable() { |
| public void run() { |
| try { |
| binding.getInjector().initializeBinding(binding, errors.withSource(source)); |
| } catch (ErrorsException e) { |
| errors.merge(e.getErrors()); |
| } |
| } |
| }); |
| } |
| }); |
| |
| return true; |
| } |
| |
| @Override public Boolean visit(PrivateElements privateElements) { |
| for (Key<?> key : privateElements.getExposedKeys()) { |
| bindExposed(privateElements, key); |
| } |
| return false; // leave the private elements for the PrivateElementsProcessor to handle |
| } |
| |
| private <T> void bindExposed(PrivateElements privateElements, Key<T> key) { |
| ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements); |
| creationListeners.add(exposedKeyFactory); |
| putBinding(new ExposedBindingImpl<T>( |
| injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements)); |
| } |
| |
| private <T> void validateKey(Object source, Key<T> key) { |
| Annotations.checkForMisplacedScopeAnnotations( |
| key.getTypeLiteral().getRawType(), source, errors); |
| } |
| |
| <T> UntargettedBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) { |
| return new UntargettedBindingImpl<T>(injector, key, source); |
| } |
| |
| public void initializeBindings() { |
| for (Runnable initializer : uninitializedBindings) { |
| initializer.run(); |
| } |
| } |
| |
| public void runCreationListeners() { |
| for (CreationListener creationListener : creationListeners) { |
| creationListener.notify(errors); |
| } |
| } |
| |
| private void putBinding(BindingImpl<?> binding) { |
| Key<?> key = binding.getKey(); |
| |
| Class<?> rawType = key.getTypeLiteral().getRawType(); |
| if (FORBIDDEN_TYPES.contains(rawType)) { |
| errors.cannotBindToGuiceType(rawType.getSimpleName()); |
| return; |
| } |
| |
| BindingImpl<?> original = injector.state.getExplicitBinding(key); |
| if (original != null) { |
| try { |
| if(!isOkayDuplicate(original, binding, injector.state)) { |
| errors.bindingAlreadySet(key, original.getSource()); |
| return; |
| } |
| } catch(Throwable t) { |
| errors.errorCheckingDuplicateBinding(key, original.getSource(), t); |
| return; |
| } |
| } |
| |
| // prevent the parent from creating a JIT binding for this key |
| injector.state.parent().blacklist(key); |
| injector.state.putBinding(key, binding); |
| } |
| |
| /** |
| * We tolerate duplicate bindings if one exposes the other or if the two bindings |
| * are considered duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}. |
| * |
| * @param original the binding in the parent injector (candidate for an exposing binding) |
| * @param binding the binding to check (candidate for the exposed binding) |
| */ |
| private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) { |
| if (original instanceof ExposedBindingImpl) { |
| ExposedBindingImpl exposed = (ExposedBindingImpl) original; |
| InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector(); |
| return (exposedFrom == binding.getInjector()); |
| } else { |
| original = (BindingImpl<?>)state.getExplicitBindingsThisLevel().get(binding.getKey()); |
| // If no original at this level, the original was on a parent, and we don't |
| // allow deduplication between parents & children. |
| if(original == null) { |
| return false; |
| } else { |
| return original.equals(binding); |
| } |
| } |
| } |
| |
| // It's unfortunate that we have to maintain a blacklist of specific |
| // classes, but we can't easily block the whole package because of |
| // all our unit tests. |
| private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.of( |
| AbstractModule.class, |
| Binder.class, |
| Binding.class, |
| Injector.class, |
| Key.class, |
| MembersInjector.class, |
| Module.class, |
| Provider.class, |
| Scope.class, |
| TypeLiteral.class); |
| // TODO(jessewilson): fix BuiltInModule, then add Stage |
| |
| interface CreationListener { |
| void notify(Errors errors); |
| } |
| } |