Changed the InjectionPoint factory methods. Now the ConfigurationException includes a partial value, which means that a sink collection doesn't need to be passed in.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@670 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/ConfigurationException.java b/src/com/google/inject/ConfigurationException.java
index b67a20c..e74a35b 100644
--- a/src/com/google/inject/ConfigurationException.java
+++ b/src/com/google/inject/ConfigurationException.java
@@ -16,6 +16,7 @@
package com.google.inject;
+import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors;
import com.google.inject.spi.Message;
@@ -30,6 +31,7 @@
public final class ConfigurationException extends RuntimeException {
private final ImmutableSet<Message> messages;
+ private Object partialValue = null;
/** Creates a ConfigurationException containing {@code messages}. */
public ConfigurationException(Iterable<Message> messages) {
@@ -37,11 +39,32 @@
initCause(Errors.getOnlyCause(this.messages));
}
+ /** Returns a copy of this configuration exception with the specified partial value. */
+ public ConfigurationException withPartialValue(Object partialValue) {
+ checkState(this.partialValue == null,
+ "Can't clobber existing partial value %s with %s", this.partialValue, partialValue);
+ ConfigurationException result = new ConfigurationException(messages);
+ result.partialValue = partialValue;
+ return result;
+ }
+
/** Returns messages for the errors that caused this exception. */
public Collection<Message> getErrorMessages() {
return messages;
}
+ /**
+ * Returns a value that was only partially computed due to this exception. The caller can use
+ * this while collecting additional configuration problems.
+ *
+ * @return the partial value, or {@code null} if none was set. The type of the partial value is
+ * specified by the throwing method.
+ */
+ @SuppressWarnings("unchecked") // this is *extremely* unsafe. We trust the caller here.
+ public <E> E getPartialValue() {
+ return (E) partialValue;
+ }
+
@Override public String getMessage() {
return Errors.format("Guice configuration errors", messages);
}
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index e9a5138..1699835 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -42,7 +42,7 @@
this.implementation = implementation;
try {
- this.injectionPoint = InjectionPoint.forConstructorOf(implementation.getType());
+ this.injectionPoint = InjectionPoint.forConstructorOf(implementation);
} catch (ConfigurationException e) {
throw errors.merge(e.getErrorMessages()).toException();
}
diff --git a/src/com/google/inject/InjectionRequestProcessor.java b/src/com/google/inject/InjectionRequestProcessor.java
index 39858cb..b761b33 100644
--- a/src/com/google/inject/InjectionRequestProcessor.java
+++ b/src/com/google/inject/InjectionRequestProcessor.java
@@ -17,7 +17,6 @@
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.internal.Errors;
import com.google.inject.internal.ErrorsException;
@@ -25,6 +24,7 @@
import com.google.inject.spi.InjectionRequest;
import com.google.inject.spi.StaticInjectionRequest;
import java.util.List;
+import java.util.Set;
/**
* Handles {@link Binder#requestInjection} and {@link Binder#requestStaticInjection} commands.
@@ -50,16 +50,16 @@
}
@Override public Boolean visitInjectionRequest(InjectionRequest command) {
- List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ Set<InjectionPoint> injectionPoints;
try {
- InjectionPoint.addForInstanceMethodsAndFields(
- command.getInstance().getClass(), injectionPointsList);
+ injectionPoints = InjectionPoint.forInstanceMethodsAndFields(
+ command.getInstance().getClass());
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
+ injectionPoints = e.getPartialValue();
}
- memberInjector.requestInjection(command.getInstance(), command.getSource(),
- ImmutableSet.copyOf(injectionPointsList));
+ memberInjector.requestInjection(command.getInstance(), command.getSource(), injectionPoints);
return true;
}
@@ -88,11 +88,12 @@
void validate(final InjectorImpl injector) {
Errors errorsForMember = errors.withSource(source);
- List<InjectionPoint> injectionPoints = Lists.newArrayList();
+ Set<InjectionPoint> injectionPoints;
try {
- InjectionPoint.addForStaticMethodsAndFields(type, injectionPoints);
+ injectionPoints = InjectionPoint.forStaticMethodsAndFields(type);
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
+ injectionPoints = e.getPartialValue();
}
memberInjectors = injector.getInjectors(injectionPoints, errorsForMember);
}
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 8a785ce..8786d19 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -50,6 +50,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Default {@link Injector} implementation.
@@ -617,11 +618,12 @@
protected ImmutableList<SingleMemberInjector> create(TypeLiteral<?> type, Errors errors)
throws ErrorsException {
int numErrorsBefore = errors.size();
- List<InjectionPoint> injectionPoints = Lists.newArrayList();
+ Set<InjectionPoint> injectionPoints;
try {
- InjectionPoint.addForInstanceMethodsAndFields(type.getType(), injectionPoints);
+ injectionPoints = InjectionPoint.forInstanceMethodsAndFields(type);
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
+ injectionPoints = e.getPartialValue();
}
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
errors.throwIfNewErrors(numErrorsBefore);
@@ -631,7 +633,7 @@
/** Returns the injectors for the specified injection points. */
ImmutableList<SingleMemberInjector> getInjectors(
- List<InjectionPoint> injectionPoints, Errors errors) {
+ Set<InjectionPoint> injectionPoints, Errors errors) {
List<SingleMemberInjector> injectors = Lists.newArrayList();
for (InjectionPoint injectionPoint : injectionPoints) {
try {
diff --git a/src/com/google/inject/internal/ModuleBinding.java b/src/com/google/inject/internal/ModuleBinding.java
index 2b34100..d15845a 100644
--- a/src/com/google/inject/internal/ModuleBinding.java
+++ b/src/com/google/inject/internal/ModuleBinding.java
@@ -18,7 +18,6 @@
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.ConfigurationException;
@@ -39,7 +38,6 @@
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;
/**
@@ -204,16 +202,15 @@
}
// lookup the injection points, adding any errors to the binder's errors list
- List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ Set<InjectionPoint> injectionPoints;
try {
- InjectionPoint.addForInstanceMethodsAndFields(instance.getClass(), injectionPointsList);
+ injectionPoints = InjectionPoint.forInstanceMethodsAndFields(instance.getClass());
} catch (ConfigurationException e) {
for (Message message : e.getErrorMessages()) {
binder.addError(message);
}
+ injectionPoints = e.getPartialValue();
}
- ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
-
target = new InstanceTarget<T>(instance, injectionPoints);
}
@@ -222,19 +219,20 @@
checkNotTargetted();
// lookup the injection points, adding any errors to the binder's errors list
- List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+ Set<InjectionPoint> injectionPoints;
try {
- InjectionPoint.addForInstanceMethodsAndFields(provider.getClass(), injectionPointsList);
+ injectionPoints = InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
} catch (ConfigurationException e) {
for (Message message : e.getErrorMessages()) {
binder.addError(message);
}
+ injectionPoints = e.getPartialValue();
}
- final ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
+ final Set<InjectionPoint> injectionPointsFinal = injectionPoints;
target = new Target<T>() {
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
- return visitor.visitProvider(provider, injectionPoints);
+ return visitor.visitProvider(provider, injectionPointsFinal);
}
};
return this;
@@ -469,9 +467,9 @@
private final T instance;
private final ImmutableSet<InjectionPoint> injectionPoints;
- public InstanceTarget(T instance, ImmutableSet<InjectionPoint> injectionPoints) {
+ public InstanceTarget(T instance, Set<InjectionPoint> injectionPoints) {
this.instance = instance;
- this.injectionPoints = injectionPoints;
+ this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index 1d5aac9..4f03ee6 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -17,6 +17,7 @@
package com.google.inject.spi;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
@@ -41,6 +42,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
* A constructor, field or method that can receive injections. Typically this is a member with the
@@ -181,10 +183,9 @@
* constructor, or if parameters of the injectable constructor are malformed, such as a
* parameter with multiple binding annotations.
*/
- public static InjectionPoint forConstructorOf(Type type) {
- Errors errors = new Errors(type);
- TypeLiteral<?> typeLiteral = TypeLiteral.get(type);
- Class<?> rawType = MoreTypes.getRawType(type);
+ public static InjectionPoint forConstructorOf(TypeLiteral<?> type) {
+ Class<?> rawType = MoreTypes.getRawType(type.getType());
+ Errors errors = new Errors(rawType);
Constructor<?> injectableConstructor = null;
for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
@@ -206,7 +207,7 @@
errors.throwConfigurationExceptionIfErrorsExist();
if (injectableConstructor != null) {
- return new InjectionPoint(typeLiteral, injectableConstructor);
+ return new InjectionPoint(type, injectableConstructor);
}
// If no annotated constructor is found, look for a no-arg constructor instead.
@@ -221,7 +222,7 @@
}
checkForMisplacedBindingAnnotations(noArgConstructor, errors);
- return new InjectionPoint(typeLiteral, noArgConstructor);
+ return new InjectionPoint(type, noArgConstructor);
} catch (NoSuchMethodException e) {
errors.missingConstructor(rawType);
throw new ConfigurationException(errors.getMessages());
@@ -229,38 +230,92 @@
}
/**
- * 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.
+ * Returns a new injection point for the injectable constructor of {@code type}.
*
- * @throws ConfigurationException 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.
+ * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
+ * or a no-arguments constructor that is not private.
+ * @throws ConfigurationException if there is no injectable constructor, more than one injectable
+ * constructor, or if parameters of the injectable constructor are malformed, such as a
+ * parameter with multiple binding annotations.
*/
- public static void addForStaticMethodsAndFields(Type type, Collection<InjectionPoint> sink) {
- Errors errors = new Errors();
- TypeLiteral<?> typeLiteral = TypeLiteral.get(type);
- addInjectionPoints(typeLiteral, Factory.FIELDS, true, sink, errors);
- addInjectionPoints(typeLiteral, Factory.METHODS, true, sink, errors);
- errors.throwConfigurationExceptionIfErrorsExist();
+ public static InjectionPoint forConstructorOf(Class<?> type) {
+ return forConstructorOf(TypeLiteral.get(type));
}
/**
- * 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.
+ * Returns all static method and field injection points on {@code type}. 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 ConfigurationException 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.
+ * a field with multiple binding annotations. The exception's {@link
+ * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
+ * of the valid injection points.
*/
- public static void addForInstanceMethodsAndFields(Type type, Collection<InjectionPoint> sink) {
- // TODO (crazybob): Filter out overridden members.
+ public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral type) {
+ List<InjectionPoint> sink = Lists.newArrayList();
Errors errors = new Errors();
- TypeLiteral<?> typeLiteral = TypeLiteral.get(type);
- addInjectionPoints(typeLiteral, Factory.FIELDS, false, sink, errors);
- addInjectionPoints(typeLiteral, Factory.METHODS, false, sink, errors);
- errors.throwConfigurationExceptionIfErrorsExist();
+
+ addInjectionPoints(type, Factory.FIELDS, true, sink, errors);
+ addInjectionPoints(type, Factory.METHODS, true, sink, errors);
+
+ ImmutableSet<InjectionPoint> result = ImmutableSet.copyOf(sink);
+ if (errors.hasErrors()) {
+ throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
+ }
+ return result;
+ }
+ /**
+ * Returns all static method and field injection points on {@code type}. 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 ConfigurationException if there is a malformed injection point on {@code type}, such as
+ * a field with multiple binding annotations. The exception's {@link
+ * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
+ * of the valid injection points.
+ */
+ public static Set<InjectionPoint> forStaticMethodsAndFields(Class<?> type) {
+ return forStaticMethodsAndFields(TypeLiteral.get(type));
+ }
+
+ /**
+ * Returns all instance method and field injection points on {@code type}. 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 ConfigurationException if there is a malformed injection point on {@code type}, such as
+ * a field with multiple binding annotations. The exception's {@link
+ * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
+ * of the valid injection points.
+ */
+ public static Set<InjectionPoint> forInstanceMethodsAndFields(TypeLiteral<?> type) {
+ List<InjectionPoint> sink = Lists.newArrayList();
+ Errors errors = new Errors();
+
+ // TODO (crazybob): Filter out overridden members.
+ addInjectionPoints(type, Factory.FIELDS, false, sink, errors);
+ addInjectionPoints(type, Factory.METHODS, false, sink, errors);
+
+ ImmutableSet<InjectionPoint> result = ImmutableSet.copyOf(sink);
+ if (errors.hasErrors()) {
+ throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
+ }
+ return result;
+ }
+
+ /**
+ * Returns all instance method and field injection points on {@code type}. 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 ConfigurationException if there is a malformed injection point on {@code type}, such as
+ * a field with multiple binding annotations. The exception's {@link
+ * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
+ * of the valid injection points.
+ */
+ public static Set<InjectionPoint> forInstanceMethodsAndFields(Class<?> type) {
+ return forInstanceMethodsAndFields(TypeLiteral.get(type));
}
private static void checkForMisplacedBindingAnnotations(Member member, Errors errors) {
diff --git a/test/com/google/inject/ProxyFactoryTest.java b/test/com/google/inject/ProxyFactoryTest.java
index 9d0dd2e..93ce73f 100644
--- a/test/com/google/inject/ProxyFactoryTest.java
+++ b/test/com/google/inject/ProxyFactoryTest.java
@@ -46,8 +46,8 @@
aspects.add(new MethodAspect(any(), any(), interceptor));
ProxyFactory factory = new ProxyFactory(aspects);
- ConstructionProxy<Simple> constructionProxy
- = factory.createConstructionProxy(InjectionPoint.forConstructorOf(Simple.class));
+ ConstructionProxy<Simple> constructionProxy = factory.createConstructionProxy(
+ InjectionPoint.forConstructorOf(Simple.class));
Simple simple = constructionProxy.newInstance();
simple.invoke();
@@ -79,10 +79,8 @@
aspects.add(new MethodAspect(only(Bar.class), annotatedWith(Intercept.class), interceptor));
ProxyFactory factory = new ProxyFactory(aspects);
- ConstructionProxy<Foo> fooFactory =
- factory.get(InjectionPoint.forConstructorOf(Foo.class));
- ConstructionProxy<Bar> barFactory =
- factory.get(InjectionPoint.forConstructorOf(Bar.class));
+ ConstructionProxy<Foo> fooFactory = factory.get(InjectionPoint.forConstructorOf(Foo.class));
+ ConstructionProxy<Bar> barFactory = factory.get(InjectionPoint.forConstructorOf(Bar.class));
Foo foo = fooFactory.newInstance();
Bar bar = barFactory.newInstance();
@@ -133,8 +131,7 @@
aspects.add(new MethodAspect(any(), any(), interceptor));
ProxyFactory factory = new ProxyFactory(aspects);
- ConstructionProxy<A> constructor =
- factory.get(InjectionPoint.forConstructorOf(A.class));
+ ConstructionProxy<A> constructor = factory.get(InjectionPoint.forConstructorOf(A.class));
A a = constructor.newInstance(5);
a.a();
@@ -148,8 +145,7 @@
aspects.add(new MethodAspect(not(any()), not(any()), interceptor));
ProxyFactory factory = new ProxyFactory(aspects);
- ConstructionProxy<A> constructor =
- factory.get(InjectionPoint.forConstructorOf(A.class));
+ ConstructionProxy<A> constructor = factory.get(InjectionPoint.forConstructorOf(A.class));
A a = constructor.newInstance(5);
assertEquals(A.class, a.getClass());
@@ -171,8 +167,8 @@
aspects.add(new MethodAspect(any(), any(), doubleInterceptor, countingInterceptor));
ProxyFactory factory = new ProxyFactory(aspects);
- ConstructionProxy<Counter> constructor =
- factory.get(InjectionPoint.forConstructorOf(Counter.class));
+ ConstructionProxy<Counter> constructor
+ = factory.get(InjectionPoint.forConstructorOf(Counter.class));
Counter counter = constructor.newInstance();
counter.inc();
diff --git a/test/com/google/inject/spi/InjectionPointTest.java b/test/com/google/inject/spi/InjectionPointTest.java
index 9760f0e..39d823d 100644
--- a/test/com/google/inject/spi/InjectionPointTest.java
+++ b/test/com/google/inject/spi/InjectionPointTest.java
@@ -18,7 +18,6 @@
import com.google.common.collect.ImmutableSet;
import static com.google.common.collect.Iterables.getOnlyElement;
-import com.google.common.collect.Sets;
import static com.google.inject.Asserts.assertEqualsBothWays;
import static com.google.inject.Asserts.assertSimilarWhenReserialized;
import com.google.inject.Inject;
@@ -31,7 +30,6 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import junit.framework.Assert;
@@ -140,24 +138,23 @@
Method instanceMethod = HasInjections.class.getMethod("instanceMethod", String.class);
Field instanceField = HasInjections.class.getField("instanceField");
- Set<InjectionPoint> sink = Sets.newHashSet();
- InjectionPoint.addForInstanceMethodsAndFields(HasInjections.class, sink);
+ TypeLiteral<HasInjections> type = TypeLiteral.get(HasInjections.class);
assertEquals(ImmutableSet.of(
- new InjectionPoint(TypeLiteral.get(HasInjections.class), instanceMethod),
- new InjectionPoint(TypeLiteral.get(HasInjections.class), instanceField)),
- sink);
+ new InjectionPoint(type, instanceMethod),
+ new InjectionPoint(type, instanceField)),
+ InjectionPoint.forInstanceMethodsAndFields(HasInjections.class));
}
public void testAddForStaticMethodsAndFields() throws Exception {
Method staticMethod = HasInjections.class.getMethod("staticMethod", String.class);
Field staticField = HasInjections.class.getField("staticField");
- Set<InjectionPoint> sink = Sets.newHashSet();
- InjectionPoint.addForStaticMethodsAndFields(HasInjections.class, sink);
+ Set<InjectionPoint> injectionPoints = InjectionPoint.forStaticMethodsAndFields(
+ HasInjections.class);
assertEquals(ImmutableSet.of(
new InjectionPoint(TypeLiteral.get(HasInjections.class), staticMethod),
new InjectionPoint(TypeLiteral.get(HasInjections.class), staticField)),
- sink);
+ injectionPoints);
}
static class HasInjections {
@@ -168,15 +165,13 @@
}
public void testAddForParameterizedInjections() {
- Set<InjectionPoint> sink = Sets.newHashSet();
- Type type = new TypeLiteral<ParameterizedInjections<String>>() {}.getType();
+ TypeLiteral<?> type = new TypeLiteral<ParameterizedInjections<String>>() {};
InjectionPoint constructor = InjectionPoint.forConstructorOf(type);
assertEquals(new Key<Map<String, String>>() {},
getOnlyElement(constructor.getDependencies()).getKey());
- InjectionPoint.addForInstanceMethodsAndFields(type, sink);
- InjectionPoint field = getOnlyElement(sink);
+ InjectionPoint field = getOnlyElement(InjectionPoint.forInstanceMethodsAndFields(type));
assertEquals(new Key<Set<String>>() {}, getOnlyElement(field.getDependencies()).getKey());
}