Last of the planned major changes to the SPI. Aside from Javadoc I consider the SPI to be complete for v2. (with a possible addition of a ProviderMethodBinding feature also)
This change changes the interface of 3 methods of BindingTargetVisitor to take a Set<InjectionPoint>, which replaces the HasInjections interface.
Also, making the ProviderMethods class non-public.
Also, adding extra checks for serialization. I've still got to fix serialization of TypeLiteral.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@616 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindingAnnotation.java b/src/com/google/inject/BindingAnnotation.java
index 707b3e7..110630b 100644
--- a/src/com/google/inject/BindingAnnotation.java
+++ b/src/com/google/inject/BindingAnnotation.java
@@ -28,7 +28,7 @@
*
* <pre>
* {@code @}Retention(RUNTIME)
- * {@code @}Target({ FIELD, PARAMETER })
+ * {@code @}Target({ FIELD, PARAMETER, METHOD })
* {@code @}BindingAnnotation
* public {@code @}interface Transactional {}
* </pre>
diff --git a/src/com/google/inject/BindingProcessor.java b/src/com/google/inject/BindingProcessor.java
index 9ed2f37..430dd6b 100644
--- a/src/com/google/inject/BindingProcessor.java
+++ b/src/com/google/inject/BindingProcessor.java
@@ -18,10 +18,12 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.inject.internal.Annotations;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.spi.BindingScopingVisitor;
import com.google.inject.spi.BindingTargetVisitor;
+import com.google.inject.spi.InjectionPoint;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
@@ -119,29 +121,24 @@
});
command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
- public Void visitInstance(T instance) {
- if (instance == null) {
- errors.cannotBindToNullInstance();
- putBinding(invalidBinding(injector, key, source));
- return null;
- }
-
+ public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
- memberInjector.requestInjection(instance, source);
- InternalFactory<? extends T> scopedFactory
- = Scopes.scope(key, injector, factory, scope);
- putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, instance));
+ memberInjector.requestInjection(instance, source, injectionPoints);
+ InternalFactory<? extends T> scopedFactory = Scopes.scope(key, injector, factory, scope);
+ putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
+ instance));
return null;
}
- public Void visitProvider(Provider<? extends T> provider) {
+ public Void visitProvider(Provider<? extends T> provider,
+ Set<InjectionPoint> injectionPoints) {
InternalFactoryToProviderAdapter<? extends T> factory
= new InternalFactoryToProviderAdapter<T>(provider, source);
- memberInjector.requestInjection(provider, source);
+ memberInjector.requestInjection(provider, source, injectionPoints);
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, injector, factory, scope);
- putBinding(new ProviderInstanceBindingImpl<T>(
- injector, key, source, scopedFactory, scope, provider, loadStrategy));
+ putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scope,
+ provider, loadStrategy, injectionPoints));
return null;
}
@@ -214,7 +211,8 @@
throw new IllegalArgumentException("Cannot apply a non-module element");
}
- public Void visitConstructor(Constructor<? extends T> constructor) {
+ public Void visitConstructor(Constructor<? extends T> constructor,
+ Set<InjectionPoint> injectionPoints) {
throw new IllegalArgumentException("Cannot apply a non-module element");
}
@@ -227,7 +225,7 @@
}
private <T> void validateKey(Object source, Key<T> key) {
- Scopes.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors);
+ Annotations.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors);
}
<T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index cd13461..cb2f8f8 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -16,27 +16,27 @@
package com.google.inject;
+import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import com.google.inject.InjectorImpl.LateBoundConstructor;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.HasInjections;
import com.google.inject.spi.InjectionPoint;
-import java.util.Set;
/**
*
*
*/
-class ClassBindingImpl<T> extends BindingImpl<T> implements HasInjections {
+class ClassBindingImpl<T> extends BindingImpl<T> {
- private final InjectorImpl.LateBoundConstructor<T> lateBoundConstructor;
+ private final LateBoundConstructor<T> lateBoundConstructor;
+ private ImmutableSet<InjectionPoint> injectionPoints;
ClassBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
- InjectorImpl.LateBoundConstructor<T> lateBoundConstructor,
+ LateBoundConstructor<T> lateBoundConstructor,
LoadStrategy loadStrategy) {
super(injector, key, source, internalFactory, scope, loadStrategy);
this.lateBoundConstructor = lateBoundConstructor;
@@ -44,10 +44,12 @@
@Override void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
lateBoundConstructor.bind(injector, getBoundClass(), errors);
+ injectionPoints = lateBoundConstructor.constructorInjector.getInjectionPoints();
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitConstructor(lateBoundConstructor.getConstructor());
+ checkState(injectionPoints != null, "not initialized");
+ return visitor.visitConstructor(lateBoundConstructor.getConstructor(), injectionPoints);
}
@SuppressWarnings("unchecked")
@@ -56,26 +58,6 @@
return (Class<T>) key.getRawType();
}
- public Set<InjectionPoint> getInjectionPoints() {
- if (lateBoundConstructor == null) {
- throw new AssertionError();
- }
-
- Set<InjectionPoint> injectionPoints = Sets.newLinkedHashSet();
-
- Class<T> boundClass = getBoundClass();
- ConstructorInjector<T> constructor = lateBoundConstructor.constructorInjector;
- injectionPoints.add(constructor.getInjectionPoint());
-
- try {
- injectionPoints.addAll(injector.getFieldAndMethodInjectionsFor(boundClass));
- } catch (ErrorsException e) {
- throw new AssertionError("This should have failed at CreationTime");
- }
-
- return ImmutableSet.copyOf(injectionPoints);
- }
-
@Override public String toString() {
return new ToStringBuilder(Binding.class)
.add("class", getBoundClass())
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index 1db97ef..de36367 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -16,6 +16,7 @@
package com.google.inject;
+import com.google.common.collect.ImmutableSet;
import com.google.inject.InjectorImpl.SingleMemberInjector;
import com.google.inject.InjectorImpl.SingleParameterInjector;
import com.google.inject.internal.Errors;
@@ -41,8 +42,7 @@
this.implementation = implementation;
constructionProxy = injector.reflection.getConstructionProxy(errors, implementation);
parameterInjectors = createParameterInjector(errors, injector, constructionProxy);
- List<SingleMemberInjector> memberInjectorsList
- = injector.getMemberInjectors(implementation, errors);
+ List<SingleMemberInjector> memberInjectorsList = injector.injectors.get(implementation, errors);
memberInjectors = memberInjectorsList.toArray(
new SingleMemberInjector[memberInjectorsList.size()]);
}
@@ -53,8 +53,13 @@
return injector.getParametersInjectors(constructionProxy.getInjectionPoint(), errors);
}
- InjectionPoint getInjectionPoint() {
- return constructionProxy.getInjectionPoint();
+ ImmutableSet<InjectionPoint> getInjectionPoints() {
+ InjectionPoint[] injectionPoints = new InjectionPoint[memberInjectors.length + 1];
+ injectionPoints[0] = constructionProxy.getInjectionPoint();
+ for (int i = 0; i < memberInjectors.length; i++) {
+ injectionPoints[i+1] = memberInjectors[i].getInjectionPoint();
+ }
+ return ImmutableSet.of(injectionPoints);
}
/**
diff --git a/src/com/google/inject/CreationException.java b/src/com/google/inject/CreationException.java
index 799a3bc..94440ba 100644
--- a/src/com/google/inject/CreationException.java
+++ b/src/com/google/inject/CreationException.java
@@ -58,4 +58,6 @@
@Override public String getMessage() {
return Errors.format("Guice configuration errors", errorMessages);
}
+
+ private static final long serialVersionUID = 0;
}
diff --git a/src/com/google/inject/CreationTimeMemberInjector.java b/src/com/google/inject/CreationTimeMemberInjector.java
index c81b762..15d9639 100644
--- a/src/com/google/inject/CreationTimeMemberInjector.java
+++ b/src/com/google/inject/CreationTimeMemberInjector.java
@@ -20,8 +20,10 @@
import com.google.common.collect.Maps;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.InjectionPoint;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
@@ -51,7 +53,8 @@
* @Inject).
* @param source the source location that this injection was requested
*/
- public void requestInjection(Object instance, Object source) {
+ public void requestInjection(Object instance, Object source,
+ Set<InjectionPoint> injectionPoints) {
checkNotNull(source);
outstandingInjections.put(instance, source);
}
@@ -65,7 +68,7 @@
try {
Object toInject = entry.getKey();
Object source = entry.getValue();
- injector.getMemberInjectors(toInject.getClass(), errors.withSource(source));
+ injector.injectors.get(toInject.getClass(), errors.withSource(source));
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
diff --git a/src/com/google/inject/InjectionRequestProcessor.java b/src/com/google/inject/InjectionRequestProcessor.java
index 7b55a48..f7c8042 100644
--- a/src/com/google/inject/InjectionRequestProcessor.java
+++ b/src/com/google/inject/InjectionRequestProcessor.java
@@ -17,11 +17,12 @@
package com.google.inject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.InjectorImpl.SingleMemberInjector;
+import com.google.inject.internal.ConfigurationException;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.ConfigurationException;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InjectionRequest;
import com.google.inject.spi.StaticInjectionRequest;
@@ -46,16 +47,21 @@
}
@Override public Boolean visitStaticInjectionRequest(StaticInjectionRequest command) {
- for (Class<?> type : command.getTypes()) {
- staticInjections.add(new StaticInjection(command.getSource(), type));
- }
+ staticInjections.add(new StaticInjection(command.getSource(), command.getType()));
return true;
}
@Override public Boolean visitInjectionRequest(InjectionRequest command) {
- for (Object instance : command.getInstances()) {
- memberInjector.requestInjection(instance, command.getSource());
+ List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ try {
+ InjectionPoint.addForInstanceMethodsAndFields(
+ command.getInstance().getClass(), injectionPointsList);
+ } catch (ConfigurationException e) {
+ errors.merge(e.getErrorMessages());
}
+
+ memberInjector.requestInjection(command.getInstance(), command.getSource(),
+ ImmutableSet.copyOf(injectionPointsList));
return true;
}
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index c4dcb00..8e92beb 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -17,6 +17,7 @@
package com.google.inject;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Reflection.Factory;
@@ -258,7 +259,7 @@
injector.explicitBindings.put(key,
new ProviderInstanceBindingImpl<Logger>(injector, key,
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
- loggerFactory, LoadStrategy.LAZY));
+ loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of()));
}
static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index a09583f..cca4ffd 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -23,16 +23,15 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
+import com.google.inject.internal.Annotations;
import com.google.inject.internal.BytecodeGen.Visibility;
import static com.google.inject.internal.BytecodeGen.newFastClass;
import com.google.inject.internal.Classes;
import com.google.inject.internal.ConfigurationException;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.Keys;
+import com.google.inject.internal.FailableCache;
import com.google.inject.internal.MatcherAndConverter;
-import com.google.inject.internal.ReferenceCache;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
@@ -48,11 +47,9 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
@@ -165,6 +162,7 @@
if (binding != null
&& binding.getScope() != null
&& !binding.getScope().equals(Scopes.NO_SCOPE)) {
+ // TODO: this binding won't report its injection points or scoping properly
bindingImpl = new ProviderInstanceBindingImpl(
this,
key,
@@ -172,7 +170,8 @@
new InternalFactoryToProviderAdapter(binding.getProvider(), binding.getSource()),
Scopes.NO_SCOPE,
binding.getProvider(),
- LoadStrategy.LAZY);
+ LoadStrategy.LAZY,
+ ImmutableSet.<InjectionPoint>of());
}
parentBindings.put(key, bindingImpl); // this kinda scares me
return bindingImpl;
@@ -267,10 +266,6 @@
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
return visitor.visitProviderBinding(providedBinding.getKey());
}
-
- public BindingImpl<T> getTargetBinding() {
- return providedBinding;
- }
}
/**
@@ -352,18 +347,10 @@
return provider;
}
- public T getValue() {
- return value;
- }
-
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visitConvertedConstant(value);
}
- public BindingImpl<String> getOriginal() {
- return (BindingImpl<String>) originalBinding;
- }
-
@Override public String toString() {
return new ToStringBuilder(Binding.class)
.add("key", key)
@@ -428,14 +415,14 @@
// Handle @ImplementedBy
ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class);
if (implementedBy != null) {
- Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+ Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
return createImplementedByBinding(type, scope, implementedBy, loadStrategy, errors);
}
// Handle @ProvidedBy.
ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class);
if (providedBy != null) {
- Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+ Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
return createProvidedByBinding(type, scope, providedBy, loadStrategy, errors);
}
@@ -452,7 +439,7 @@
}
if (scope == null) {
- Class<? extends Annotation> scopeAnnotation = Scopes.findScopeAnnotation(errors, type);
+ Class<? extends Annotation> scopeAnnotation = Annotations.findScopeAnnotation(errors, type);
if (scopeAnnotation != null) {
scope = scopes.get(scopeAnnotation);
if (scope == null) {
@@ -473,8 +460,11 @@
static class LateBoundConstructor<T> implements InternalFactory<T> {
ConstructorInjector<T> constructorInjector;
- void bind(InjectorImpl injector, Class<T> implementation, Errors errors) throws ErrorsException {
- constructorInjector = injector.getConstructor(implementation, errors);
+ @SuppressWarnings("unchecked") // the constructor T is the same as the implementation T
+ void bind(InjectorImpl injector, Class<T> implementation, Errors errors)
+ throws ErrorsException {
+ constructorInjector = (ConstructorInjector<T>) injector.constructors.get(
+ implementation, errors);
}
public Constructor<T> getConstructor() {
@@ -630,13 +620,11 @@
return getBindingOrThrow(key, errors).internalFactory;
}
- /**
- * Field and method injectors. Each value is either an Errors or a
- * {@code List<SingleMemberInjector>}.
- */
- private final Map<Class<?>, Object> injectors = new ReferenceCache<Class<?>, Object>() {
- protected Object create(Class<?> type) {
- Errors errors = new Errors();
+ /** Cached field and method injectors for a type. */
+ final FailableCache<Class<?>, List<SingleMemberInjector>> injectors
+ = new FailableCache<Class<?>, List<SingleMemberInjector>>() {
+ protected List<SingleMemberInjector> create(Class<?> type, Errors errors)
+ throws ErrorsException {
List<InjectionPoint> injectionPoints = Lists.newArrayList();
try {
InjectionPoint.addForInstanceMethodsAndFields(type, injectionPoints);
@@ -644,28 +632,12 @@
errors.merge(e.getErrorMessages());
}
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
- return errors.hasErrors() ? errors.makeImmutable() : injectors;
+ errors.throwIfNecessary();
+ return injectors;
}
};
- public List<SingleMemberInjector> getMemberInjectors(Class<?> type, Errors errors)
- throws ErrorsException {
- Object injectorsOrError = injectors.get(type);
- if (injectorsOrError instanceof List) {
- @SuppressWarnings("unchecked") // the only type of list we use
- List<SingleMemberInjector> result = (List<SingleMemberInjector>) injectorsOrError;
- return result;
- } else if (injectorsOrError instanceof Errors) {
- errors.merge((Errors) injectorsOrError);
- throw errors.toException();
- } else {
- throw new AssertionError();
- }
- }
-
- /**
- * Returns the injectors for the specified injection points.
- */
+ /** Returns the injectors for the specified injection points. */
ImmutableList<SingleMemberInjector> getInjectors(
List<InjectionPoint> injectionPoints, Errors errors) {
List<SingleMemberInjector> injectors = Lists.newArrayList();
@@ -684,10 +656,6 @@
return ImmutableList.copyOf(injectors);
}
- Map<Key<?>, BindingImpl<?>> internalBindings() {
- return explicitBindings;
- }
-
// not test-covered
public Map<Key<?>, Binding<?>> getBindings() {
return Collections.<Key<?>, Binding<?>>unmodifiableMap(explicitBindings);
@@ -733,10 +701,6 @@
return injectionPoint;
}
- public Collection<Dependency<?>> getDependencies() {
- return injectionPoint.getDependencies();
- }
-
public void inject(Errors errors, InternalContext context, Object o) {
context.setDependency(dependency);
errors.pushSource(dependency);
@@ -765,7 +729,7 @@
errors.pushSource(injectionPoint);
try {
Member member = injectionPoint.getMember();
- Annotation misplacedBindingAnnotation = Keys.findBindingAnnotation(
+ Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation(
errors, member, ((AnnotatedElement) member).getAnnotations());
if (misplacedBindingAnnotation != null) {
errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation);
@@ -838,13 +802,6 @@
};
}
- try {
- injectionPoint = InjectionPoint.get(method);
- } catch (ConfigurationException e) {
- errors.merge(e.getErrorMessages());
- throw errors.toException();
- }
-
parameterInjectors = injector.getParametersInjectors(injectionPoint, errors);
}
@@ -875,18 +832,12 @@
throws IllegalAccessException, InvocationTargetException;
}
- final Map<Class<?>, Object> constructors = new ReferenceCache<Class<?>, Object>() {
+ /** Cached constructor injectors for each type */
+ final FailableCache<Class<?>, ConstructorInjector<?>> constructors
+ = new FailableCache<Class<?>, ConstructorInjector<?>>() {
@SuppressWarnings("unchecked")
- protected Object create(Class<?> implementation) {
- Errors errors = new Errors();
- try {
- ConstructorInjector result = new ConstructorInjector(
- errors, InjectorImpl.this, implementation);
- errors.throwIfNecessary();
- return result;
- } catch (ErrorsException e) {
- return errors.merge(e.getErrors()).makeImmutable();
- }
+ protected ConstructorInjector<?> create(Class<?> key, Errors errors) throws ErrorsException {
+ return new ConstructorInjector(errors, InjectorImpl.this, key);
}
};
@@ -939,7 +890,7 @@
return;
}
- for (SingleMemberInjector injector : getMemberInjectors(o.getClass(), errors)) {
+ for (SingleMemberInjector injector : injectors.get(o.getClass(), errors)) {
injector.inject(errors, context, o);
}
}
@@ -970,8 +921,7 @@
return getProvider(Key.get(type));
}
- <T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors)
- throws ErrorsException {
+ <T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException {
final InternalFactory<? extends T> factory = getInternalFactory(key, errors);
return new Provider<T>() {
@@ -1049,40 +999,12 @@
}
}
- /** Gets a constructor function for a given implementation class. */
- @SuppressWarnings("unchecked")
- <T> ConstructorInjector<T> getConstructor(Class<T> implementation, Errors errors)
- throws ErrorsException {
- Object o = constructors.get(implementation);
- if (o instanceof Errors) {
- errors.merge((Errors) o);
- throw errors.toException();
- }
- else if (o instanceof ConstructorInjector<?>) {
- return (ConstructorInjector<T>) o;
- }
- else {
- throw new AssertionError();
- }
- }
-
/** Injects a field or method in a given object. */
public interface SingleMemberInjector {
void inject(Errors errors, InternalContext context, Object o);
InjectionPoint getInjectionPoint();
}
- Set<InjectionPoint> getFieldAndMethodInjectionsFor(Class<?> clazz)
- throws ErrorsException {
- Errors errors = new Errors();
- Set<InjectionPoint> dependencies = Sets.newLinkedHashSet();
- for (SingleMemberInjector singleMemberInjector : getMemberInjectors(clazz, errors)) {
- dependencies.add(singleMemberInjector.getInjectionPoint());
- }
- errors.throwIfNecessary();
- return ImmutableSet.copyOf(dependencies);
- }
-
public String toString() {
return new ToStringBuilder(Injector.class)
.add("bindings", explicitBindings)
diff --git a/src/com/google/inject/InstanceBindingImpl.java b/src/com/google/inject/InstanceBindingImpl.java
index aa81b4b..0588896 100644
--- a/src/com/google/inject/InstanceBindingImpl.java
+++ b/src/com/google/inject/InstanceBindingImpl.java
@@ -17,7 +17,7 @@
package com.google.inject;
-import com.google.inject.internal.ErrorsException;
+import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.InjectionPoint;
@@ -28,10 +28,13 @@
final T instance;
final Provider<T> provider;
+ final ImmutableSet<InjectionPoint> injectionPoints;
InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
- InternalFactory<? extends T> internalFactory, T instance) {
+ InternalFactory<? extends T> internalFactory, Set<InjectionPoint> injectionPoints,
+ T instance) {
super(injector, key, source, internalFactory, Scopes.NO_SCOPE, LoadStrategy.EAGER);
+ this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
this.instance = instance;
this.provider = Providers.of(instance);
}
@@ -41,21 +44,7 @@
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitInstance(instance);
- }
-
- public T getInstance() {
- return this.instance;
- }
-
- public Set<InjectionPoint> getInjectionPoints() {
- try {
- return injector.getFieldAndMethodInjectionsFor(instance.getClass());
- } catch (ErrorsException e) {
- // this would have been a creation exception
- // TODO: initialize the dependencies via a callback
- throw new AssertionError(e);
- }
+ return visitor.visitInstance(instance, injectionPoints);
}
@Override public String toString() {
diff --git a/src/com/google/inject/InternalContext.java b/src/com/google/inject/InternalContext.java
index 69eb611..bcb4e59 100644
--- a/src/com/google/inject/InternalContext.java
+++ b/src/com/google/inject/InternalContext.java
@@ -38,10 +38,6 @@
this.injector = injector;
}
- public InjectorImpl getInjector() {
- return injector;
- }
-
@SuppressWarnings("unchecked")
public <T> ConstructionContext<T> getConstructionContext(Object key) {
if (constructionContexts == null) {
diff --git a/src/com/google/inject/Module.java b/src/com/google/inject/Module.java
index baac103..10fcdc0 100644
--- a/src/com/google/inject/Module.java
+++ b/src/com/google/inject/Module.java
@@ -33,8 +33,11 @@
public interface Module {
/**
- * Contributes bindings and other configurations for this module to a
- * {@code Binder}.
+ * Contributes bindings and other configurations for this module to a {@code Binder}.
+ *
+ * <p><strong>Do not invoke this method directly</strong> to install submodules. Instead use
+ * {@link Binder#install(Module)}, which ensures that {@link Provides provider methods} are
+ * discovered.
*/
void configure(Binder binder);
}
diff --git a/src/com/google/inject/ProviderInstanceBindingImpl.java b/src/com/google/inject/ProviderInstanceBindingImpl.java
index 41336e4..977d3d3 100644
--- a/src/com/google/inject/ProviderInstanceBindingImpl.java
+++ b/src/com/google/inject/ProviderInstanceBindingImpl.java
@@ -16,45 +16,33 @@
package com.google.inject;
-import com.google.inject.internal.ErrorsException;
+import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.HasInjections;
import com.google.inject.spi.InjectionPoint;
import java.util.Set;
/**
*
*/
-class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements HasInjections {
+class ProviderInstanceBindingImpl<T> extends BindingImpl<T> {
final Provider<? extends T> providerInstance;
+ final ImmutableSet<InjectionPoint> injectionPoints;
ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
Provider<? extends T> providerInstance,
- LoadStrategy loadStrategy) {
+ LoadStrategy loadStrategy,
+ Set<InjectionPoint> injectionPoints) {
super(injector, key, source, internalFactory, scope, loadStrategy);
this.providerInstance = providerInstance;
+ this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitProvider(providerInstance);
- }
-
- public Provider<? extends T> getProviderInstance() {
- return this.providerInstance;
- }
-
- public Set<InjectionPoint> getInjectionPoints() {
- try {
- return injector.getFieldAndMethodInjectionsFor(providerInstance.getClass());
- } catch (ErrorsException e) {
- // this would have been a creation exception
- // TODO: initialize the dependencies via a callback
- throw new AssertionError(e);
- }
+ return visitor.visitProvider(providerInstance, injectionPoints);
}
@Override
diff --git a/src/com/google/inject/Provides.java b/src/com/google/inject/Provides.java
index b587765..8df6a65 100644
--- a/src/com/google/inject/Provides.java
+++ b/src/com/google/inject/Provides.java
@@ -16,15 +16,15 @@
package com.google.inject;
-import java.lang.annotation.Target;
-import java.lang.annotation.Retention;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * Annotates methods which can be used as providers. Guice will pass
- * dependencies to the method as parameters.
+ * Annotates methods of a {@link Module} to create a provider method binding. The method's return
+ * type is bound to it's returned value. Guice will pass dependencies to the method as parameters.
*
* @author crazybob@google.com (Bob Lee)
*/
diff --git a/src/com/google/inject/ProvisionException.java b/src/com/google/inject/ProvisionException.java
index 9a2d455..4069288 100644
--- a/src/com/google/inject/ProvisionException.java
+++ b/src/com/google/inject/ProvisionException.java
@@ -92,4 +92,6 @@
throw new ProvisionException(errors.makeImmutable());
}
}
+
+ private static final long serialVersionUID = 0;
}
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 215679b..07c9227 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -21,10 +21,9 @@
import com.google.inject.internal.BytecodeGen.Visibility;
import static com.google.inject.internal.BytecodeGen.newEnhancer;
import static com.google.inject.internal.BytecodeGen.newFastClass;
-import com.google.inject.internal.ConfigurationException;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.ReferenceCache;
+import com.google.inject.internal.FailableCache;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -56,39 +55,20 @@
defaultFactory = new DefaultConstructionProxyFactory();
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked") // the constructed T is the same as the injection point's T
public <T> ConstructionProxy<T> get(Errors errors, InjectionPoint injectionPoint)
throws ErrorsException {
- return (ConstructionProxy<T>) getConstructionProxy(injectionPoint, errors);
+ return (ConstructionProxy<T>) constructionProxies.get(injectionPoint, errors);
}
- Map<InjectionPoint, Object> constructionProxies = new ReferenceCache<InjectionPoint, Object>() {
- protected Object create(InjectionPoint injectionPoint) {
- Errors errors = new Errors();
- try {
- ConstructionProxy<?> result = createConstructionProxy(errors, injectionPoint);
- errors.throwIfNecessary();
- return result;
- } catch (ErrorsException e) {
- return errors.merge(e.getErrors()).makeImmutable();
- }
+ /** Cached construction proxies for each injection point */
+ FailableCache<InjectionPoint, ConstructionProxy> constructionProxies
+ = new FailableCache<InjectionPoint, ConstructionProxy>() {
+ protected ConstructionProxy create(InjectionPoint key, Errors errors) throws ErrorsException {
+ return createConstructionProxy(errors, key);
}
};
- public ConstructionProxy<?> getConstructionProxy(InjectionPoint injectionPoint, Errors errors)
- throws ErrorsException {
- Object constructionProxyOrErrors = constructionProxies.get(injectionPoint);
-
- if (constructionProxyOrErrors instanceof ConstructionProxy<?>) {
- return (ConstructionProxy<?>) constructionProxyOrErrors;
- } else if (constructionProxyOrErrors instanceof Errors) {
- errors.merge((Errors) constructionProxyOrErrors);
- throw errors.toException();
- } else {
- throw new AssertionError();
- }
- }
-
<T> ConstructionProxy<T> createConstructionProxy(Errors errors, InjectionPoint injectionPoint)
throws ErrorsException {
@SuppressWarnings("unchecked") // the member of injectionPoint is always a Constructor<T>
@@ -170,25 +150,19 @@
// Store callbacks.
Enhancer.registerStaticCallbacks(proxied, callbacks);
- return createConstructionProxy(errors, proxied, constructor);
+ return createConstructionProxy(proxied, injectionPoint);
}
/**
* Creates a construction proxy given a class and parameter types.
*/
- private <T> ConstructionProxy<T> createConstructionProxy(Errors errors, final Class<?> clazz,
- final Constructor<T> standardConstructor) throws ErrorsException {
+ private <T> ConstructionProxy<T> createConstructionProxy(final Class<?> clazz,
+ final InjectionPoint injectionPoint) throws ErrorsException {
+ @SuppressWarnings("unchecked") // injection point's member must be a Constructor<T>
+ final Constructor<T> standardConstructor = (Constructor<T>) injectionPoint.getMember();
FastClass fastClass = newFastClass(clazz, Visibility.PUBLIC);
final FastConstructor fastConstructor
= fastClass.getConstructor(standardConstructor.getParameterTypes());
- final InjectionPoint injectionPoint;
-
- try {
- injectionPoint = InjectionPoint.get(standardConstructor);
- } catch (ConfigurationException e) {
- errors.merge(e.getErrorMessages());
- throw errors.toException();
- }
return new ConstructionProxy<T>() {
@SuppressWarnings("unchecked")
diff --git a/src/com/google/inject/ScopeBindingProcessor.java b/src/com/google/inject/ScopeBindingProcessor.java
index 3cc5cdb..1458df5 100644
--- a/src/com/google/inject/ScopeBindingProcessor.java
+++ b/src/com/google/inject/ScopeBindingProcessor.java
@@ -43,7 +43,7 @@
Scope scope = command.getScope();
Class<? extends Annotation> annotationType = command.getAnnotationType();
- if (!Scopes.isScopeAnnotation(annotationType)) {
+ if (!Annotations.isScopeAnnotation(annotationType)) {
errors.withSource(annotationType).missingScopeAnnotation();
// Go ahead and bind anyway so we don't get collateral errors.
}
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index 8cb2cba..e2597cc 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -16,10 +16,6 @@
package com.google.inject;
-import com.google.inject.internal.Classes;
-import com.google.inject.internal.Errors;
-import java.lang.annotation.Annotation;
-
/**
* Built in scope implementations.
*
@@ -87,61 +83,7 @@
}
};
- /**
- * Returns the scope annotation on {@code type}, or null if none is specified.
- */
- static Class<? extends Annotation> findScopeAnnotation(
- Errors errors, Class<?> implementation) {
- return findScopeAnnotation(errors, implementation.getAnnotations());
- }
-
-
- /**
- * Returns the scoping annotation, or null if there isn't one.
- */
- static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
- Class<? extends Annotation> found = null;
-
- for (Annotation annotation : annotations) {
- if (annotation.annotationType()
- .isAnnotationPresent(ScopeAnnotation.class)) {
- if (found != null) {
- errors.duplicateScopeAnnotations(found, annotation.annotationType());
- } else {
- found = annotation.annotationType();
- }
- }
- }
-
- return found;
- }
-
- static boolean isScopeAnnotation(Annotation annotation) {
- return isScopeAnnotation(annotation.annotationType());
- }
-
- static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
- return annotationType.isAnnotationPresent(ScopeAnnotation.class);
- }
-
- /**
- * Adds an error if there is a misplaced annotations on {@code type}. Scoping
- * annotations are not allowed on abstract classes or interfaces.
- */
- static void checkForMisplacedScopeAnnotations(Class<?> type, Object source, Errors errors) {
- if (Classes.isConcrete(type)) {
- return;
- }
-
- Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
- if (scopeAnnotation != null) {
- errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
- }
- }
-
- /**
- * Scopes an internal factory.
- */
+ /** Scopes an internal factory. */
static <T> InternalFactory<? extends T> scope(Key<T> key,
InjectorImpl injector, InternalFactory<? extends T> creator,
Scope scope) {
diff --git a/src/com/google/inject/internal/Annotations.java b/src/com/google/inject/internal/Annotations.java
index 6248170..ba749d5 100644
--- a/src/com/google/inject/internal/Annotations.java
+++ b/src/com/google/inject/internal/Annotations.java
@@ -16,9 +16,14 @@
package com.google.inject.internal;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Key;
+import com.google.inject.ScopeAnnotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Member;
+import java.lang.reflect.Type;
/**
* Annotation utilities.
@@ -30,9 +35,79 @@
/**
* Returns true if the given annotation is retained at runtime.
*/
- public static boolean isRetainedAtRuntime(
- Class<? extends Annotation> annotationType) {
+ public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
Retention retention = annotationType.getAnnotation(Retention.class);
- return !(retention == null || retention.value() != RetentionPolicy.RUNTIME);
+ return retention != null && retention.value() == RetentionPolicy.RUNTIME;
+ }
+
+ /** Returns the scope annotation on {@code type}, or null if none is specified. */
+ public static Class<? extends Annotation> findScopeAnnotation(
+ Errors errors, Class<?> implementation) {
+ return findScopeAnnotation(errors, implementation.getAnnotations());
+ }
+
+ /** Returns the scoping annotation, or null if there isn't one. */
+ public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
+ Class<? extends Annotation> found = null;
+
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(ScopeAnnotation.class)) {
+ if (found != null) {
+ errors.duplicateScopeAnnotations(found, annotation.annotationType());
+ } else {
+ found = annotation.annotationType();
+ }
+ }
+ }
+
+ return found;
+ }
+
+ public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
+ return annotationType.isAnnotationPresent(ScopeAnnotation.class);
+ }
+
+ /**
+ * Adds an error if there is a misplaced annotations on {@code type}. Scoping
+ * annotations are not allowed on abstract classes or interfaces.
+ */
+ public static void checkForMisplacedScopeAnnotations(Class<?> type, Object source, Errors errors) {
+ if (Classes.isConcrete(type)) {
+ return;
+ }
+
+ Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
+ if (scopeAnnotation != null) {
+ errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
+ }
+ }
+
+ /** Gets a key for the given type, member and annotations. */
+ public static Key<?> getKey(Type type, Member member, Annotation[] annotations, Errors errors)
+ throws ErrorsException {
+ Annotation found = findBindingAnnotation(errors, member, annotations);
+ errors.throwIfNecessary();
+ return found == null ? Key.get(type) : Key.get(type, found);
+ }
+
+ /**
+ * Returns the binding annotation on {@code member}, or null if there isn't one.
+ */
+ public static Annotation findBindingAnnotation(
+ Errors errors, Member member, Annotation[] annotations) {
+ Annotation found = null;
+
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(BindingAnnotation.class)) {
+ if (found != null) {
+ errors.duplicateBindingAnnotations(member,
+ found.annotationType(), annotation.annotationType());
+ } else {
+ found = annotation;
+ }
+ }
+ }
+
+ return found;
}
}
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index 9063565..228d2d1 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -38,8 +38,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Formatter;
-import java.util.List;
import java.util.Iterator;
+import java.util.List;
/**
* A collection of error messages. If this type is passed as a method parameter, the method is
@@ -90,10 +90,6 @@
checkArgument(source == popped);
}
- public Errors userReportedError(String messageFormat, List<Object> arguments) {
- return addMessage(messageFormat, arguments);
- }
-
/**
* We use a fairly generic error message here. The motivation is to share the
* same message for both bind time errors:
@@ -182,11 +178,6 @@
return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
}
- public Errors cannotBindToNullInstance() {
- return addMessage("Binding to null instances is not allowed. "
- + "Use toProvider(Providers.of(null)) if this is your intended behaviour.");
- }
-
public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
return addMessage("No scope is bound to %s.", scopeAnnotation);
}
@@ -294,7 +285,7 @@
List<Object> sources = Lists.newArrayList();
sources.addAll(this.sources);
sources.addAll(message.getSources());
- return new Message(message.getMessage(), message.getCause(), stripDuplicates(sources));
+ return new Message(stripDuplicates(sources), message.getMessage(), message.getCause());
}
public Errors merge(Collection<Message> messages) {
@@ -333,7 +324,7 @@
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments);
- addMessage(new Message(message, cause, stripDuplicates(sources)));
+ addMessage(new Message(stripDuplicates(sources), message, cause));
return this;
}
diff --git a/src/com/google/inject/internal/FailableCache.java b/src/com/google/inject/internal/FailableCache.java
new file mode 100644
index 0000000..72a4bdf
--- /dev/null
+++ b/src/com/google/inject/internal/FailableCache.java
@@ -0,0 +1,51 @@
+/**
+ * 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;
+
+/**
+ * Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
+ * exception is thrown on retrieval.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public abstract class FailableCache<K, V> {
+
+ private final ReferenceCache<K, Object> delegate = new ReferenceCache<K, Object>() {
+ protected final Object create(K key) {
+ Errors errors = new Errors();
+ V result = null;
+ try {
+ result = FailableCache.this.create(key, errors);
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+ return errors.hasErrors() ? errors.makeImmutable() : result;
+ }
+ };
+
+ protected abstract V create(K key, Errors errors) throws ErrorsException;
+
+ public V get(K key, Errors errors) throws ErrorsException {
+ Object resultOrError = delegate.get(key);
+ if (resultOrError instanceof Errors) {
+ errors.merge((Errors) resultOrError);
+ throw errors.toException();
+ } else {
+ return (V) resultOrError;
+ }
+ }
+}
diff --git a/src/com/google/inject/internal/Keys.java b/src/com/google/inject/internal/Keys.java
deleted file mode 100644
index 0b2beac..0000000
--- a/src/com/google/inject/internal/Keys.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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.BindingAnnotation;
-import com.google.inject.Key;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Member;
-import java.lang.reflect.Type;
-
-public class Keys {
-
- /**
- * Gets a key for the given type, member and annotations.
- */
- public static Key<?> get(Type type, Member member, Annotation[] annotations, Errors errors)
- throws ErrorsException {
- Annotation found = findBindingAnnotation(errors, member, annotations);
- errors.throwIfNecessary();
- return found == null ? Key.get(type) : Key.get(type, found);
- }
-
- /**
- * Returns the binding annotation on {@code member}, or null if there isn't one.
- */
- public static Annotation findBindingAnnotation(
- Errors errors, Member member, Annotation[] annotations) {
- Annotation found = null;
-
- for (Annotation annotation : annotations) {
- if (annotation.annotationType().isAnnotationPresent(BindingAnnotation.class)) {
- if (found != null) {
- errors.duplicateBindingAnnotations(member,
- found.annotationType(), annotation.annotationType());
- } else {
- found = annotation;
- }
- }
- }
-
- return found;
- }
-}
diff --git a/src/com/google/inject/internal/ModuleBinding.java b/src/com/google/inject/internal/ModuleBinding.java
index f7aa503..87dc78f 100644
--- a/src/com/google/inject/internal/ModuleBinding.java
+++ b/src/com/google/inject/internal/ModuleBinding.java
@@ -17,6 +17,8 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Key;
@@ -32,7 +34,12 @@
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.DefaultBindingTargetVisitor;
import com.google.inject.spi.ElementVisitor;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.Message;
+import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
/**
* Immutable snapshot of a request to bind a value.
@@ -57,7 +64,7 @@
private static final BindingTargetVisitor<Object, Boolean> SUPPORTS_SCOPES
= new DefaultBindingTargetVisitor<Object, Boolean>() {
- @Override public Boolean visitInstance(Object instance) {
+ @Override public Boolean visitInstance(Object instance, Set<InjectionPoint> injectionPoints) {
return false;
}
@@ -187,15 +194,47 @@
public void toInstance(final T instance) {
checkNotTargetted();
- target = new InstanceTarget<T>(instance);
+
+ if (instance == null) {
+ binder.addError("Binding to null instances is not allowed. "
+ + "Use toProvider(Providers.of(null)) if this is your intended behaviour.");
+ // we finish the binding to prevent additional errors
+ toProvider(Providers.<T>of(null));
+ return;
+ }
+
+ // lookup the injection points, adding any errors to the binder's errors list
+ List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ try {
+ InjectionPoint.addForInstanceMethodsAndFields(instance.getClass(), injectionPointsList);
+ } catch (ConfigurationException e) {
+ for (Message message : e.getErrorMessages()) {
+ binder.addError(message);
+ }
+ }
+ ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
+
+ target = new InstanceTarget<T>(instance, injectionPoints);
}
public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
checkNotNull(provider, "provider");
checkNotTargetted();
+
+ // lookup the injection points, adding any errors to the binder's errors list
+ List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ try {
+ InjectionPoint.addForInstanceMethodsAndFields(provider.getClass(), injectionPointsList);
+ } catch (ConfigurationException e) {
+ for (Message message : e.getErrorMessages()) {
+ binder.addError(message);
+ }
+ }
+ final ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
+
target = new Target<T>() {
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitProvider(provider);
+ return visitor.visitProvider(provider, injectionPoints);
}
};
return this;
@@ -408,7 +447,8 @@
key = Key.get(typeAsClassT);
}
- ModuleBinding.this.target = new InstanceTarget<T>(instanceAsT);
+ ModuleBinding.this.target = new InstanceTarget<T>(instanceAsT,
+ ImmutableSet.<InjectionPoint>of());
}
@Override public String toString() {
@@ -430,13 +470,15 @@
static class InstanceTarget<T> implements Target<T> {
private final T instance;
+ private final ImmutableSet<InjectionPoint> injectionPoints;
- public InstanceTarget(T instance) {
+ public InstanceTarget(T instance, ImmutableSet<InjectionPoint> injectionPoints) {
this.instance = instance;
+ this.injectionPoints = injectionPoints;
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitInstance(instance);
+ return visitor.visitInstance(instance, injectionPoints);
}
}
}
diff --git a/src/com/google/inject/internal/SourceProvider.java b/src/com/google/inject/internal/SourceProvider.java
index 1f99ee8..741dbda 100644
--- a/src/com/google/inject/internal/SourceProvider.java
+++ b/src/com/google/inject/internal/SourceProvider.java
@@ -20,7 +20,6 @@
import static com.google.common.collect.Iterables.concat;
import com.google.common.collect.Lists;
import java.util.List;
-import java.util.Set;
/**
* Provides access to the calling line of code.
@@ -59,11 +58,6 @@
return strings;
}
- /** Returns an immutable set with the names of the classes that are skipped. */
- public Set<String> getSkippedClassNames() {
- return classNamesToSkip;
- }
-
/**
* Returns the calling line of code. The selected line is the nearest to the top of the stack that
* is not skipped.
diff --git a/src/com/google/inject/matcher/AbstractMatcher.java b/src/com/google/inject/matcher/AbstractMatcher.java
index 1897b1a..91ea2e6 100644
--- a/src/com/google/inject/matcher/AbstractMatcher.java
+++ b/src/com/google/inject/matcher/AbstractMatcher.java
@@ -58,6 +58,8 @@
@Override public String toString() {
return "and(" + a + ", " + b + ")";
}
+
+ private static final long serialVersionUID = 0;
}
private static class OrMatcher<T> extends AbstractMatcher<T> implements Serializable {
@@ -85,5 +87,7 @@
@Override public String toString() {
return "or(" + a + ", " + b + ")";
}
+
+ private static final long serialVersionUID = 0;
}
}
diff --git a/src/com/google/inject/matcher/Matchers.java b/src/com/google/inject/matcher/Matchers.java
index 32c1a2e..9bb5b55 100644
--- a/src/com/google/inject/matcher/Matchers.java
+++ b/src/com/google/inject/matcher/Matchers.java
@@ -54,6 +54,8 @@
public Object readResolve() {
return any();
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -86,6 +88,8 @@
@Override public String toString() {
return "not(" + delegate + ")";
}
+
+ private static final long serialVersionUID = 0;
}
private static void checkForRuntimeRetention(
@@ -129,6 +133,8 @@
@Override public String toString() {
return "annotatedWith(" + annotationType.getSimpleName() + ".class)";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -166,6 +172,8 @@
@Override public String toString() {
return "annotatedWith(" + annotation + ")";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -200,6 +208,8 @@
@Override public String toString() {
return "subclassesOf(" + superclass.getSimpleName() + ".class)";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -233,6 +243,8 @@
@Override public String toString() {
return "only(" + value + ")";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -266,6 +278,8 @@
@Override public String toString() {
return "identicalTo(" + value + ")";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -305,6 +319,8 @@
public Object readResolve() {
return inPackage(Package.getPackage(packageName));
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -340,6 +356,8 @@
@Override public String toString() {
return "inSubpackage(" + targetPackageName + ")";
}
+
+ private static final long serialVersionUID = 0;
}
/**
@@ -373,5 +391,7 @@
@Override public String toString() {
return "returns(" + returnType + ")";
}
+
+ private static final long serialVersionUID = 0;
}
}
diff --git a/src/com/google/inject/spi/BindingTargetVisitor.java b/src/com/google/inject/spi/BindingTargetVisitor.java
index ab00d1b..0da1c3e 100644
--- a/src/com/google/inject/spi/BindingTargetVisitor.java
+++ b/src/com/google/inject/spi/BindingTargetVisitor.java
@@ -19,6 +19,7 @@
import com.google.inject.Key;
import com.google.inject.Provider;
import java.lang.reflect.Constructor;
+import java.util.Set;
/**
* Visits each of the strategies used to find an instance to satisfy an injection.
@@ -33,16 +34,20 @@
* found in both module and injector bindings.
*
* @param instance the user-supplied value
+ * @param injectionPoints the field and method injection points of the instance, injected at
+ * injector-creation time only.
*/
- V visitInstance(T instance);
+ V visitInstance(T instance, Set<InjectionPoint> injectionPoints);
/**
* Visit a provider instance binding. The provider's {@code get} method is invoked to resolve
* injections. This target is found in both module and injector bindings.
*
* @param provider the user-supplied, unscoped provider
+ * @param injectionPoints the field and method injection points of the provider, injected at
+ * injector-creation time only.
*/
- V visitProvider(Provider<? extends T> provider);
+ V visitProvider(Provider<? extends T> provider, Set<InjectionPoint> injectionPoints);
/**
* Visit a provider key binding. To resolve injections, the provider injection is first
@@ -76,8 +81,10 @@
*
* @param constructor the {@link com.google.inject.Inject annotated} or default constructor that
* is invoked for creating values
+ * @param injectionPoints the constructor, field and method injection points to create and
+ * populate a new instance. The set contains exactly one constructor injection point.
*/
- V visitConstructor(Constructor<? extends T> constructor);
+ V visitConstructor(Constructor<? extends T> constructor, Set<InjectionPoint> injectionPoints);
/**
* Visit a binding created from converting a bound instance to a new type. The source binding
diff --git a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
index c8da65c..8e30542 100644
--- a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
+++ b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
@@ -19,6 +19,7 @@
import com.google.inject.Key;
import com.google.inject.Provider;
import java.lang.reflect.Constructor;
+import java.util.Set;
/**
* No-op visitor for subclassing. All interface methods simply delegate to
@@ -35,11 +36,11 @@
return null;
}
- public V visitInstance(T instance) {
+ public V visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
return visitOther();
}
- public V visitProvider(Provider<? extends T> provider) {
+ public V visitProvider(Provider<? extends T> provider, Set<InjectionPoint> injectionPoints) {
return visitOther();
}
@@ -55,7 +56,8 @@
return visitOther();
}
- public V visitConstructor(Constructor<? extends T> constructor) {
+ public V visitConstructor(Constructor<? extends T> constructor,
+ Set<InjectionPoint> injectionPoints) {
return visitOther();
}
diff --git a/src/com/google/inject/spi/Dependency.java b/src/com/google/inject/spi/Dependency.java
index 1d71fdd..fd77a22 100644
--- a/src/com/google/inject/spi/Dependency.java
+++ b/src/com/google/inject/spi/Dependency.java
@@ -108,4 +108,6 @@
}
return builder.toString();
}
+
+ private static final long serialVersionUID = 0;
}
diff --git a/src/com/google/inject/spi/Elements.java b/src/com/google/inject/spi/Elements.java
index 8f449f6..025674c 100644
--- a/src/com/google/inject/spi/Elements.java
+++ b/src/com/google/inject/spi/Elements.java
@@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
@@ -25,7 +26,6 @@
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
-import com.google.inject.ProviderMethods;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
@@ -51,7 +51,7 @@
public final class Elements {
private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
= new DefaultBindingTargetVisitor<Object, Object>() {
- @Override public Object visitInstance(Object instance) {
+ @Override public Object visitInstance(Object instance, Set<InjectionPoint> injectionPoints) {
return instance;
}
@@ -139,11 +139,15 @@
}
public void requestInjection(Object... instances) {
- elements.add(new InjectionRequest(getSource(), instances));
+ for (Object instance : instances) {
+ elements.add(new InjectionRequest(getSource(), instance));
+ }
}
public void requestStaticInjection(Class<?>... types) {
- elements.add(new StaticInjectionRequest(getSource(), types));
+ for (Class<?> type : types) {
+ elements.add(new StaticInjectionRequest(getSource(), type));
+ }
}
public void install(Module module) {
@@ -166,7 +170,8 @@
}
public void addError(Throwable t) {
- elements.add(new Message(getSource(), t));
+ String message = "An exception was caught and reported. Message: " + t.getMessage();
+ elements.add(new Message(ImmutableList.of(getSource()), message, t));
}
public void addError(Message message) {
diff --git a/src/com/google/inject/spi/HasInjections.java b/src/com/google/inject/spi/HasInjections.java
deleted file mode 100644
index 2e23769..0000000
--- a/src/com/google/inject/spi/HasInjections.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (C) 2007 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.spi;
-
-import java.util.Set;
-
-/**
- * Implemented by bindings which have implementation dependencies on other
- * bindings.
- */
-public interface HasInjections {
-
- /**
- * Gets the injection points for this binding.
- */
- Set<InjectionPoint> getInjectionPoints();
-}
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index af18e9b..27c24a3 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -20,10 +20,10 @@
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Key;
+import com.google.inject.internal.Annotations;
import com.google.inject.internal.ConfigurationException;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.Keys;
import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.Nullability;
import java.io.ObjectStreamException;
@@ -42,7 +42,9 @@
import java.util.List;
/**
- * A constructor, method or field that can receive injections.
+ * A constructor, field or method that can receive injections. Typically this is a member with the
+ * {@literal @}{@link Inject} annotation. For non-private, no argument constructors, the member may
+ * omit the annotation.
*
* @author crazybob@google.com (Bob Lee)
*/
@@ -59,7 +61,7 @@
this.optional = optional;
}
- private InjectionPoint(Method method) {
+ InjectionPoint(Method method) {
this.member = method;
Inject inject = method.getAnnotation(Inject.class);
@@ -69,7 +71,7 @@
method.getParameterAnnotations());
}
- private InjectionPoint(Constructor<?> constructor) {
+ InjectionPoint(Constructor<?> constructor) {
this.member = constructor;
this.optional = false;
// TODO(jessewilson): make sure that if @Inject it exists, its not optional
@@ -77,7 +79,7 @@
constructor.getParameterAnnotations());
}
- private InjectionPoint(Field field) {
+ InjectionPoint(Field field) {
this.member = field;
Inject inject = field.getAnnotation(Inject.class);
@@ -88,7 +90,7 @@
Errors errors = new Errors(field);
Key<?> key = null;
try {
- key = Keys.get(field.getGenericType(), field, annotations, errors);
+ key = Annotations.getKey(field.getGenericType(), field, annotations, errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@@ -108,7 +110,7 @@
for (Type parameterType : genericParameterTypes) {
try {
Annotation[] parameterAnnotations = annotationsIterator.next();
- Key<?> key = Keys.get(parameterType, member, parameterAnnotations, errors);
+ Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
index++;
} catch (ErrorsException e) {
@@ -120,10 +122,14 @@
return ImmutableList.copyOf(dependencies);
}
+ // This metohd is necessary to create a Dependency<T> with proper generic type information
private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
return new Dependency<T>(this, key, allowsNull, parameterIndex);
}
+ /**
+ * Returns the injected constructor, field, or method.
+ */
public Member getMember() {
return member;
}
@@ -132,11 +138,19 @@
* Returns the dependencies for this injection point. If the injection point is for a method or
* constructor, the dependencies will correspond to that member's parameters. Field injection
* points always have a single dependency for the field itself.
+ *
+ * @return a possibly-empty list
*/
public List<Dependency<?>> getDependencies() {
return dependencies;
}
+ /**
+ * Returns true if this injection point shall be skipped if the injector cannot resolve bindings
+ * for all required dependencies. Both explicit bindings (as specified in a module), and implicit
+ * bindings ({@literal @}{@link com.google.inject.ImplementedBy ImplementedBy}, default
+ * constructors etc.) may be used to satisfy optional injection points.
+ */
public boolean isOptional() {
return optional;
}
@@ -160,69 +174,6 @@
}
/**
- * Returns a new injection point for {@code constructor}.
- *
- * @param constructor a no arguments constructor, or a constructor with any number of arguments
- * and the {@literal @}{@link Inject} annotation.
- */
- public static InjectionPoint get(Constructor constructor) {
- return new InjectionPoint(constructor);
- }
-
- /**
- * Returns a new injection point for {@code method}.
- *
- * @param method a method with the {@literal @}{@link Inject} annotation.
- */
- public static InjectionPoint get(Method method) {
- return new InjectionPoint(method);
- }
-
- /**
- * Returns a new injection point for {@code field}.
- *
- * @param field a field with the {@literal @}{@link Inject} annotation.
- */
- public static InjectionPoint get(Field field) {
- return new InjectionPoint(field);
- }
-
- /**
- * Adds all static method and field injection points on {@code type} to {@code injectionPoints}.
- * All fields are added first, and then all methods. Within the fields, supertype fields are added
- * before subtype fields. Similarly, supertype methods are added before subtype methods.
- *
- * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
- * field with multiple binding annotations. When such an exception is thrown, the valid
- * injection points are still added to the collection.
- */
- public static void addForStaticMethodsAndFields(Class<?> type,
- Collection<InjectionPoint> injectionPoints) {
- Errors errors = new Errors();
- addInjectionPoints(type, Factory.FIELDS, true, injectionPoints, errors);
- addInjectionPoints(type, Factory.METHODS, true, injectionPoints, errors);
- ConfigurationException.throwNewIfNonEmpty(errors);
- }
-
- /**
- * Adds all instance method and field injection points on {@code type} to {@code injectionPoints}.
- * All fields are added first, and then all methods. Within the fields, supertype fields are added
- * before subtype fields. Similarly, supertype methods are added before subtype methods.
- *
- * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
- * field with multiple binding annotations. When such an exception is thrown, the valid
- * injection points are still added to the collection.
- */
- public static void addForInstanceMethodsAndFields(Class<?> type,
- List<InjectionPoint> injectionPoints) {
- // TODO (crazybob): Filter out overridden members.
- Errors errors = new Errors();
- addInjectionPoints(type, Factory.FIELDS, false, injectionPoints, errors);
- addInjectionPoints(type, Factory.METHODS, false, injectionPoints, errors);
- ConfigurationException.throwNewIfNonEmpty(errors);
- }
-
- /**
* Returns a new injection point for the injectable constructor of {@code type}.
*
* @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
@@ -253,7 +204,7 @@
ConfigurationException.throwNewIfNonEmpty(errors);
if (found != null) {
- return get(found);
+ return new InjectionPoint(found);
}
// If no annotated constructor is found, look for a no-arg constructor
@@ -268,13 +219,47 @@
throw new ConfigurationException(errors);
}
- return get(noArgCtor);
+ return new InjectionPoint(noArgCtor);
} catch (NoSuchMethodException e) {
errors.missingConstructor(type);
throw new ConfigurationException(errors);
}
}
+ /**
+ * Adds all static method and field injection points on {@code type} to {@code injectionPoints}.
+ * All fields are added first, and then all methods. Within the fields, supertype fields are added
+ * before subtype fields. Similarly, supertype methods are added before subtype methods.
+ *
+ * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
+ * field with multiple binding annotations. When such an exception is thrown, the valid
+ * injection points are still added to the collection.
+ */
+ public static void addForStaticMethodsAndFields(Class<?> type, Collection<InjectionPoint> sink) {
+ Errors errors = new Errors();
+ addInjectionPoints(type, Factory.FIELDS, true, sink, errors);
+ addInjectionPoints(type, Factory.METHODS, true, sink, errors);
+ ConfigurationException.throwNewIfNonEmpty(errors);
+ }
+
+ /**
+ * Adds all instance method and field injection points on {@code type} to {@code injectionPoints}.
+ * All fields are added first, and then all methods. Within the fields, supertype fields are added
+ * before subtype fields. Similarly, supertype methods are added before subtype methods.
+ *
+ * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
+ * field with multiple binding annotations. When such an exception is thrown, the valid
+ * injection points are still added to the collection.
+ */
+ public static void addForInstanceMethodsAndFields(Class<?> type,
+ Collection<InjectionPoint> sink) {
+ // TODO (crazybob): Filter out overridden members.
+ Errors errors = new Errors();
+ addInjectionPoints(type, Factory.FIELDS, false, sink, errors);
+ addInjectionPoints(type, Factory.METHODS, false, sink, errors);
+ ConfigurationException.throwNewIfNonEmpty(errors);
+ }
+
private static <M extends Member & AnnotatedElement> void addInjectionPoints(Class<?> type,
Factory<M> factory, boolean statics, Collection<InjectionPoint> injectionPoints,
Errors errors) {
@@ -322,7 +307,7 @@
return type.getDeclaredFields();
}
public InjectionPoint create(Field member) {
- return get(member);
+ return new InjectionPoint(member);
}
};
@@ -331,11 +316,13 @@
return type.getDeclaredMethods();
}
public InjectionPoint create(Method member) {
- return get(member);
+ return new InjectionPoint(member);
}
};
M[] getMembers(Class<?> type);
InjectionPoint create(M member);
}
+
+ private static final long serialVersionUID = 0;
}
diff --git a/src/com/google/inject/spi/InjectionRequest.java b/src/com/google/inject/spi/InjectionRequest.java
index 53601fb..ba60f6b 100644
--- a/src/com/google/inject/spi/InjectionRequest.java
+++ b/src/com/google/inject/spi/InjectionRequest.java
@@ -17,8 +17,6 @@
package com.google.inject.spi;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
/**
* A request to inject the instance fields and methods of an instance. Requests are created
@@ -31,19 +29,19 @@
*/
public final class InjectionRequest implements Element {
private Object source;
- private List<Object> instances;
+ private Object instance;
- public InjectionRequest(Object source, Object[] instances) {
+ public InjectionRequest(Object source, Object instance) {
this.source = checkNotNull(source, "source");
- this.instances = ImmutableList.of(instances);
+ this.instance = checkNotNull(instance, "instance");
}
public Object getSource() {
return source;
}
- public List<Object> getInstances() {
- return instances;
+ public Object getInstance() {
+ return instance;
}
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
diff --git a/src/com/google/inject/spi/Message.java b/src/com/google/inject/spi/Message.java
index c5a139e..f417ed3 100644
--- a/src/com/google/inject/spi/Message.java
+++ b/src/com/google/inject/spi/Message.java
@@ -42,23 +42,18 @@
private final Throwable cause;
private final List<Object> sources;
- public Message(String message, Throwable cause, List<Object> sources) {
+ public Message(List<Object> sources, String message, Throwable cause) {
+ this.sources = ImmutableList.copyOf(sources);
this.message = checkNotNull(message, "message");
this.cause = cause;
- this.sources = ImmutableList.copyOf(sources);
- }
-
- public Message(Object source, Throwable throwable) {
- this("An exception was caught and reported. Message: " + throwable.getMessage(),
- throwable, ImmutableList.of(source));
}
public Message(Object source, String message) {
- this(message, null, ImmutableList.of(source));
+ this(ImmutableList.of(source), message, null);
}
public Message(String message) {
- this(message, (Throwable) null);
+ this(ImmutableList.of(), message, null);
}
public String getSource() {
diff --git a/src/com/google/inject/spi/ModuleWriter.java b/src/com/google/inject/spi/ModuleWriter.java
index cd03f78..3d43d16 100644
--- a/src/com/google/inject/spi/ModuleWriter.java
+++ b/src/com/google/inject/spi/ModuleWriter.java
@@ -28,6 +28,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.List;
+import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
/**
@@ -122,16 +123,13 @@
public void writeRequestInjection(final Binder binder,
final InjectionRequest command) {
- List<Object> objects = command.getInstances();
- binder.withSource(command.getSource())
- .requestInjection(objects.toArray());
+ binder.withSource(command.getSource()).requestInjection(command.getInstance());
}
public void writeRequestStaticInjection(final Binder binder,
final StaticInjectionRequest element) {
- List<Class> types = element.getTypes();
- binder.withSource(element.getSource())
- .requestStaticInjection(types.toArray(new Class[types.size()]));
+ Class<?> type = element.getType();
+ binder.withSource(element.getSource()).requestStaticInjection(type);
}
public void writeConvertToTypes(final Binder binder, final TypeConverterBinding element) {
@@ -152,12 +150,13 @@
public <T> ScopedBindingBuilder applyTarget(Binding<T> binding,
final LinkedBindingBuilder<T> linkedBindingBuilder) {
return binding.acceptTargetVisitor(new BindingTargetVisitor<T, ScopedBindingBuilder>() {
- public ScopedBindingBuilder visitInstance(T instance) {
+ public ScopedBindingBuilder visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
linkedBindingBuilder.toInstance(instance);
return null;
}
- public ScopedBindingBuilder visitProvider(Provider<? extends T> provider) {
+ public ScopedBindingBuilder visitProvider(Provider<? extends T> provider,
+ Set<InjectionPoint> injectionPoints) {
return linkedBindingBuilder.toProvider(provider);
}
@@ -177,7 +176,8 @@
throw new IllegalArgumentException("Non-module element");
}
- public ScopedBindingBuilder visitConstructor(Constructor<? extends T> constructor) {
+ public ScopedBindingBuilder visitConstructor(Constructor<? extends T> constructor,
+ Set<InjectionPoint> injectionPoints) {
throw new IllegalArgumentException("Non-module element");
}
diff --git a/src/com/google/inject/spi/ProviderMethods.java b/src/com/google/inject/spi/ProviderMethods.java
new file mode 100644
index 0000000..53506b0
--- /dev/null
+++ b/src/com/google/inject/spi/ProviderMethods.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2007 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.spi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.Lists;
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.internal.Annotations;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.TypeResolver;
+import com.google.inject.util.Modules;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
+ * binding annotations on the provider method to configure the binding.
+ */
+class ProviderMethods {
+
+ /**
+ * Returns a module which creates bindings for provider methods from the
+ * given object.
+ */
+ static Module from(Object providers) {
+ // avoid infinite recursion, since installing a module always installs itself
+ if (providers instanceof ProviderMethodsModule) {
+ return Modules.EMPTY_MODULE;
+ }
+
+ return new ProviderMethodsModule(providers);
+ }
+
+ static class ProviderMethodsModule implements Module {
+ final Object providers;
+ final TypeResolver typeResolver;
+ Binder binder;
+
+ ProviderMethodsModule(Object providers) {
+ this.providers = checkNotNull(providers, "providers");
+ this.typeResolver = new TypeResolver(providers.getClass());
+ }
+
+ public synchronized void configure(Binder binder) {
+ checkState(this.binder == null, "Re-entry is not allowed.");
+
+ for (Class c = providers.getClass(); c != Object.class; c = c.getSuperclass()) {
+ for (Method method : c.getDeclaredMethods()) {
+ if (!method.isAnnotationPresent(Provides.class)) {
+ continue;
+ }
+
+ this.binder = binder.withSource(method);
+ try {
+ bindProviderMethod(method);
+ } finally {
+ this.binder = null;
+ }
+ }
+ }
+ }
+
+ <T> void bindProviderMethod(final Method method) {
+ Errors errors = new Errors(method);
+
+ method.setAccessible(true);
+
+ Class<? extends Annotation> scopeAnnotation
+ = Annotations.findScopeAnnotation(errors, method.getAnnotations());
+ Annotation bindingAnnotation
+ = Annotations.findBindingAnnotation(errors, method, method.getAnnotations());
+
+ final List<Provider<?>> parameterProviders = findParameterProviders(errors, method);
+
+ for (Message message : errors.getMessages()) {
+ binder.addError(message);
+ }
+
+ // Define T as the method's return type.
+ @SuppressWarnings("unchecked") TypeLiteral<T> returnType
+ = (TypeLiteral<T>) TypeLiteral.get(typeResolver.getReturnType(method));
+
+ Provider<T> provider = new Provider<T>() {
+ public T get() {
+ Object[] parameters = new Object[parameterProviders.size()];
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = parameterProviders.get(i).get();
+ }
+
+ try {
+ // We know this cast is safe becase T is the method's return type.
+ @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
+ T result = (T) method.invoke(providers, parameters);
+ return result;
+ }
+ catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ AnnotatedBindingBuilder<T> builder = binder.bind(returnType);
+
+ if (bindingAnnotation != null) {
+ builder.annotatedWith(bindingAnnotation);
+ }
+
+ builder.toProvider(provider);
+
+ if (scopeAnnotation != null) {
+ builder.in(scopeAnnotation);
+ }
+ }
+
+ List<Provider<?>> findParameterProviders(Errors errors, Method method) {
+ List<Provider<?>> parameterProviders = Lists.newArrayList();
+
+ List<Type> parameterTypes = typeResolver.getParameterTypes(method);
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ Type parameterType = parameterTypes.get(i);
+ Annotation bindingAnnotation
+ = Annotations.findBindingAnnotation(errors, method, parameterAnnotations[i]);
+ Key<?> key = bindingAnnotation == null ? Key.get(parameterType)
+ : Key.get(parameterType, bindingAnnotation);
+ Provider<?> provider = binder.getProvider(key);
+ parameterProviders.add(provider);
+ }
+
+ return parameterProviders;
+ }
+
+ @Override public boolean equals(Object o) {
+ return o instanceof ProviderMethodsModule
+ && ((ProviderMethodsModule) o).providers == providers;
+ }
+
+ @Override public int hashCode() {
+ return providers.hashCode();
+ }
+ }
+}
diff --git a/src/com/google/inject/spi/StaticInjectionRequest.java b/src/com/google/inject/spi/StaticInjectionRequest.java
index acc5a5f..107cc1b 100644
--- a/src/com/google/inject/spi/StaticInjectionRequest.java
+++ b/src/com/google/inject/spi/StaticInjectionRequest.java
@@ -18,8 +18,6 @@
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
/**
* A request to inject the static fields and methods of type. Requests are created
@@ -32,19 +30,19 @@
*/
public final class StaticInjectionRequest implements Element {
private final Object source;
- private final List<Class> types;
+ private final Class<?> type;
- StaticInjectionRequest(Object source, Class[] types) {
+ StaticInjectionRequest(Object source, Class<?> type) {
this.source = checkNotNull(source, "source");
- this.types = ImmutableList.of(types);
+ this.type = checkNotNull(type, "type");
}
public Object getSource() {
return source;
}
- public List<Class> getTypes() {
- return types;
+ public Class<?> getType() {
+ return type;
}
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 1ca0b34..6c16fb7 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -24,8 +24,10 @@
import com.google.inject.internal.UniqueAnnotationsTest;
import com.google.inject.matcher.MatcherTest;
import com.google.inject.spi.ElementsTest;
+import com.google.inject.spi.InjectionPointTest;
import com.google.inject.spi.ModuleRewriterTest;
import com.google.inject.spi.ModuleWriterTest;
+import com.google.inject.spi.ProviderMethodsTest;
import com.google.inject.spi.SpiBindingsTest;
import com.google.inject.util.ProvidersTest;
import com.google.inject.util.TypesTest;
diff --git a/test/com/google/inject/Asserts.java b/test/com/google/inject/Asserts.java
index 2c06282..cb2ef58 100644
--- a/test/com/google/inject/Asserts.java
+++ b/test/com/google/inject/Asserts.java
@@ -66,6 +66,7 @@
*/
public static void assertEqualWhenReserialized(Object object)
throws IOException {
+ Assert.assertTrue("Expected serialVersionUID", hasSerialVersionUid(object));
Object reserialized = reserialize(object);
Assert.assertEquals(object, reserialized);
Assert.assertEquals(object.hashCode(), reserialized.hashCode());
@@ -75,10 +76,19 @@
* Fails unless {@code object} has the same toString value when reserialized.
*/
public static void assertSimilarWhenReserialized(Object object) throws IOException {
+ Assert.assertTrue("Expected serialVersionUID", hasSerialVersionUid(object));
Object reserialized = reserialize(object);
Assert.assertEquals(object.toString(), reserialized.toString());
}
+ static boolean hasSerialVersionUid(Object object) {
+ try {
+ return null != object.getClass().getDeclaredField("serialVersionUID");
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
+
static Object reserialize(Object object) throws IOException {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -91,6 +101,7 @@
}
public static void assertNotSerializable(Object object) throws IOException {
+ Assert.assertFalse("Unexpected serialVersionUID", hasSerialVersionUid(object));
try {
reserialize(object);
Assert.fail();
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 26b56b0..a3388e4 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -294,8 +294,14 @@
}
public void testDuplicateScopeAnnotations() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bindScope(CustomScoped.class, Scopes.NO_SCOPE);
+ }
+ });
+
try {
- Guice.createInjector().getInstance(SingletonAndCustomScoped.class);
+ injector.getInstance(SingletonAndCustomScoped.class);
fail();
} catch (ProvisionException expected) {
assertContains(expected.getMessage(),
diff --git a/test/com/google/inject/spi/ElementsTest.java b/test/com/google/inject/spi/ElementsTest.java
index 9cde48d..447cf6d 100644
--- a/test/com/google/inject/spi/ElementsTest.java
+++ b/test/com/google/inject/spi/ElementsTest.java
@@ -346,7 +346,8 @@
@Override public <T> Void visitBinding(Binding<T> command) {
assertEquals(Key.get(String.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
- public Void visitProvider(Provider<? extends T> provider) {
+ public Void visitProvider(Provider<? extends T> provider,
+ Set<InjectionPoint> injectionPoints) {
assertSame(aProvider, provider);
return null;
}
@@ -637,7 +638,14 @@
new FailingElementVisitor() {
@Override public Void visitInjectionRequest(InjectionRequest command) {
- assertEquals(Arrays.asList(firstObject, secondObject), command.getInstances());
+ assertEquals(firstObject, command.getInstance());
+ return null;
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public Void visitInjectionRequest(InjectionRequest command) {
+ assertEquals(secondObject, command.getInstance());
return null;
}
}
@@ -654,7 +662,7 @@
new FailingElementVisitor() {
@Override public Void visitStaticInjectionRequest(StaticInjectionRequest command) {
- assertEquals(Arrays.asList(ArrayList.class), command.getTypes());
+ assertEquals(ArrayList.class, command.getType());
return null;
}
}
diff --git a/test/com/google/inject/InjectionPointTest.java b/test/com/google/inject/spi/InjectionPointTest.java
similarity index 71%
rename from test/com/google/inject/InjectionPointTest.java
rename to test/com/google/inject/spi/InjectionPointTest.java
index d52753d..0b0562c 100644
--- a/test/com/google/inject/InjectionPointTest.java
+++ b/test/com/google/inject/spi/InjectionPointTest.java
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package com.google.inject;
+package com.google.inject.spi;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.inject.Asserts.assertEqualsBothWays;
import static com.google.inject.Asserts.assertSimilarWhenReserialized;
+import com.google.inject.Inject;
+import com.google.inject.Key;
import com.google.inject.internal.ErrorsException;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InjectionPoint;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import junit.framework.Assert;
import junit.framework.TestCase;
/**
@@ -45,61 +46,66 @@
public void testFieldInjectionPoint() throws NoSuchFieldException, IOException, ErrorsException {
Field fooField = getClass().getField("foo");
- InjectionPoint injectionPoint = InjectionPoint.get(fooField);
+ InjectionPoint injectionPoint = new InjectionPoint(fooField);
assertSame(fooField, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
- assertEquals("com.google.inject.InjectionPointTest.foo", injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, InjectionPoint.get(fooField));
+ assertEquals(getClass().getName() + ".foo", injectionPoint.toString());
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(fooField));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
- assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=a)]"
- + "@com.google.inject.InjectionPointTest.foo", dependency.toString());
+ assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=a)]@"
+ + getClass().getName() + ".foo", dependency.toString());
assertEquals(fooField, dependency.getInjectionPoint().getMember());
assertEquals(-1, dependency.getParameterIndex());
- assertEquals(Key.get(String.class, Names.named("a")), dependency.getKey());
+ Assert.assertEquals(Key.get(String.class, Names.named("a")), dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
+ assertEqualsBothWays(dependency,
+ getOnlyElement(new InjectionPoint(fooField).getDependencies()));
}
- public void testMethodInjectionPoint() throws NoSuchMethodException, IOException, ErrorsException {
+ public void testMethodInjectionPoint() throws Exception {
Method barMethod = getClass().getMethod("bar", String.class);
- InjectionPoint injectionPoint = InjectionPoint.get(barMethod);
+ InjectionPoint injectionPoint = new InjectionPoint(barMethod);
assertSame(barMethod, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
- assertEquals("com.google.inject.InjectionPointTest.bar()", injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, InjectionPoint.get(barMethod));
+ assertEquals(getClass().getName() + ".bar()", injectionPoint.toString());
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(barMethod));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
- assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=b)]"
- + "@com.google.inject.InjectionPointTest.bar()[0]", dependency.toString());
+ assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=b)]@"
+ + getClass().getName() + ".bar()[0]", dependency.toString());
assertEquals(barMethod, dependency.getInjectionPoint().getMember());
assertEquals(0, dependency.getParameterIndex());
assertEquals(Key.get(String.class, Names.named("b")), dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
+ assertEqualsBothWays(dependency,
+ getOnlyElement(new InjectionPoint(barMethod).getDependencies()));
}
public void testConstructorInjectionPoint() throws NoSuchMethodException, IOException,
ErrorsException {
Constructor<?> constructor = Constructable.class.getConstructor(String.class);
- InjectionPoint injectionPoint = InjectionPoint.get(constructor);
+ InjectionPoint injectionPoint = new InjectionPoint(constructor);
assertSame(constructor, injectionPoint.getMember());
assertFalse(injectionPoint.isOptional());
- assertEquals("com.google.inject.InjectionPointTest$Constructable.<init>()",
- injectionPoint.toString());
- assertEqualsBothWays(injectionPoint, InjectionPoint.get(constructor));
+ assertEquals(Constructable.class.getName() + ".<init>()", injectionPoint.toString());
+ assertEqualsBothWays(injectionPoint, new InjectionPoint(constructor));
assertSimilarWhenReserialized(injectionPoint);
Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
- assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=c)]"
- + "@com.google.inject.InjectionPointTest$Constructable.<init>()[0]", dependency.toString());
+ assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=c)]@"
+ + Constructable.class.getName() + ".<init>()[0]", dependency.toString());
assertEquals(constructor, dependency.getInjectionPoint().getMember());
assertEquals(0, dependency.getParameterIndex());
assertEquals(Key.get(String.class, Names.named("c")), dependency.getKey());
assertEquals(false, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
+ assertEqualsBothWays(dependency,
+ getOnlyElement(new InjectionPoint(constructor).getDependencies()));
}
public void testUnattachedDependency() throws IOException {
@@ -111,5 +117,6 @@
assertEquals(Key.get(String.class, Names.named("d")), dependency.getKey());
assertEquals(true, dependency.isNullable());
assertSimilarWhenReserialized(dependency);
+ assertEqualsBothWays(dependency, Dependency.get(Key.get(String.class, Names.named("d"))));
}
}
diff --git a/test/com/google/inject/spi/ProviderMethodsTest.java b/test/com/google/inject/spi/ProviderMethodsTest.java
new file mode 100644
index 0000000..dda600a
--- /dev/null
+++ b/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (C) 2007 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.spi;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Asserts;
+import com.google.inject.Binder;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.google.inject.util.Types;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Set;
+import junit.framework.TestCase;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class ProviderMethodsTest extends TestCase implements Module {
+
+ @SuppressWarnings("unchecked")
+ public void testProviderMethods() {
+ Injector injector = Guice.createInjector(this);
+
+ Bob bob = injector.getInstance(Bob.class);
+ assertEquals("A Bob", bob.getName());
+
+ Bob clone = injector.getInstance(Bob.class);
+ assertEquals("A Bob", clone.getName());
+
+ assertNotSame(bob, clone);
+ assertSame(bob.getDaughter(), clone.getDaughter());
+
+ Key soleBobKey = Key.get(Bob.class, Sole.class);
+ assertSame(
+ injector.getInstance(soleBobKey),
+ injector.getInstance(soleBobKey)
+ );
+ }
+
+ public void configure(Binder binder) {}
+
+ interface Bob {
+ String getName();
+ Dagny getDaughter();
+ }
+
+ interface Dagny {
+ int getAge();
+ }
+
+ @Provides
+ Bob provideBob(final Dagny dagny) {
+ return new Bob() {
+ public String getName() {
+ return "A Bob";
+ }
+
+ public Dagny getDaughter() {
+ return dagny;
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ @Sole
+ Bob provideSoleBob(final Dagny dagny) {
+ return new Bob() {
+ public String getName() {
+ return "Only Bob";
+ }
+
+ public Dagny getDaughter() {
+ return dagny;
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ Dagny provideDagny() {
+ return new Dagny() {
+ public int getAge() {
+ return 1;
+ }
+ };
+ }
+
+ @Retention(RUNTIME)
+ @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+ @BindingAnnotation
+ @interface Sole {}
+
+
+
+// We'll have to make getProvider() support circular dependencies before this
+// will work.
+//
+// public void testCircularDependency() {
+// Injector injector = Guice.createInjector(new Module() {
+// public void configure(Binder binder) {
+// binder.install(ProviderMethods.from(ProviderMethodsTest.this));
+// }
+// });
+//
+// Foo foo = injector.getInstance(Foo.class);
+// assertEquals(5, foo.getI());
+// assertEquals(10, foo.getBar().getI());
+// assertEquals(5, foo.getBar().getFoo().getI());
+// }
+//
+// interface Foo {
+// Bar getBar();
+// int getI();
+// }
+//
+// interface Bar {
+// Foo getFoo();
+// int getI();
+// }
+//
+// @Provides Foo newFoo(final Bar bar) {
+// return new Foo() {
+//
+// public Bar getBar() {
+// return bar;
+// }
+//
+// public int getI() {
+// return 5;
+// }
+// };
+// }
+//
+// @Provides Bar newBar(final Foo foo) {
+// return new Bar() {
+//
+// public Foo getFoo() {
+// return foo;
+// }
+//
+// public int getI() {
+// return 10;
+// }
+// };
+// }
+
+
+ public void testMultipleBindingAnnotations() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ protected void configure() {}
+
+ @Provides @Named("A") @Blue
+ public String provideString() {
+ return "a";
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ Asserts.assertContains(expected.getMessage(),
+ "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue",
+ "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:");
+ }
+
+ }
+
+ @Retention(RUNTIME)
+ @BindingAnnotation @interface Blue {}
+
+ public void testGenericProviderMethods() {
+ Injector injector = Guice.createInjector(
+ new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
+
+ assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
+ assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
+ assertEquals(ImmutableSet.of("A", "B"),
+ injector.getInstance(Key.get(Types.setOf(String.class))));
+
+ assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue());
+ assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue());
+ assertEquals(ImmutableSet.of(1, 2),
+ injector.getInstance(Key.get(Types.setOf(Integer.class))));
+ }
+
+ abstract class ProvideTs<T> extends AbstractModule {
+ final T first;
+ final T second;
+
+ protected ProvideTs(T first, T second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ protected void configure() {}
+
+ @Named("First") @Provides T provideFirst() {
+ return first;
+ }
+
+ @Named("Second") @Provides T provideSecond() {
+ return second;
+ }
+
+ @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) {
+ return ImmutableSet.of(first, second);
+ }
+ }
+
+ public void testAutomaticProviderMethods() {
+ Injector injector = Guice.createInjector((Module) new AbstractModule() {
+ protected void configure() { }
+ private int next = 1;
+
+ @Provides @Named("count")
+ public Integer provideCount() {
+ return next++;
+ }
+ });
+
+ assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+ assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+ assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+ }
+
+ /**
+ * If the user installs provider methods for the module manually, that shouldn't cause a double
+ * binding of the provider methods' types.
+ */
+ public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
+ Module installsSelf = new AbstractModule() {
+ protected void configure() {
+ install(this);
+ bind(Integer.class).toInstance(5);
+ }
+ @Provides public String provideString(Integer count) {
+ return "A" + count;
+ }
+ };
+
+ Injector injector = Guice.createInjector(installsSelf);
+ assertEquals("A5", injector.getInstance(String.class));
+ }
+
+ public void testWildcardProviderMethods() {
+ final List<String> strings = ImmutableList.of("A", "B", "C");
+ final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ @SuppressWarnings("unchecked")
+ Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
+ Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
+ bind(listOfSupertypesOfInteger).toInstance(numbers);
+ }
+ @Provides public List<? extends CharSequence> provideCharSequences() {
+ return strings;
+ }
+ @Provides public Class<?> provideType() {
+ return Float.class;
+ }
+ });
+
+ assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences);
+ assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers);
+ assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type);
+ }
+
+ static class HasWildcardInjection {
+ @Inject List<? extends CharSequence> charSequences;
+ @Inject List<? super Integer> numbers;
+ @Inject Class<?> type;
+ }
+}
\ No newline at end of file
diff --git a/test/com/google/inject/spi/SpiBindingsTest.java b/test/com/google/inject/spi/SpiBindingsTest.java
index 6628eea..5da78c3 100644
--- a/test/com/google/inject/spi/SpiBindingsTest.java
+++ b/test/com/google/inject/spi/SpiBindingsTest.java
@@ -38,6 +38,7 @@
import com.google.inject.name.Names;
import java.lang.reflect.Constructor;
import java.util.List;
+import java.util.Set;
import java.util.logging.Logger;
import junit.framework.TestCase;
@@ -76,7 +77,7 @@
assertContains(command.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(String.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
- @Override public Void visitInstance(T instance) {
+ @Override public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
assertEquals("A", instance);
return null;
}
@@ -102,7 +103,8 @@
assertContains(command.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(String.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
- @Override public Void visitProvider(Provider<? extends T> provider) {
+ @Override public Void visitProvider(Provider<? extends T> provider,
+ Set<InjectionPoint> injectionPoints) {
assertSame(stringProvider, provider);
return null;
}
@@ -185,7 +187,8 @@
assertContains(command.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(D.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
- @Override public Void visitConstructor(Constructor<? extends T> constructor) {
+ @Override public Void visitConstructor(Constructor<? extends T> constructor,
+ Set<InjectionPoint> injectionPoints) {
Constructor<?> expected = D.class.getDeclaredConstructors()[0];
assertEquals(expected, constructor);
return null;
@@ -210,7 +213,7 @@
assertContains(command.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(Integer.class, Names.named("one")), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
- @Override public Void visitInstance(T instance) {
+ @Override public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
assertEquals((Integer) 1, instance);
return null;
}