Author: limpbizkit
Date: Sat Mar 28 11:25:35 2009
New Revision: 913
Added:
trunk/src/com/google/inject/LookupProcessor.java
- copied, changed from r906,
/trunk/src/com/google/inject/ProviderLookupProcessor.java
trunk/src/com/google/inject/spi/InjectableType.java
trunk/test/com/google/inject/MembersInjectorTest.java
Removed:
trunk/src/com/google/inject/ProviderLookupProcessor.java
Modified:
trunk/src/com/google/inject/ConstructorInjectorStore.java
trunk/src/com/google/inject/InjectorBuilder.java
trunk/src/com/google/inject/InjectorImpl.java
trunk/src/com/google/inject/MembersInjector.java
trunk/src/com/google/inject/internal/Errors.java
trunk/src/com/google/inject/spi/Elements.java
trunk/src/com/google/inject/spi/MembersInjectorLookup.java
trunk/src/com/google/inject/spi/ModuleWriter.java
trunk/src/com/google/inject/spi/ProviderLookup.java
trunk/test/com/google/inject/AllTests.java
trunk/test/com/google/inject/InjectableTypeListenerTest.java
trunk/test/com/google/inject/spi/ElementsTest.java
Log:
Injector.getMembersInjector() and Binder.getMembersInjector() APIs both
implemented and tested.
Still need to make it possible to inject a MembersInjector<T> anywhere,
just like how we already support injecting a Provider<T> anywhere.
Modified: trunk/src/com/google/inject/ConstructorInjectorStore.java
==============================================================================
--- trunk/src/com/google/inject/ConstructorInjectorStore.java (original)
+++ trunk/src/com/google/inject/ConstructorInjectorStore.java Sat Mar 28
11:25:35 2009
@@ -32,8 +32,6 @@
import com.google.inject.spi.Message;
import java.lang.reflect.Method;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
/**
@@ -86,7 +84,7 @@
ProxyFactory<T> proxyFactory = new ProxyFactory<T>(injectionPoint,
injector.methodAspects);
EncounterImpl<T> encounter = new EncounterImpl<T>();
- InjectableType<T> injectableType = new InjectableTypeImpl<T>(
+ InjectableType<T> injectableType = new InjectableType<T>(
injectionPoint, type, injectableMembers,
proxyFactory.getInterceptors());
for (InjectableTypeListenerBinding typeListener :
injectableTypeListenerBindings) {
@@ -103,7 +101,7 @@
if (encounter.hasAddedAspects()) {
proxyFactory = new ProxyFactory<T>(
injectionPoint, concat(injector.methodAspects,
encounter.aspects));
- injectableType = new InjectableTypeImpl<T>(
+ injectableType = new InjectableType<T>(
injectionPoint, type, injectableMembers,
proxyFactory.getInterceptors());
}
@@ -174,41 +172,4 @@
}
}
- static class InjectableTypeImpl<T> implements InjectableType<T> {
- private final InjectionPoint injectionPoint;
- private final TypeLiteral<T> type;
- private final Set<InjectionPoint> injectableMembers;
- private final Map<Method, List<MethodInterceptor>> methodInterceptors;
-
- InjectableTypeImpl(InjectionPoint injectionPoint, TypeLiteral<T> type,
- Set<InjectionPoint> injectableMembers,
- Map<Method, List<MethodInterceptor>> methodInterceptors) {
- this.injectionPoint = injectionPoint;
- this.type = type;
- this.injectableMembers = injectableMembers;
- this.methodInterceptors = methodInterceptors;
- }
-
- public TypeLiteral<T> getType() {
- return type;
- }
-
- public InjectionPoint getInjectableConstructor() {
- return injectionPoint;
- }
-
- public Set<InjectionPoint> getInjectableMembers() throws
ConfigurationException {
- return injectableMembers;
- }
-
- /*if[AOP]*/
- public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
- return methodInterceptors;
- }
- /*end[AOP]*/
-
- @Override public String toString() {
- return type.toString();
- }
- }
}
Modified: trunk/src/com/google/inject/InjectorBuilder.java
==============================================================================
--- trunk/src/com/google/inject/InjectorBuilder.java (original)
+++ trunk/src/com/google/inject/InjectorBuilder.java Sat Mar 28 11:25:35
2009
@@ -137,7 +137,7 @@
initializer.validateOustandingInjections(errors);
stopwatch.resetAndLog("Instance member validation");
- new ProviderLookupProcessor(errors).process(shells);
+ new LookupProcessor(errors).process(shells);
stopwatch.resetAndLog("Provider verification");
for (InjectorShell shell : shells) {
Modified: trunk/src/com/google/inject/InjectorImpl.java
==============================================================================
--- trunk/src/com/google/inject/InjectorImpl.java (original)
+++ trunk/src/com/google/inject/InjectorImpl.java Sat Mar 28 11:25:35 2009
@@ -726,54 +726,36 @@
/** Cached constructor injectors for each type */
FailableCache<TypeLiteral<?>, ConstructorInjector<?>> constructors;
- void injectMembers(Errors errors, Object o, InternalContext context,
- List<SingleMemberInjector> injectors)
- throws ErrorsException {
- for (SingleMemberInjector injector : injectors) {
- injector.inject(errors, context, o);
- }
+ @SuppressWarnings("unchecked") // the members injector type is
consistent with instance's type
+ public void injectMembers(Object instance) {
+ MembersInjector membersInjector =
getMembersInjector(instance.getClass());
+ membersInjector.injectMembers(instance);
}
- // Not test-covered
- public void injectMembers(final Object o) {
- Errors errors = new Errors(o.getClass());
-
- // configuration/validation stuff throws ConfigurationException
- List<SingleMemberInjector> injectors;
+ public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>
typeLiteral) {
+ Errors errors = new Errors(typeLiteral);
try {
- injectors = this.injectors.get(TypeLiteral.get(o.getClass()),
errors);
+ return getMembersInjectorOrThrow(typeLiteral, errors);
} catch (ErrorsException e) {
throw new
ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
-
- // injection can throw ProvisionException
- try {
- injectMembersOrThrow(errors, o, injectors);
- } catch (ErrorsException e) {
- errors.merge(e.getErrors());
- }
-
- errors.throwProvisionExceptionIfErrorsExist();
- }
-
- public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>
typeLiteral) {
- throw new UnsupportedOperationException("TODO");
}
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
- throw new UnsupportedOperationException("TODO");
+ return getMembersInjector(TypeLiteral.get(type));
}
- public void injectMembersOrThrow(final Errors errors, final Object o,
- final List<SingleMemberInjector> injectors)
- throws ErrorsException {
- if (o == null) {
+ public void injectMembersOrThrow(final Errors errors, final Object
instance,
+ final List<SingleMemberInjector> injectors) throws ErrorsException {
+ if (instance == null) {
return;
}
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) throws ErrorsException {
- injectMembers(errors, o, context, injectors);
+ for (SingleMemberInjector injector : injectors) {
+ injector.inject(errors, context, instance);
+ }
return null;
}
});
@@ -810,6 +792,23 @@
@Override public String toString() {
return factory.toString();
+ }
+ };
+ }
+
+ <T> MembersInjector<T> getMembersInjectorOrThrow(TypeLiteral<T> type,
final Errors errors)
+ throws ErrorsException {
+ final ImmutableList<SingleMemberInjector> memberInjectors =
injectors.get(type, errors);
+
+ return new MembersInjector<T>() {
+ public void injectMembers(T instance) {
+ try {
+ injectMembersOrThrow(errors, instance, memberInjectors);
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+
+ errors.throwProvisionExceptionIfErrorsExist();
}
};
}
Copied: trunk/src/com/google/inject/LookupProcessor.java (from r906,
/trunk/src/com/google/inject/ProviderLookupProcessor.java)
==============================================================================
--- /trunk/src/com/google/inject/ProviderLookupProcessor.java (original)
+++ trunk/src/com/google/inject/LookupProcessor.java Sat Mar 28 11:25:35
2009
@@ -18,25 +18,38 @@
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.MembersInjectorLookup;
import com.google.inject.spi.ProviderLookup;
/**
- * Handles {...@link Binder#getProvider} commands.
+ * Handles {...@link Binder#getProvider} and {...@link
Binder#getMembersInjector(TypeLiteral)} commands.
*
* @author [email protected] (Bob Lee)
* @author [email protected] (Jesse Wilson)
*/
-class ProviderLookupProcessor extends AbstractProcessor {
+class LookupProcessor extends AbstractProcessor {
- ProviderLookupProcessor(Errors errors) {
+ LookupProcessor(Errors errors) {
super(errors);
}
- @Override public <T> Boolean visit(ProviderLookup<T> command) {
+ @Override public <T> Boolean visit(MembersInjectorLookup<T> lookup) {
+ try {
+ MembersInjector<T> membersInjector
+ = injector.getMembersInjectorOrThrow(lookup.getTypeLiteral(),
errors);
+ lookup.initializeDelegate(membersInjector);
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors()); // TODO: source
+ }
+
+ return true;
+ }
+
+ @Override public <T> Boolean visit(ProviderLookup<T> lookup) {
// ensure the provider can be created
try {
- Provider<T> provider = injector.getProviderOrThrow(command.getKey(),
errors);
- command.initializeDelegate(provider);
+ Provider<T> provider = injector.getProviderOrThrow(lookup.getKey(),
errors);
+ lookup.initializeDelegate(provider);
} catch (ErrorsException e) {
errors.merge(e.getErrors()); // TODO: source
}
Modified: trunk/src/com/google/inject/MembersInjector.java
==============================================================================
--- trunk/src/com/google/inject/MembersInjector.java (original)
+++ trunk/src/com/google/inject/MembersInjector.java Sat Mar 28 11:25:35
2009
@@ -36,7 +36,7 @@
* performing constructor injection), so if you're able to let Guice
create all your objects for
* you, you'll never need to use this method.
*
- * @param instance to inject members on
+ * @param instance to inject members on. May be {...@code null}.
*/
void injectMembers(T instance);
}
Modified: trunk/src/com/google/inject/internal/Errors.java
==============================================================================
--- trunk/src/com/google/inject/internal/Errors.java (original)
+++ trunk/src/com/google/inject/internal/Errors.java Sat Mar 28 11:25:35
2009
@@ -578,6 +578,9 @@
} else if (source instanceof Member) {
formatter.format(" at %s%n", StackTraceElements.forMember((Member)
source));
+ } else if (source instanceof TypeLiteral) {
+ formatter.format(" while locating %s%n", source);
+
} else if (source instanceof Key) {
Key<?> key = (Key<?>) source;
formatter.format(" while locating %s%n", convert(key));
Modified: trunk/src/com/google/inject/spi/Elements.java
==============================================================================
--- trunk/src/com/google/inject/spi/Elements.java (original)
+++ trunk/src/com/google/inject/spi/Elements.java Sat Mar 28 11:25:35 2009
@@ -20,6 +20,7 @@
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Key;
+import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.PrivateModule;
@@ -27,7 +28,6 @@
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
-import com.google.inject.MembersInjector;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.AnnotatedElementBuilder;
@@ -178,12 +178,26 @@
elements.add(new InjectionRequest<T>(getSource(), type, instance));
}
- public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T>
typeLiteral) {
- throw new UnsupportedOperationException("TODO");
+ public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T>
typeLiteral) {
+ final MembersInjectorLookup<T> element
+ = new MembersInjectorLookup<T>(getSource(), typeLiteral);
+ elements.add(element);
+ return new MembersInjector<T>() {
+ public void injectMembers(T instance) {
+ MembersInjector<T> delegate = element.getDelegate();
+ checkState(delegate != null,
+ "This MembersInjector cannot be used until the Injector has
been created.");
+ delegate.injectMembers(instance);
+ }
+
+ @Override public String toString() {
+ return "MembersInjector<" + typeLiteral + ">";
+ }
+ };
}
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
- throw new UnsupportedOperationException("TODO");
+ return getMembersInjector(TypeLiteral.get(type));
}
public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
@@ -252,13 +266,13 @@
}
public <T> Provider<T> getProvider(final Key<T> key) {
- final ProviderLookup<T> command = new ProviderLookup<T>(getSource(),
key);
- elements.add(command);
+ final ProviderLookup<T> element = new ProviderLookup<T>(getSource(),
key);
+ elements.add(element);
return new Provider<T>() {
public T get() {
- Provider<T> delegate = command.getDelegate();
+ Provider<T> delegate = element.getDelegate();
checkState(delegate != null,
- "This provider cannot be used until the Injector has been
created.");
+ "This Provider cannot be used until the Injector has been
created.");
return delegate.get();
}
Added: trunk/src/com/google/inject/spi/InjectableType.java
==============================================================================
--- (empty file)
+++ trunk/src/com/google/inject/spi/InjectableType.java Sat Mar 28 11:25:35
2009
@@ -0,0 +1,222 @@
+/**
+ * Copyright (C) 2009 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.inject.Key;
+import com.google.inject.MembersInjector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.ImmutableMap;
+import com.google.inject.matcher.Matcher;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.aopalliance.intercept.MethodInterceptor;
+
+/**
+ * Represents an injectable type. A type is injectable if:
+ *
+ * <ol type="a">
+ * <li>Guice can inject its constructor, or</li>
+ * <li>Guice can inject the methods and fields of a pre-existing
instance of the type</li>
+ * </ol>
+ *
+ * <p>Note: Despite generic type erasure, Guice keeps track of full types,
so it can and does treat
+ * {...@code Foo<String>} and {...@code Foo<Integer>} as distinct injectable
types.
+ *
+ * @author [email protected] (Bob Lee)
+ * @author [email protected] (Jesse Wilson)
+ * @since 2.0
+ */
+public final class InjectableType<T> {
+ private final InjectionPoint injectableConstructor;
+ private final TypeLiteral<T> type;
+ private final Set<InjectionPoint> injectableMembers;
+ private final ImmutableMap<Method, List<MethodInterceptor>>
methodInterceptors;
+
+ public InjectableType(InjectionPoint injectableConstructor,
TypeLiteral<T> type,
+ Set<InjectionPoint> injectableMembers,
+ Map<Method, List<MethodInterceptor>> methodInterceptors) {
+ this.injectableConstructor = injectableConstructor;
+ this.type = type;
+ this.injectableMembers = injectableMembers;
+ this.methodInterceptors = ImmutableMap.copyOf(methodInterceptors);
+ }
+
+ /**
+ * Returns the type literal.
+ */
+ public TypeLiteral<T> getType() {
+ return type;
+ }
+
+ /**
+ * Returns the injection point representing the contructor or {...@code
null} if no injectable
+ * constructor is present
+ */
+ public InjectionPoint getInjectableConstructor() {
+ return injectableConstructor;
+ }
+
+ /**
+ * Returns the instance methods and fields of {...@code T} that can be
injected.
+ *
+ * @return a possibly empty set of injection points. The set has a
specified iteration order.
+ * All fields are returned and then all methods. Within the fields,
supertype fields are
+ * returned before subtype fields. Similarly, supertype methods are
returned before subtype
+ * methods.
+ */
+ public Set<InjectionPoint> getInjectableMembers() {
+ return injectableMembers;
+ }
+
+ /*if[AOP]*/
+ /**
+ * Returns the interceptors applied to each method, in the order that
they will be applied.
+ * These only apply when Guice instantiates {...@code I}.
+ *
+ * @return a possibly empty map
+ */
+ public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
+ return methodInterceptors;
+ }
+ /*end[AOP]*/
+
+ @Override public String toString() {
+ return type.toString();
+ }
+
+ /**
+ * Listens for Guice to encounter injectable types. If a given type has
its constructor injected
+ * in one situation but only its methods and fields injected in another,
Guice will notify
+ * this listener once.
+ *
+ * <p>Useful for extra type checking, {...@linkplain
Encounter#register(InjectionListener)
+ * registering injection listeners}, and {...@linkplain
Encounter#bindInterceptor(
+ * com.google.inject.matcher.Matcher,
org.aopalliance.intercept.MethodInterceptor[])
+ * binding method interceptors}.
+ */
+ public interface Listener {
+
+ /**
+ * Invoked when Guice encounters a new type eligible for constructor
or members injection.
+ * Called during injector creation (or afterwords if Guice encounters
a type at run time and
+ * creates a JIT binding).
+ *
+ * @param injectableType encountered by Guice
+ * @param encounter context of this encounter, enables reporting
errors, registering injection
+ * listeners and binding method interceptors for injectableType
+ *
+ * @param <I> the injectable type
+ */
+ <I> void hear(InjectableType<I> injectableType, Encounter<I>
encounter);
+
+ }
+
+ /**
+ * Context of the injectable type encounter. Enables reporting errors,
registering injection
+ * listeners and binding method interceptors for injectable type {...@code
I}.
+ *
+ * @param <I> the injectable type encountered
+ */
+ public interface Encounter<I> {
+
+ /**
+ * Records an error message for type {...@code I} which will be presented
to the user at a later
+ * time. Unlike throwing an exception, this enable us to continue
configuring the Injector and
+ * discover more errors. Uses {...@link String#format(String, Object[])}
to insert the arguments
+ * into the message.
+ */
+ void addError(String message, Object... arguments);
+
+ /**
+ * Records an exception for type {...@code I}, the full details of which
will be logged, and the
+ * message of which will be presented to the user at a later time. If
your
+ * InjectableTypeListener calls something that you worry may fail, you
should catch the
+ * exception and pass it to this method.
+ */
+ void addError(Throwable t);
+
+ /**
+ * Records an error message to be presented to the user at a later
time.
+ */
+ void addError(Message message);
+
+ /**
+ * Returns the provider used to obtain instances for the given
injection key. The returned
+ * provider will not be valid until the injector has been created. The
provider will throw an
+ * {...@code IllegalStateException} if you try to use it beforehand.
+ */
+ <T> Provider<T> getProvider(Key<T> key);
+
+ /**
+ * Returns the provider used to obtain instances for the given
injection type. The returned
+ * provider will not be valid until the injetor has been created. The
provider will throw an
+ * {...@code IllegalStateException} if you try to use it beforehand.
+ */
+ <T> Provider<T> getProvider(Class<T> type);
+
+ /**
+ * Returns the members injector used to inject dependencies into
methods and fields on instances
+ * of the given type {...@code T}. The returned members injector will not
be valid until the main
+ * injector has been created. The members injector will throw an
{...@code IllegalStateException}
+ * if you try to use it beforehand.
+ *
+ * @param typeLiteral type to get members injector for
+ */
+ <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
+
+ /**
+ * Returns the members injector used to inject dependencies into
methods and fields on instances
+ * of the given type {...@code T}. The returned members injector will not
be valid until the main
+ * injector has been created. The members injector will throw an
{...@code IllegalStateException}
+ * if you try to use it beforehand.
+ *
+ * @param type type to get members injector for
+ */
+ <T> MembersInjector<T> getMembersInjector(Class<T> type);
+
+ /**
+ * Registers an injection listener for type {...@code I}. Guice will
notify the listener after
+ * injecting an instance of {...@code I}. The order in which Guice will
invoke listeners is
+ * unspecified.
+ *
+ * @param listener for injections into instances of type {...@code I}
+ */
+ void register(InjectionListener<? super I> listener);
+
+ /*if[AOP]*/
+ /**
+ * Binds method interceptor[s] to methods matched in type {...@code I}
and its supertypes. A
+ * method is eligible for interception if:
+ *
+ * <ul>
+ * <li>Guice created the instance the method is on</li>
+ * <li>Neither the enclosing type nor the method is final</li>
+ * <li>And the method is package-private or more accessible</li>
+ * </ul>
+ *
+ * @param methodMatcher matches methods the interceptor should apply
to. For
+ * example: {...@code annotatedWith(Transactional.class)}.
+ * @param interceptors to bind
+ */
+ void bindInterceptor(Matcher<? super Method> methodMatcher,
+ org.aopalliance.intercept.MethodInterceptor... interceptors);
+ /*end[AOP]*/
+ }
+}
Modified: trunk/src/com/google/inject/spi/MembersInjectorLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/MembersInjectorLookup.java (original)
+++ trunk/src/com/google/inject/spi/MembersInjectorLookup.java Sat Mar 28
11:25:35 2009
@@ -16,8 +16,9 @@
package com.google.inject.spi;
-import com.google.inject.MembersInjector;
import com.google.inject.Binder;
+import com.google.inject.MembersInjector;
+import com.google.inject.TypeLiteral;
import static com.google.inject.internal.Preconditions.checkNotNull;
import static com.google.inject.internal.Preconditions.checkState;
@@ -34,12 +35,12 @@
public final class MembersInjectorLookup<T> implements Element {
private final Object source;
- private final InjectableType<T> injectableType;
+ private final TypeLiteral<T> typeLiteral;
private MembersInjector<T> delegate;
- MembersInjectorLookup(Object source, InjectableType<T> injectableType) {
+ MembersInjectorLookup(Object source, TypeLiteral<T> typeLiteral) {
this.source = checkNotNull(source, "source");
- this.injectableType = checkNotNull(injectableType, "injectableType");
+ this.typeLiteral = checkNotNull(typeLiteral, "typeLiteral");
}
public Object getSource() {
@@ -47,10 +48,10 @@
}
/**
- * Gets the injectable type containing the members to be injected.
+ * Gets the type containing the members to be injected.
*/
- public InjectableType<T> getInjectableType() {
- return injectableType;
+ public TypeLiteral<T> getTypeLiteral() {
+ return typeLiteral;
}
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
@@ -60,18 +61,15 @@
/**
* Sets the actual members injector.
*
- * @param delegate members injector
* @throws IllegalStateException if the delegate is already set
- * @throws NullPointerException if the delegate is null
*/
public void initializeDelegate(MembersInjector<T> delegate) {
checkState(this.delegate == null, "delegate already initialized");
- checkNotNull(delegate, "delegate");
- this.delegate = delegate;
+ this.delegate = checkNotNull(delegate, "delegate");
}
public void applyTo(Binder binder) {
-
binder.withSource(getSource()).getMembersInjector(injectableType.getType());
+ binder.withSource(getSource()).getMembersInjector(typeLiteral);
}
/**
Modified: trunk/src/com/google/inject/spi/ModuleWriter.java
==============================================================================
--- trunk/src/com/google/inject/spi/ModuleWriter.java (original)
+++ trunk/src/com/google/inject/spi/ModuleWriter.java Sat Mar 28 11:25:35
2009
@@ -19,6 +19,7 @@
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Key;
+import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
@@ -103,8 +104,9 @@
return null;
}
- public <T> Void visit(MembersInjectorLookup<T> lookup) {
- throw new UnsupportedOperationException("TODO");
+ public <T> Void visit(MembersInjectorLookup<T> element) {
+ writeGetMembersInjector(binder, element);
+ return null;
}
public Void visit(InjectableTypeListenerBinding binding) {
@@ -263,5 +265,11 @@
protected <T> void writeGetProvider(Binder binder, ProviderLookup<T>
element) {
Provider<T> provider =
binder.withSource(element.getSource()).getProvider(element.getKey());
element.initializeDelegate(provider);
+ }
+
+ protected <T> void writeGetMembersInjector(Binder binder,
MembersInjectorLookup<T> element) {
+ MembersInjector<T> membersInjector
+ =
binder.withSource(element.getSource()).getMembersInjector(element.getTypeLiteral());
+ element.initializeDelegate(membersInjector);
}
}
Modified: trunk/src/com/google/inject/spi/ProviderLookup.java
==============================================================================
--- trunk/src/com/google/inject/spi/ProviderLookup.java (original)
+++ trunk/src/com/google/inject/spi/ProviderLookup.java Sat Mar 28 11:25:35
2009
@@ -57,14 +57,11 @@
/**
* Sets the actual provider.
*
- * @param delegate provider
* @throws IllegalStateException if the delegate is already set
- * @throws NullPointerException if the delegate is null
*/
public void initializeDelegate(Provider<T> delegate) {
checkState(this.delegate == null, "delegate already initialized");
- checkNotNull(delegate, "delegate");
- this.delegate = delegate;
+ this.delegate = checkNotNull(delegate, "delegate");
}
public void applyTo(Binder binder) {
Modified: trunk/test/com/google/inject/AllTests.java
==============================================================================
--- trunk/test/com/google/inject/AllTests.java (original)
+++ trunk/test/com/google/inject/AllTests.java Sat Mar 28 11:25:35 2009
@@ -62,6 +62,7 @@
suite.addTestSuite(KeyTest.class);
suite.addTestSuite(LoggerInjectionTest.class);
// MethodInterceptionTest is AOP-only
+ suite.addTestSuite(MembersInjectorTest.class);
suite.addTestSuite(ModulesTest.class);
suite.addTestSuite(ModuleTest.class);
suite.addTestSuite(NullableInjectionPointTest.class);
Modified: trunk/test/com/google/inject/InjectableTypeListenerTest.java
==============================================================================
--- trunk/test/com/google/inject/InjectableTypeListenerTest.java
(original)
+++ trunk/test/com/google/inject/InjectableTypeListenerTest.java Sat Mar
28
11:25:35 2009
@@ -47,6 +47,30 @@
};
}
+ final InjectableType.Listener failingInjectableTypeListener = new
InjectableType.Listener() {
+ int failures = 0;
+
+ public <I> void hear(InjectableType<I> injectableType, Encounter<I>
encounter) {
+ throw new ClassCastException("whoops, failure #" + (++failures));
+ }
+
+ @Override public String toString() {
+ return "clumsy";
+ }
+ };
+
+ final InjectionListener<Object> failingInjectionListener = new
InjectionListener<Object>() {
+ int failures = 0;
+
+ public void afterInjection(Object injectee) {
+ throw new ClassCastException("whoops, failure #" + (++failures));
+ }
+
+ @Override public String toString() {
+ return "goofy";
+ }
+ };
+
public void testTypeListenersAreFired() throws NoSuchMethodException {
final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
final AtomicInteger firedCount = new AtomicInteger();
@@ -133,22 +157,10 @@
}
public void testInjectableTypeListenerThrows() {
- final InjectableType.Listener clumsyListener = new
InjectableType.Listener() {
- int failures = 0;
-
- public <I> void hear(InjectableType<I> injectableType, Encounter<I>
encounter) {
- throw new ClassCastException("whoops, failure #" + (++failures));
- }
-
- @Override public String toString() {
- return "clumsy";
- }
- };
-
try {
Guice.createInjector(new AbstractModule() {
protected void configure() {
- bindListener(any(), clumsyListener);
+ bindListener(any(), failingInjectableTypeListener);
bind(B.class);
bind(C.class);
}
@@ -168,7 +180,7 @@
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
- bindListener(any(), clumsyListener);
+ bindListener(any(), failingInjectableTypeListener);
}
});
try {
@@ -199,23 +211,11 @@
}
public void testInjectionListenerThrows() {
- final InjectionListener<Object> injectionListener = new
InjectionListener<Object>() {
- int failures = 0;
-
- public void afterInjection(Object injectee) {
- throw new ClassCastException("whoops, failure #" + (++failures));
- }
-
- @Override public String toString() {
- return "goofy";
- }
- };
-
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindListener(any(), new InjectableType.Listener() {
public <I> void hear(InjectableType<I> injectableType,
Encounter<I> encounter) {
- encounter.register(injectionListener);
+ encounter.register(failingInjectionListener);
}
});
bind(B.class);
@@ -254,6 +254,20 @@
// non-constructed types do not participate
assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
+ }
+
+ public void testInjectMembersInjectableTypeListener() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ getMembersInjector(A.class);
+ bindListener(any(), failingInjectableTypeListener);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ assertContains(expected.getMessage(), "foo");
+ }
}
static class A {
Added: trunk/test/com/google/inject/MembersInjectorTest.java
==============================================================================
--- (empty file)
+++ trunk/test/com/google/inject/MembersInjectorTest.java Sat Mar 28
11:25:35 2009
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2009 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;
+
+import static com.google.inject.Asserts.assertContains;
+import java.util.concurrent.atomic.AtomicReference;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * @author [email protected] (Jesse Wilson)
+ */
+public class MembersInjectorTest extends TestCase {
+
+ private static final A<C> uninjectableA = new A<C>() {
+ @Override void doNothing() {
+ throw new AssertionFailedError();
+ }
+ };
+
+ private static final B uninjectableB = new B() {
+ @Override void doNothing() {
+ throw new AssertionFailedError();
+ }
+ };
+
+ private static final C myFavouriteC = new C();
+
+ public void testMembersInjectorFromBinder() {
+ final AtomicReference<MembersInjector<A<C>>> aMembersInjectorReference
+ = new AtomicReference<MembersInjector<A<C>>>();
+ final AtomicReference<MembersInjector<B>> bMembersInjectorReference
+ = new AtomicReference<MembersInjector<B>>();
+
+ Guice.createInjector(new AbstractModule() {
+ @Override protected void configure() {
+ MembersInjector<A<C>> aMembersInjector = getMembersInjector(new
TypeLiteral<A<C>>() {});
+ try {
+ aMembersInjector.injectMembers(uninjectableA);
+ fail();
+ } catch (IllegalStateException expected) {
+ assertContains(expected.getMessage(),
+ "This MembersInjector cannot be used until the Injector has
been created.");
+ }
+
+ MembersInjector<B> bMembersInjector = getMembersInjector(B.class);
+ try {
+ bMembersInjector.injectMembers(uninjectableB);
+ fail();
+ } catch (IllegalStateException expected) {
+ assertContains(expected.getMessage(),
+ "This MembersInjector cannot be used until the Injector has
been created.");
+ }
+
+ aMembersInjectorReference.set(aMembersInjector);
+ bMembersInjectorReference.set(bMembersInjector);
+
+ bind(C.class).toInstance(myFavouriteC);
+ }
+ });
+
+ A<C> injectableA = new A<C>();
+ aMembersInjectorReference.get().injectMembers(injectableA);
+ assertSame(myFavouriteC, injectableA.t);
+ assertSame(myFavouriteC, injectableA.b.c);
+
+ B injectableB = new B();
+ bMembersInjectorReference.get().injectMembers(injectableB);
+ assertSame(myFavouriteC, injectableB.c);
+
+ B anotherInjectableB = new B();
+ bMembersInjectorReference.get().injectMembers(anotherInjectableB);
+ assertSame(myFavouriteC, anotherInjectableB.c);
+ }
+
+ public void testMembersInjectorFromInjector() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(C.class).toInstance(myFavouriteC);
+ }
+ });
+
+ MembersInjector<A<C>> aMembersInjector
+ = injector.getMembersInjector(new TypeLiteral<A<C>>() {});
+ MembersInjector<B> bMembersInjector =
injector.getMembersInjector(B.class);
+
+ A<C> injectableA = new A<C>();
+ aMembersInjector.injectMembers(injectableA);
+ assertSame(myFavouriteC, injectableA.t);
+ assertSame(myFavouriteC, injectableA.b.c);
+
+ B injectableB = new B();
+ bMembersInjector.injectMembers(injectableB);
+ assertSame(myFavouriteC, injectableB.c);
+
+ B anotherInjectableB = new B();
+ bMembersInjector.injectMembers(anotherInjectableB);
+ assertSame(myFavouriteC, anotherInjectableB.c);
+ }
+
+ public void testMembersInjectorWithNonInjectedTypes() {
+ Injector injector = Guice.createInjector();
+
+ MembersInjector<NoInjectedMembers> membersInjector
+ = injector.getMembersInjector(NoInjectedMembers.class);
+
+ membersInjector.injectMembers(new NoInjectedMembers());
+ membersInjector.injectMembers(new NoInjectedMembers());
+ }
+
+ public void testInjectionFailure() {
+ Injector injector = Guice.createInjector();
+
+ MembersInjector<InjectionFailure> membersInjector
+ = injector.getMembersInjector(InjectionFailure.class);
+
+ try {
+ membersInjector.injectMembers(new InjectionFailure());
+ fail();
+ } catch (ProvisionException expected) {
+ assertContains(expected.getMessage(),
+ "1) Error injecting method, java.lang.ClassCastException:
whoops, failure #1");
+ }
+ }
+
+ public void testInjectionAppliesToSpecifiedType() {
+ Injector injector = Guice.createInjector();
+
+ MembersInjector<Object> membersInjector =
injector.getMembersInjector(Object.class);
+ membersInjector.injectMembers(new InjectionFailure());
+ }
+
+ static class A<T> {
+ @Inject B b;
+ @Inject T t;
+ @Inject void doNothing() {}
+ }
+
+ static class B {
+ @Inject C c;
+ @Inject void doNothing() {}
+ }
+
+ static class C {}
+
+ static class NoInjectedMembers {}
+
+ static class InjectionFailure {
+ int failures = 0;
+
+ @Inject void fail() {
+ throw new ClassCastException("whoops, failure #" + (++failures));
+ }
+ }
+}
Modified: trunk/test/com/google/inject/spi/ElementsTest.java
==============================================================================
--- trunk/test/com/google/inject/spi/ElementsTest.java (original)
+++ trunk/test/com/google/inject/spi/ElementsTest.java Sat Mar 28 11:25:35
2009
@@ -20,13 +20,16 @@
import static com.google.inject.Asserts.assertContains;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
+import com.google.inject.Inject;
import com.google.inject.Key;
+import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
+import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
@@ -613,11 +616,12 @@
checkModule(
new AbstractModule() {
protected void configure() {
- Provider<String> keyGetProvider =
getProvider(Key.get(String.class, SampleAnnotation.class));
+ Provider<String> keyGetProvider
+ = getProvider(Key.get(String.class,
SampleAnnotation.class));
try {
keyGetProvider.get();
} catch (IllegalStateException e) {
- assertEquals("This provider cannot be used until the
Injector has been created.",
+ assertEquals("This Provider cannot be used until the
Injector has been created.",
e.getMessage());
}
@@ -625,14 +629,14 @@
try {
typeGetProvider.get();
} catch (IllegalStateException e) {
- assertEquals("This provider cannot be used until the
Injector has been created.",
+ assertEquals("This Provider cannot be used until the
Injector has been created.",
e.getMessage());
}
}
},
new FailingElementVisitor() {
- @Override public Void visit(ProviderLookup command) {
+ @Override public <T> Void visit(ProviderLookup<T> command) {
assertEquals(Key.get(String.class, SampleAnnotation.class),
command.getKey());
assertNull(command.getDelegate());
return null;
@@ -640,7 +644,7 @@
},
new FailingElementVisitor() {
- @Override public Void visit(ProviderLookup command) {
+ @Override public <T> Void visit(ProviderLookup<T> command) {
assertEquals(Key.get(String.class), command.getKey());
assertNull(command.getDelegate());
return null;
@@ -649,6 +653,49 @@
);
}
+ public void testGetMembersInjector() {
+ checkModule(
+ new AbstractModule() {
+ protected void configure() {
+ MembersInjector<A<String>> typeMembersInjector
+ = getMembersInjector(new TypeLiteral<A<String>>() {});
+ try {
+ typeMembersInjector.injectMembers(new A<String>());
+ } catch (IllegalStateException e) {
+ assertEquals(
+ "This MembersInjector cannot be used until the Injector
has been created.",
+ e.getMessage());
+ }
+
+ MembersInjector<String> classMembersInjector =
getMembersInjector(String.class);
+ try {
+ classMembersInjector.injectMembers("hello");
+ } catch (IllegalStateException e) {
+ assertEquals(
+ "This MembersInjector cannot be used until the Injector
has been created.",
+ e.getMessage());
+ }
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public <T> Void visit(MembersInjectorLookup<T>
command) {
+ assertEquals(new TypeLiteral<A<String>>() {},
command.getTypeLiteral());
+ assertNull(command.getDelegate());
+ return null;
+ }
+ },
+
+ new FailingElementVisitor() {
+ @Override public <T> Void visit(MembersInjectorLookup<T>
command) {
+ assertEquals(TypeLiteral.get(String.class),
command.getTypeLiteral());
+ assertNull(command.getDelegate());
+ return null;
+ }
+ }
+ );
+ }
+
public void testRequestInjection() {
final Object firstObject = new Object();
final Object secondObject = new Object();
@@ -1004,4 +1051,8 @@
public @interface SampleAnnotation { }
public enum CoinSide { HEADS, TAILS }
+
+ static class A<T> {
+ @Inject Stage stage;
+ }
}
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---