/* * Copyright (C) 2011 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.common.collect.ImmutableSet; 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.Stage; import com.google.inject.TypeLiteral; import com.google.inject.spi.DefaultBindingTargetVisitor; import java.util.Set; /** * Guarantees that processing of Binding elements happens in a sane way. * * @author sameb@google.com (Sam Berlin) */ abstract class AbstractBindingProcessor extends AbstractProcessor { // 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.<Class<?>>of( AbstractModule.class, Binder.class, Binding.class, Injector.class, Key.class, MembersInjector.class, Module.class, Provider.class, Scope.class, Stage.class, TypeLiteral.class); protected final ProcessedBindingData bindingData; AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) { super(errors); this.bindingData = bindingData; } protected <T> UntargettedBindingImpl<T> invalidBinding( InjectorImpl injector, Key<T> key, Object source) { return new UntargettedBindingImpl<T>(injector, key, source); } protected 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.getExistingBinding(key); if (original != null) { // If it failed because of an explicit duplicate binding... if (injector.state.getExplicitBinding(key) != null) { try { if (!isOkayDuplicate(original, binding, injector.state)) { errors.bindingAlreadySet(key, original.getSource()); return; } } catch (Throwable t) { errors.errorCheckingDuplicateBinding(key, original.getSource(), t); return; } } else { // Otherwise, it failed because of a duplicate JIT binding // in the parent errors.jitBindingAlreadySet(key); return; } } // prevent the parent from creating a JIT binding for this key injector.state.parent().blacklist(key, injector.state, binding.getSource()); 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); } } } private <T> void validateKey(Object source, Key<T> key) { Annotations.checkForMisplacedScopeAnnotations( key.getTypeLiteral().getRawType(), source, errors); } /** * Processor for visiting bindings. Each overriden method that wants to actually process the * binding should call prepareBinding first. */ abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> { final Object source; final Key<T> key; final Class<? super T> rawType; Scoping scoping; Processor(BindingImpl<T> binding) { source = binding.getSource(); key = binding.getKey(); rawType = key.getTypeLiteral().getRawType(); scoping = binding.getScoping(); } protected void prepareBinding() { validateKey(source, key); scoping = Scoping.makeInjectable(scoping, injector, errors); } /** * Schedule initialization of this binding to occur immediately after all bindings have been * initialially processed. */ protected void scheduleInitialization(BindingImpl<?> binding) { bindingData.addUninitializedBinding(asRunnable(binding)); } /** * Schedule initialization for this binding to occur after all other static initialization of * bindings. */ protected void scheduleDelayedInitialization(BindingImpl<?> binding) { bindingData.addDelayedUninitializedBinding(asRunnable(binding)); } private Runnable asRunnable(final BindingImpl<?> binding) { return new Runnable() { @Override public void run() { try { binding.getInjector().initializeBinding(binding, errors.withSource(source)); } catch (ErrorsException e) { errors.merge(e.getErrors()); } } }; } } }