remove phases from BindingProcessor, refactor so that two different classes do the two different bits, with an abstract superclass managing the shared pieces.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1527 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
new file mode 100644
index 0000000..fdbc752
--- /dev/null
+++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
@@ -0,0 +1,167 @@
+/**
+ * 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 java.util.Set;
+
+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.spi.DefaultBindingTargetVisitor;
+
+/**
+ * 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,
+ TypeLiteral.class);
+ // TODO(jessewilson): fix BuiltInModule, then add Stage
+
+ 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, 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);
+ }
+
+ protected void scheduleInitialization(final BindingImpl<?> binding) {
+ bindingData.addUninitializedBinding(new Runnable() {
+ public void run() {
+ try {
+ binding.getInjector().initializeBinding(binding, errors.withSource(source));
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java
index 28d04a6..a26fed3 100644
--- a/core/src/com/google/inject/internal/BindingProcessor.java
+++ b/core/src/com/google/inject/internal/BindingProcessor.java
@@ -16,19 +16,10 @@
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;
@@ -40,7 +31,6 @@
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;
/**
@@ -49,28 +39,18 @@
* @author crazybob@google.com (Bob Lee)
* @author jessewilson@google.com (Jesse Wilson)
*/
-final class BindingProcessor extends AbstractProcessor {
+final class BindingProcessor extends AbstractBindingProcessor {
- private final List<CreationListener> creationListeners = Lists.newArrayList();
private final Initializer initializer;
- private final List<Runnable> uninitializedBindings = Lists.newArrayList();
-
- enum Phase { PASS_ONE, PASS_TWO; }
- private Phase phase;
- BindingProcessor(Errors errors, Initializer initializer) {
- super(errors);
+ BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) {
+ super(errors, bindingData);
this.initializer = initializer;
}
-
- void setPhase(Phase phase) {
- this.phase = phase;
- }
@Override public <T> Boolean visit(Binding<T> command) {
- final Object source = command.getSource();
-
- if (Void.class.equals(command.getKey().getTypeLiteral().getRawType())) {
+ Class<?> rawType = command.getKey().getTypeLiteral().getRawType();
+ if (Void.class.equals(rawType)) {
if (command instanceof ProviderInstanceBinding
&& ((ProviderInstanceBinding) command).getProviderInstance() instanceof ProviderMethod) {
errors.voidProviderMethod();
@@ -79,22 +59,15 @@
}
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);
-
- return command.acceptTargetVisitor(new BindingTargetVisitor<T, Boolean>() {
+
+ return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>)command) {
public Boolean visit(ConstructorBinding<? extends T> binding) {
+ prepareBinding();
try {
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
binding.getConstructor(), source, scoping, errors, false);
@@ -108,6 +81,7 @@
}
public Boolean visit(InstanceBinding<? extends T> binding) {
+ prepareBinding();
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
T instance = binding.getInstance();
Initializable<T> ref = initializer.requestInjection(
@@ -121,6 +95,7 @@
}
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
+ prepareBinding();
Provider<? extends T> provider = binding.getProviderInstance();
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
Initializable<Provider<? extends T>> initializable = initializer
@@ -134,10 +109,11 @@
}
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
+ prepareBinding();
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
BoundProviderFactory<T> boundProviderFactory
= new BoundProviderFactory<T>(injector, providerKey, source);
- creationListeners.add(boundProviderFactory);
+ bindingData.addCreationListener(boundProviderFactory);
InternalFactory<? extends T> scopedFactory = Scoping.scope(
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
putBinding(new LinkedProviderBindingImpl<T>(
@@ -146,13 +122,14 @@
}
public Boolean visit(LinkedKeyBinding<? extends T> binding) {
+ prepareBinding();
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);
+ bindingData.addCreationListener(factory);
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factory, source, scoping);
putBinding(
@@ -160,34 +137,8 @@
return true;
}
- public Boolean 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 true;
- }
-
- // We want to do UntargettedBindings in the second pass.
- if (phase == Phase.PASS_ONE) {
- return false;
- }
-
- // 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 true;
+ public Boolean visit(UntargettedBinding<? extends T> untargetted) {
+ return false;
}
public Boolean visit(ExposedBinding<? extends T> binding) {
@@ -201,28 +152,15 @@
public Boolean 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());
- }
- }
- });
+
+ @Override
+ protected Boolean visitOther(Binding<? extends T> binding) {
+ throw new IllegalStateException("BindingProcessor should override all visitations");
}
});
}
@Override public Boolean visit(PrivateElements privateElements) {
- // Because we do two passes, we have to ignore the PrivateElements in the second
- // pass. Otherwise we end up calling bindExposed twice for each one.
- if (phase == Phase.PASS_TWO) {
- return false;
- }
-
for (Key<?> key : privateElements.getExposedKeys()) {
bindExposed(privateElements, key);
}
@@ -231,108 +169,8 @@
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
- creationListeners.add(exposedKeyFactory);
+ bindingData.addCreationListener(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.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, 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);
- }
- }
- }
-
- // 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,
- TypeLiteral.class);
- // TODO(jessewilson): fix BuiltInModule, then add Stage
-
- interface CreationListener {
- void notify(Errors errors);
- }
}
diff --git a/core/src/com/google/inject/internal/BoundProviderFactory.java b/core/src/com/google/inject/internal/BoundProviderFactory.java
index 6599750..27f957e 100644
--- a/core/src/com/google/inject/internal/BoundProviderFactory.java
+++ b/core/src/com/google/inject/internal/BoundProviderFactory.java
@@ -17,7 +17,6 @@
package com.google.inject.internal;
import com.google.inject.Key;
-import com.google.inject.internal.BindingProcessor.CreationListener;
import com.google.inject.internal.InjectorImpl.JitLimitation;
import com.google.inject.spi.Dependency;
diff --git a/core/src/com/google/inject/internal/CreationListener.java b/core/src/com/google/inject/internal/CreationListener.java
new file mode 100644
index 0000000..533b14f
--- /dev/null
+++ b/core/src/com/google/inject/internal/CreationListener.java
@@ -0,0 +1,24 @@
+/**
+ * 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;
+
+/** Something that is notified upon creation. */
+interface CreationListener {
+
+ /** Notifies that creation should happen. */
+ void notify(Errors errors);
+}
\ No newline at end of file
diff --git a/core/src/com/google/inject/internal/ExposedKeyFactory.java b/core/src/com/google/inject/internal/ExposedKeyFactory.java
index 822282d..52bf7cb 100644
--- a/core/src/com/google/inject/internal/ExposedKeyFactory.java
+++ b/core/src/com/google/inject/internal/ExposedKeyFactory.java
@@ -24,7 +24,7 @@
* This factory exists in a parent injector. When invoked, it retrieves its value from a child
* injector.
*/
-final class ExposedKeyFactory<T> implements InternalFactory<T>, BindingProcessor.CreationListener {
+final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener {
private final Key<T> key;
private final PrivateElements privateElements;
private BindingImpl<T> delegate;
diff --git a/core/src/com/google/inject/internal/FactoryProxy.java b/core/src/com/google/inject/internal/FactoryProxy.java
index ca9a33c..c6d2f3b 100644
--- a/core/src/com/google/inject/internal/FactoryProxy.java
+++ b/core/src/com/google/inject/internal/FactoryProxy.java
@@ -25,7 +25,7 @@
* A placeholder which enables us to swap in the real factory once the injector is created.
* Used for a linked binding, so that getting the linked binding returns the link's factory.
*/
-final class FactoryProxy<T> implements InternalFactory<T>, BindingProcessor.CreationListener {
+final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
private final InjectorImpl injector;
private final Key<T> key;
diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
index 151a1a1..66ff334 100644
--- a/core/src/com/google/inject/internal/InjectorShell.java
+++ b/core/src/com/google/inject/internal/InjectorShell.java
@@ -24,7 +24,6 @@
import static com.google.inject.Scopes.SINGLETON;
import com.google.inject.Singleton;
import com.google.inject.Stage;
-import com.google.inject.internal.BindingProcessor.Phase;
import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.internal.util.ImmutableSet;
import com.google.inject.internal.util.Lists;
@@ -51,10 +50,8 @@
private final List<Element> elements;
private final InjectorImpl injector;
- private final PrivateElements privateElements;
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
- this.privateElements = builder.privateElements;
this.elements = elements;
this.injector = injector;
}
@@ -120,8 +117,11 @@
* returned if any modules contain {@link Binder#newPrivateBinder private environments}. The
* primary injector will be first in the returned list.
*/
- List<InjectorShell> build(BindingProcessor bindingProcessor,
- Stopwatch stopwatch, Errors errors) {
+ List<InjectorShell> build(
+ Initializer initializer,
+ ProcessedBindingData bindingData,
+ Stopwatch stopwatch,
+ Errors errors) {
checkState(stage != null, "Stage not initialized");
checkState(privateElements == null || parent != null, "PrivateElements with no parent");
checkState(state != null, "no state. Did you remember to lock() ?");
@@ -171,14 +171,11 @@
bindInjector(injector);
bindLogger(injector);
- // Do two passes over our bindings -- first to get all non UntargettedBindings,
- // then to get the only UntargettedBindings. This is necessary because
- // UntargettedBindings can create JIT bindings and need all their other
- // dependencies set up ahead of time.
- bindingProcessor.setPhase(Phase.PASS_ONE);
- bindingProcessor.process(injector, elements);
- bindingProcessor.setPhase(Phase.PASS_TWO);
- bindingProcessor.process(injector, elements);
+ // Process all normal bindings, then UntargettedBindings.
+ // This is necessary because UntargettedBindings can create JIT bindings
+ // and need all their other dependencies set up ahead of time.
+ new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
+ new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
stopwatch.resetAndLog("Binding creation");
List<InjectorShell> injectorShells = Lists.newArrayList();
@@ -188,7 +185,7 @@
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
processor.process(injector, elements);
for (Builder builder : processor.getInjectorShellBuilders()) {
- injectorShells.addAll(builder.build(bindingProcessor, stopwatch, errors));
+ injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors));
}
stopwatch.resetAndLog("Private environment creation");
diff --git a/core/src/com/google/inject/internal/InternalInjectorCreator.java b/core/src/com/google/inject/internal/InternalInjectorCreator.java
index 812062a..a2c79f9 100644
--- a/core/src/com/google/inject/internal/InternalInjectorCreator.java
+++ b/core/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -61,7 +61,7 @@
private final Errors errors = new Errors();
private final Initializer initializer = new Initializer();
- private final BindingProcessor bindingProcesor;
+ private final ProcessedBindingData bindingData;
private final InjectionRequestProcessor injectionRequestProcessor;
private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
@@ -69,7 +69,7 @@
public InternalInjectorCreator() {
injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
- bindingProcesor = new BindingProcessor(errors, initializer);
+ bindingData = new ProcessedBindingData();
}
public InternalInjectorCreator stage(Stage stage) {
@@ -100,7 +100,7 @@
// Synchronize while we're building up the bindings and other injector state. This ensures that
// the JIT bindings in the parent injector don't change while we're being built
synchronized (shellBuilder.lock()) {
- shells = shellBuilder.build(bindingProcesor, stopwatch, errors);
+ shells = shellBuilder.build(initializer, bindingData, stopwatch, errors);
stopwatch.resetAndLog("Injector construction");
initializeStatically();
@@ -119,7 +119,7 @@
/** Initialize and validate everything. */
private void initializeStatically() {
- bindingProcesor.initializeBindings();
+ bindingData.initializeBindings();
stopwatch.resetAndLog("Binding initialization");
for (InjectorShell shell : shells) {
@@ -130,7 +130,7 @@
injectionRequestProcessor.process(shells);
stopwatch.resetAndLog("Collecting injection requests");
- bindingProcesor.runCreationListeners();
+ bindingData.runCreationListeners(errors);
stopwatch.resetAndLog("Binding validation");
injectionRequestProcessor.validate();
diff --git a/core/src/com/google/inject/internal/ProcessedBindingData.java b/core/src/com/google/inject/internal/ProcessedBindingData.java
new file mode 100644
index 0000000..cf311a7
--- /dev/null
+++ b/core/src/com/google/inject/internal/ProcessedBindingData.java
@@ -0,0 +1,54 @@
+/**
+ * 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 java.util.List;
+
+import com.google.inject.internal.util.Lists;
+
+/**
+ * Keeps track of creation listeners & uninitialized bindings,
+ * so they can be processed after bindings are recorded.
+ *
+ * @author sameb@google.com (Sam Berlin)
+ */
+class ProcessedBindingData {
+
+ private final List<CreationListener> creationListeners = Lists.newArrayList();
+ private final List<Runnable> uninitializedBindings = Lists.newArrayList();
+
+ void addCreationListener(CreationListener listener) {
+ creationListeners.add(listener);
+ }
+
+ void addUninitializedBinding(Runnable runnable) {
+ uninitializedBindings.add(runnable);
+ }
+
+ void initializeBindings() {
+ for (Runnable initializer : uninitializedBindings) {
+ initializer.run();
+ }
+ }
+
+ void runCreationListeners(Errors errors) {
+ for (CreationListener creationListener : creationListeners) {
+ creationListener.notify(errors);
+ }
+ }
+
+}
diff --git a/core/src/com/google/inject/internal/UntargettedBindingProcessor.java b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java
new file mode 100644
index 0000000..d056127
--- /dev/null
+++ b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java
@@ -0,0 +1,69 @@
+/**
+ * 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.inject.Binding;
+import com.google.inject.spi.UntargettedBinding;
+
+/**
+ * Processes just UntargettedBindings.
+ *
+ * @author sameb@google.com (Sam Berlin)
+ */
+class UntargettedBindingProcessor extends AbstractBindingProcessor {
+
+ UntargettedBindingProcessor(Errors errors, ProcessedBindingData bindingData) {
+ super(errors, bindingData);
+ }
+
+ @Override
+ public <T> Boolean visit(Binding<T> binding) {
+ return binding.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>)binding) {
+ public Boolean visit(UntargettedBinding<? extends T> untargetted) {
+ prepareBinding();
+
+ // 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 true;
+ }
+
+ // 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 true;
+ }
+
+ @Override
+ protected Boolean visitOther(Binding<? extends T> binding) {
+ return false;
+ }
+ });
+ }
+}