blob: 430738f4d9f45b679cce887fed6e86a7111a61ce [file] [log] [blame]
limpbizkit2d633cd2008-12-09 06:26:00 +00001/**
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.assistedinject;
18
sberlind9c913a2011-06-26 21:02:54 +000019import static com.google.common.collect.Iterables.getOnlyElement;
20import static com.google.common.base.Preconditions.checkState;
sberlin4b919552010-10-17 01:28:06 +000021
limpbizkit2d633cd2008-12-09 06:26:00 +000022import com.google.inject.AbstractModule;
23import com.google.inject.Binder;
24import com.google.inject.Binding;
25import com.google.inject.ConfigurationException;
26import com.google.inject.Inject;
27import com.google.inject.Injector;
28import com.google.inject.Key;
29import com.google.inject.Module;
30import com.google.inject.Provider;
31import com.google.inject.ProvisionException;
sberlin23cf2d42011-01-29 20:42:22 +000032import com.google.inject.Scopes;
limpbizkit2d633cd2008-12-09 06:26:00 +000033import com.google.inject.TypeLiteral;
sberlin4b919552010-10-17 01:28:06 +000034import com.google.inject.internal.Annotations;
sberlinf7ac6ea2010-05-09 12:48:24 +000035import com.google.inject.internal.BytecodeGen;
limpbizkit2d633cd2008-12-09 06:26:00 +000036import com.google.inject.internal.Errors;
37import com.google.inject.internal.ErrorsException;
sberlin@gmail.com88f9a142010-07-17 16:39:00 +000038import com.google.inject.internal.util.Classes;
sberlind9c913a2011-06-26 21:02:54 +000039import com.google.common.base.Objects;
40import com.google.common.collect.ImmutableList;
41import com.google.common.collect.ImmutableMap;
42import com.google.common.collect.ImmutableSet;
43import com.google.common.collect.Iterables;
44import com.google.common.collect.Lists;
sberlin4b919552010-10-17 01:28:06 +000045import com.google.inject.spi.BindingTargetVisitor;
sberlin141f8002010-03-25 03:37:35 +000046import com.google.inject.spi.Dependency;
sberlinb199afd2010-09-23 03:16:22 +000047import com.google.inject.spi.HasDependencies;
sberlin141f8002010-03-25 03:37:35 +000048import com.google.inject.spi.InjectionPoint;
limpbizkit2d633cd2008-12-09 06:26:00 +000049import com.google.inject.spi.Message;
sberlin4b919552010-10-17 01:28:06 +000050import com.google.inject.spi.ProviderInstanceBinding;
51import com.google.inject.spi.ProviderWithExtensionVisitor;
sberlin97c22712010-02-05 21:12:05 +000052import com.google.inject.spi.Toolable;
limpbizkit2d633cd2008-12-09 06:26:00 +000053import com.google.inject.util.Providers;
sberlin4b919552010-10-17 01:28:06 +000054
limpbizkit2d633cd2008-12-09 06:26:00 +000055import java.lang.annotation.Annotation;
sberlin141f8002010-03-25 03:37:35 +000056import java.lang.reflect.Constructor;
limpbizkit2d633cd2008-12-09 06:26:00 +000057import java.lang.reflect.InvocationHandler;
58import java.lang.reflect.Method;
sberlin@gmail.com88f9a142010-07-17 16:39:00 +000059import java.lang.reflect.Modifier;
limpbizkit2d633cd2008-12-09 06:26:00 +000060import java.lang.reflect.Proxy;
limpbizkit2d633cd2008-12-09 06:26:00 +000061import java.util.Arrays;
sberlin3748e4a2010-05-23 13:40:06 +000062import java.util.Collection;
sberlin141f8002010-03-25 03:37:35 +000063import java.util.Collections;
sberlinb199afd2010-09-23 03:16:22 +000064import java.util.HashSet;
limpbizkit53664a72009-02-21 00:25:27 +000065import java.util.List;
sberlin141f8002010-03-25 03:37:35 +000066import java.util.Map;
sberlinb199afd2010-09-23 03:16:22 +000067import java.util.Set;
limpbizkit2d633cd2008-12-09 06:26:00 +000068
69/**
70 * The newer implementation of factory provider. This implementation uses a child injector to
71 * create values.
72 *
73 * @author jessewilson@google.com (Jesse Wilson)
74 * @author dtm@google.com (Daniel Martin)
limpbizkit6840fcb2009-08-19 18:31:04 +000075 * @author schmitt@google.com (Peter Schmitt)
sberlin141f8002010-03-25 03:37:35 +000076 * @author sameb@google.com (Sam Berlin)
limpbizkit2d633cd2008-12-09 06:26:00 +000077 */
sberlin4b919552010-10-17 01:28:06 +000078final class FactoryProvider2 <F> implements InvocationHandler,
79 ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> {
limpbizkit2d633cd2008-12-09 06:26:00 +000080
81 /** if a factory method parameter isn't annotated, it gets this annotation. */
82 static final Assisted DEFAULT_ANNOTATION = new Assisted() {
83 public String value() {
84 return "";
85 }
86
87 public Class<? extends Annotation> annotationType() {
88 return Assisted.class;
89 }
90
91 @Override public boolean equals(Object o) {
92 return o instanceof Assisted
93 && ((Assisted) o).value().equals("");
94 }
95
96 @Override public int hashCode() {
97 return 127 * "value".hashCode() ^ "".hashCode();
98 }
99
100 @Override public String toString() {
limpbizkitddb38622008-12-29 05:21:16 +0000101 return "@" + Assisted.class.getName() + "(value=)";
limpbizkit2d633cd2008-12-09 06:26:00 +0000102 }
103 };
sberlin4b919552010-10-17 01:28:06 +0000104
sberlin141f8002010-03-25 03:37:35 +0000105 /** All the data necessary to perform an assisted inject. */
sberlin4b919552010-10-17 01:28:06 +0000106 private static class AssistData implements AssistedMethod {
sberlin141f8002010-03-25 03:37:35 +0000107 /** the constructor the implementation is constructed with. */
108 final Constructor<?> constructor;
109 /** the return type in the factory method that the constructor is bound to. */
110 final Key<?> returnType;
111 /** the parameters in the factory method associated with this data. */
112 final ImmutableList<Key<?>> paramTypes;
sberlin4b919552010-10-17 01:28:06 +0000113 /** the type of the implementation constructed */
114 final TypeLiteral<?> implementationType;
115
sberlinb199afd2010-09-23 03:16:22 +0000116 /** All non-assisted dependencies required by this method. */
117 final Set<Dependency<?>> dependencies;
sberlin4b919552010-10-17 01:28:06 +0000118 /** The factory method associated with this data*/
119 final Method factoryMethod;
120
sberlin141f8002010-03-25 03:37:35 +0000121 /** true if {@link #validForOptimizedAssistedInject} returned true. */
122 final boolean optimized;
123 /** the list of optimized providers, empty if not optimized. */
124 final List<ThreadLocalProvider> providers;
125 /** used to perform optimized factory creations. */
126 volatile Binding<?> cachedBinding; // TODO: volatile necessary?
sberlin4b919552010-10-17 01:28:06 +0000127
128 AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes,
129 TypeLiteral<?> implementationType, Method factoryMethod,
130 Set<Dependency<?>> dependencies,
131 boolean optimized, List<ThreadLocalProvider> providers) {
sberlin141f8002010-03-25 03:37:35 +0000132 this.constructor = constructor;
133 this.returnType = returnType;
134 this.paramTypes = paramTypes;
sberlin4b919552010-10-17 01:28:06 +0000135 this.implementationType = implementationType;
136 this.factoryMethod = factoryMethod;
137 this.dependencies = dependencies;
sberlin141f8002010-03-25 03:37:35 +0000138 this.optimized = optimized;
139 this.providers = providers;
140 }
141
142 @Override
143 public String toString() {
sberlind9c913a2011-06-26 21:02:54 +0000144 return Objects.toStringHelper(getClass())
sberlin141f8002010-03-25 03:37:35 +0000145 .add("ctor", constructor)
146 .add("return type", returnType)
147 .add("param type", paramTypes)
sberlin4b919552010-10-17 01:28:06 +0000148 .add("implementation type", implementationType)
149 .add("dependencies", dependencies)
150 .add("factory method", factoryMethod)
sberlin141f8002010-03-25 03:37:35 +0000151 .add("optimized", optimized)
152 .add("providers", providers)
153 .add("cached binding", cachedBinding)
154 .toString();
sberlin4b919552010-10-17 01:28:06 +0000155 }
156
157 public Set<Dependency<?>> getDependencies() {
158 return dependencies;
159 }
160
161 public Method getFactoryMethod() {
162 return factoryMethod;
163 }
164
165 public Constructor<?> getImplementationConstructor() {
166 return constructor;
167 }
168
169 public TypeLiteral<?> getImplementationType() {
170 return implementationType;
sberlin141f8002010-03-25 03:37:35 +0000171 }
172 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000173
sberlin4b919552010-10-17 01:28:06 +0000174 /** Mapping from method to the data about how the method will be assisted. */
175 private final ImmutableMap<Method, AssistData> assistDataByMethod;
limpbizkit2d633cd2008-12-09 06:26:00 +0000176
177 /** the hosting injector, or null if we haven't been initialized yet */
178 private Injector injector;
179
180 /** the factory interface, implemented and provided */
181 private final F factory;
sberlin4b919552010-10-17 01:28:06 +0000182
183 /** The key that this is bound to. */
184 private final Key<F> factoryKey;
limpbizkit2d633cd2008-12-09 06:26:00 +0000185
186 /**
187 * @param factoryType a Java interface that defines one or more create methods.
limpbizkitc1e65da2009-08-19 18:18:16 +0000188 * @param collector binding configuration that maps method return types to
189 * implementation types.
limpbizkit2d633cd2008-12-09 06:26:00 +0000190 */
sberlin4b919552010-10-17 01:28:06 +0000191 FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
192 this.factoryKey = factoryKey;
193
194 TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
limpbizkit2d633cd2008-12-09 06:26:00 +0000195 Errors errors = new Errors();
limpbizkit9d6b6562009-02-27 21:59:15 +0000196
197 @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
198 Class<F> factoryRawType = (Class) factoryType.getRawType();
199
limpbizkit2d633cd2008-12-09 06:26:00 +0000200 try {
sberlin2ef22662010-10-25 02:57:48 +0000201 if(!factoryRawType.isInterface()) {
202 throw errors.addMessage("%s must be an interface.", factoryRawType).toException();
203 }
204
sberlin141f8002010-03-25 03:37:35 +0000205 ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
limpbizkit2d633cd2008-12-09 06:26:00 +0000206 // TODO: also grab methods from superinterfaces
limpbizkit9d6b6562009-02-27 21:59:15 +0000207 for (Method method : factoryRawType.getMethods()) {
sberlin3748e4a2010-05-23 13:40:06 +0000208 TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
209 Key<?> returnType;
210 try {
sberlin4b919552010-10-17 01:28:06 +0000211 returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
sberlin3748e4a2010-05-23 13:40:06 +0000212 } catch(ConfigurationException ce) {
213 // If this was an error due to returnTypeLiteral not being specified, rephrase
214 // it as our factory not being specified, so it makes more sense to users.
215 if(isTypeNotSpecified(returnTypeLiteral, ce)) {
216 throw errors.keyNotFullySpecified(TypeLiteral.get(factoryRawType)).toException();
217 } else {
218 throw ce;
219 }
220 }
sberlin2ef22662010-10-25 02:57:48 +0000221 validateFactoryReturnType(errors, returnType.getTypeLiteral().getRawType(), factoryRawType);
limpbizkit9d6b6562009-02-27 21:59:15 +0000222 List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
limpbizkit2d633cd2008-12-09 06:26:00 +0000223 Annotation[][] paramAnnotations = method.getParameterAnnotations();
224 int p = 0;
limpbizkit53664a72009-02-21 00:25:27 +0000225 List<Key<?>> keys = Lists.newArrayList();
limpbizkit9d6b6562009-02-27 21:59:15 +0000226 for (TypeLiteral<?> param : params) {
sberlin4b919552010-10-17 01:28:06 +0000227 Key<?> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors);
sberlinb5a75ed2010-07-31 16:08:27 +0000228 Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
229 if (underlylingType.equals(Provider.class)
230 || underlylingType.equals(javax.inject.Provider.class)) {
231 errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject."
232 + "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
233 p, paramKey, method);
234 }
limpbizkit53664a72009-02-21 00:25:27 +0000235 keys.add(assistKey(method, paramKey, errors));
limpbizkit2d633cd2008-12-09 06:26:00 +0000236 }
sberlin141f8002010-03-25 03:37:35 +0000237 ImmutableList<Key<?>> immutableParamList = ImmutableList.copyOf(keys);
sberlin4b919552010-10-17 01:28:06 +0000238
sberlin141f8002010-03-25 03:37:35 +0000239 // try to match up the method to the constructor
240 TypeLiteral<?> implementation = collector.getBindings().get(returnType);
241 if(implementation == null) {
242 implementation = returnType.getTypeLiteral();
243 }
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000244 InjectionPoint ctorInjectionPoint;
245 try {
sberlin4b919552010-10-17 01:28:06 +0000246 ctorInjectionPoint =
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000247 findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList);
248 } catch(ErrorsException ee) {
249 errors.merge(ee.getErrors());
250 continue;
sberlin141f8002010-03-25 03:37:35 +0000251 }
sberlin4b919552010-10-17 01:28:06 +0000252
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000253 Constructor<?> constructor = (Constructor)ctorInjectionPoint.getMember();
sberlin141f8002010-03-25 03:37:35 +0000254 List<ThreadLocalProvider> providers = Collections.emptyList();
sberlinb199afd2010-09-23 03:16:22 +0000255 Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
sberlin141f8002010-03-25 03:37:35 +0000256 boolean optimized = false;
257 // Now go through all dependencies of the implementation and see if it is OK to
258 // use an optimized form of assistedinject2. The optimized form requires that
259 // all injections directly inject the object itself (and not a Provider of the object,
260 // or an Injector), because it caches a single child injector and mutates the Provider
261 // of the arguments in a ThreadLocal.
sberlinb199afd2010-09-23 03:16:22 +0000262 if(isValidForOptimizedAssistedInject(deps)) {
sberlin141f8002010-03-25 03:37:35 +0000263 ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
264 for(int i = 0; i < params.size(); i++) {
265 providerListBuilder.add(new ThreadLocalProvider());
266 }
267 providers = providerListBuilder.build();
268 optimized = true;
269 }
sberlinb199afd2010-09-23 03:16:22 +0000270 assistDataBuilder.put(method,
sberlin4b919552010-10-17 01:28:06 +0000271 new AssistData(constructor, returnType, immutableParamList, implementation,
272 method, removeAssistedDeps(deps), optimized, providers));
limpbizkit2d633cd2008-12-09 06:26:00 +0000273 }
sberlin141f8002010-03-25 03:37:35 +0000274
275 // If we generated any errors (from finding matching constructors, for instance), throw an exception.
276 if(errors.hasErrors()) {
277 throw errors.toException();
278 }
279
280 assistDataByMethod = assistDataBuilder.build();
limpbizkit2d633cd2008-12-09 06:26:00 +0000281 } catch (ErrorsException e) {
282 throw new ConfigurationException(e.getErrors().getMessages());
283 }
sberlin4b919552010-10-17 01:28:06 +0000284
sberlinf7ac6ea2010-05-09 12:48:24 +0000285 factory = factoryRawType.cast(Proxy.newProxyInstance(BytecodeGen.getClassLoader(factoryRawType),
limpbizkit9d6b6562009-02-27 21:59:15 +0000286 new Class[] { factoryRawType }, this));
limpbizkit2d633cd2008-12-09 06:26:00 +0000287 }
288
289 public F get() {
290 return factory;
291 }
sberlin4b919552010-10-17 01:28:06 +0000292
sberlinb199afd2010-09-23 03:16:22 +0000293 public Set<Dependency<?>> getDependencies() {
294 Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>();
295 for(AssistData data : assistDataByMethod.values()) {
296 combinedDeps.addAll(data.dependencies);
297 }
298 return ImmutableSet.copyOf(combinedDeps);
299 }
sberlin4b919552010-10-17 01:28:06 +0000300
301 public Key<F> getKey() {
302 return factoryKey;
303 }
304
305 // safe cast because values are typed to AssistedData, which is an AssistedMethod
306 @SuppressWarnings("unchecked")
307 public Collection<AssistedMethod> getAssistedMethods() {
308 return (Collection)assistDataByMethod.values();
309 }
310
311 @SuppressWarnings("unchecked")
sberlin28735552010-10-17 01:31:25 +0000312 public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor,
sberlin4b919552010-10-17 01:28:06 +0000313 ProviderInstanceBinding<? extends T> binding) {
314 if (visitor instanceof AssistedInjectTargetVisitor) {
315 return ((AssistedInjectTargetVisitor<T, V>)visitor).visit((AssistedInjectBinding<T>)this);
316 }
317 return visitor.visit(binding);
318 }
sberlin2ef22662010-10-25 02:57:48 +0000319
320 private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
321 if (Modifier.isPublic(factoryType.getModifiers())
322 && !Modifier.isPublic(returnType.getModifiers())) {
323 errors.addMessage("%s is public, but has a method that returns a non-public type: %s. "
324 + "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
325 + "Please either make the factory non-public or the return type public.",
326 factoryType, returnType);
327 }
328 }
sberlinb5ec94a2010-03-27 20:36:13 +0000329
330 /**
sberlin3748e4a2010-05-23 13:40:06 +0000331 * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
332 * specified.
333 */
334 private boolean isTypeNotSpecified(TypeLiteral typeLiteral, ConfigurationException ce) {
335 Collection<Message> messages = ce.getErrorMessages();
336 if (messages.size() == 1) {
337 Message msg = Iterables.getOnlyElement(
338 new Errors().keyNotFullySpecified(typeLiteral).getMessages());
339 return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
340 } else {
341 return false;
342 }
343 }
344
345 /**
sberlinb5ec94a2010-03-27 20:36:13 +0000346 * Finds a constructor suitable for the method. If the implementation contained any constructors
347 * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
348 * match the parameters (in any order) listed in the method. Otherwise, if no
349 * {@link AssistedInject} constructors exist, this will default to looking for an
350 * {@literal @}{@link Inject} constructor.
351 */
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000352 private InjectionPoint findMatchingConstructorInjectionPoint(
353 Method method, Key<?> returnType, TypeLiteral<?> implementation, List<Key<?>> paramList)
354 throws ErrorsException {
355 Errors errors = new Errors(method);
356 if(returnType.getTypeLiteral().equals(implementation)) {
357 errors = errors.withSource(implementation);
358 } else {
359 errors = errors.withSource(returnType).withSource(implementation);
360 }
sberlin4b919552010-10-17 01:28:06 +0000361
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000362 Class<?> rawType = implementation.getRawType();
363 if (Modifier.isInterface(rawType.getModifiers())) {
364 errors.addMessage(
365 "%s is an interface, not a concrete class. Unable to create AssistedInject factory.",
366 implementation);
367 throw errors.toException();
368 } else if (Modifier.isAbstract(rawType.getModifiers())) {
369 errors.addMessage(
370 "%s is abstract, not a concrete class. Unable to create AssistedInject factory.",
371 implementation);
372 throw errors.toException();
373 } else if (Classes.isInnerClass(rawType)) {
374 errors.cannotInjectInnerClass(rawType);
375 throw errors.toException();
376 }
sberlin4b919552010-10-17 01:28:06 +0000377
sberlinb5ec94a2010-03-27 20:36:13 +0000378 Constructor<?> matchingConstructor = null;
379 boolean anyAssistedInjectConstructors = false;
380 // Look for AssistedInject constructors...
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000381 for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
sberlinb5ec94a2010-03-27 20:36:13 +0000382 if (constructor.isAnnotationPresent(AssistedInject.class)) {
383 anyAssistedInjectConstructors = true;
384 if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
385 if (matchingConstructor != null) {
386 errors
387 .addMessage(
388 "%s has more than one constructor annotated with @AssistedInject"
389 + " that matches the parameters in method %s. Unable to create AssistedInject factory.",
390 implementation, method);
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000391 throw errors.toException();
sberlinb5ec94a2010-03-27 20:36:13 +0000392 } else {
393 matchingConstructor = constructor;
394 }
395 }
396 }
397 }
sberlin4b919552010-10-17 01:28:06 +0000398
sberlinb5ec94a2010-03-27 20:36:13 +0000399 if(!anyAssistedInjectConstructors) {
400 // If none existed, use @Inject.
401 try {
402 return InjectionPoint.forConstructorOf(implementation);
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000403 } catch(ConfigurationException e) {
404 errors.merge(e.getErrorMessages());
405 throw errors.toException();
sberlinb5ec94a2010-03-27 20:36:13 +0000406 }
407 } else {
408 // Otherwise, use it or fail with a good error message.
409 if(matchingConstructor != null) {
410 // safe because we got the constructor from this implementation.
411 @SuppressWarnings("unchecked")
412 InjectionPoint ip = InjectionPoint.forConstructor(
413 (Constructor)matchingConstructor, implementation);
414 return ip;
415 } else {
416 errors.addMessage(
417 "%s has @AssistedInject constructors, but none of them match the"
418 + " parameters in method %s. Unable to create AssistedInject factory.",
419 implementation, method);
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000420 throw errors.toException();
sberlinb5ec94a2010-03-27 20:36:13 +0000421 }
422 }
423 }
424
425 /**
426 * Matching logic for constructors annotated with AssistedInject.
427 * This returns true if and only if all @Assisted parameters in the
428 * constructor exactly match (in any order) all @Assisted parameters
429 * the method's parameter.
430 */
431 private boolean constructorHasMatchingParams(TypeLiteral<?> type,
432 Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
433 throws ErrorsException {
434 List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
435 Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
436 int p = 0;
437 List<Key<?>> constructorKeys = Lists.newArrayList();
438 for (TypeLiteral<?> param : params) {
sberlin4b919552010-10-17 01:28:06 +0000439 Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++],
sberlinb5ec94a2010-03-27 20:36:13 +0000440 errors);
441 constructorKeys.add(paramKey);
442 }
443 // Require that every key exist in the constructor to match up exactly.
444 for (Key<?> key : paramList) {
445 // If it didn't exist in the constructor set, we can't use it.
446 if (!constructorKeys.remove(key)) {
447 return false;
448 }
449 }
450 // If any keys remain and their annotation is Assisted, we can't use it.
451 for (Key<?> key : constructorKeys) {
452 if (key.getAnnotationType() == Assisted.class) {
453 return false;
454 }
455 }
456 // All @Assisted params match up to the method's parameters.
457 return true;
458 }
sberlinb199afd2010-09-23 03:16:22 +0000459
460 /** Calculates all dependencies required by the implementation and constructor. */
461 private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
462 ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
463 builder.addAll(ctorPoint.getDependencies());
464 if (!implementation.getRawType().isInterface()) {
465 for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
466 builder.addAll(ip.getDependencies());
sberlin141f8002010-03-25 03:37:35 +0000467 }
468 }
sberlinb199afd2010-09-23 03:16:22 +0000469 return builder.build();
470 }
sberlin4b919552010-10-17 01:28:06 +0000471
sberlinb199afd2010-09-23 03:16:22 +0000472 /** Return all non-assisted dependencies. */
473 private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
474 ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
475 for(Dependency<?> dep : deps) {
476 Class annotationType = dep.getKey().getAnnotationType();
477 if (annotationType == null || !annotationType.equals(Assisted.class)) {
478 builder.add(dep);
479 }
480 }
481 return builder.build();
482 }
sberlin4b919552010-10-17 01:28:06 +0000483
sberlinb199afd2010-09-23 03:16:22 +0000484 /**
485 * Returns true if all dependencies are suitable for the optimized version of AssistedInject. The
486 * optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if
487 * the assisted bindings are immediately provided. This looks for hints that the values may be
488 * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
489 */
490 private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies) {
491 for (Dependency<?> dep : dependencies) {
492 if (isInjectorOrAssistedProvider(dep)) {
493 return false;
sberlin141f8002010-03-25 03:37:35 +0000494 }
495 }
496 return true;
497 }
sberlin4b919552010-10-17 01:28:06 +0000498
sberlin141f8002010-03-25 03:37:35 +0000499 /**
500 * Returns true if the dependency is for {@link Injector} or if the dependency
501 * is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}.
502 */
503 private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
504 Class annotationType = dependency.getKey().getAnnotationType();
505 if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
506 if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider...
507 return true;
508 }
509 } else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector...
510 return true;
511 }
512 return false;
513 }
sberlinb5a75ed2010-07-31 16:08:27 +0000514
limpbizkit2d633cd2008-12-09 06:26:00 +0000515 /**
516 * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
517 * This fails if another binding annotation is clobbered in the process. If the key already has
518 * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
519 */
520 private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
521 if (key.getAnnotationType() == null) {
522 return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
523 } else if (key.getAnnotationType() == Assisted.class) {
524 return key;
525 } else {
526 errors.withSource(method).addMessage(
527 "Only @Assisted is allowed for factory parameters, but found @%s",
528 key.getAnnotationType());
529 throw errors.toException();
530 }
531 }
532
533 /**
534 * At injector-creation time, we initialize the invocation handler. At this time we make sure
535 * all factory methods will be able to build the target types.
536 */
sberlin97c22712010-02-05 21:12:05 +0000537 @Inject @Toolable
limpbizkit2d633cd2008-12-09 06:26:00 +0000538 void initialize(Injector injector) {
539 if (this.injector != null) {
540 throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class,
541 "Factories.create() factories may only be used in one Injector!")));
542 }
543
544 this.injector = injector;
545
sberlin141f8002010-03-25 03:37:35 +0000546 for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) {
547 Method method = entry.getKey();
548 AssistData data = entry.getValue();
549 Object[] args;
550 if(!data.optimized) {
551 args = new Object[method.getParameterTypes().length];
552 Arrays.fill(args, "dummy object for validating Factories");
553 } else {
554 args = null; // won't be used -- instead will bind to data.providers.
555 }
556 getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured
limpbizkit2d633cd2008-12-09 06:26:00 +0000557 }
558 }
559
560 /**
561 * Creates a child injector that binds the args, and returns the binding for the method's result.
562 */
sberlin141f8002010-03-25 03:37:35 +0000563 public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args, final AssistData data) {
limpbizkit2d633cd2008-12-09 06:26:00 +0000564 checkState(injector != null,
565 "Factories.create() factories cannot be used until they're initialized by Guice.");
566
sberlin141f8002010-03-25 03:37:35 +0000567 final Key<?> returnType = data.returnType;
limpbizkit2d633cd2008-12-09 06:26:00 +0000568
limpbizkitc1e65da2009-08-19 18:18:16 +0000569 // We ignore any pre-existing binding annotation.
570 final Key<?> assistedReturnType = Key.get(returnType.getTypeLiteral(), Assisted.class);
571
limpbizkit2d633cd2008-12-09 06:26:00 +0000572 Module assistedModule = new AbstractModule() {
sberlinb5a75ed2010-07-31 16:08:27 +0000573 @Override @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
limpbizkit2d633cd2008-12-09 06:26:00 +0000574 protected void configure() {
575 Binder binder = binder().withSource(method);
576
577 int p = 0;
sberlin141f8002010-03-25 03:37:35 +0000578 if(!data.optimized) {
579 for (Key<?> paramKey : data.paramTypes) {
580 // Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
581 binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
582 }
583 } else {
584 for (Key<?> paramKey : data.paramTypes) {
585 // Bind to our ThreadLocalProviders.
586 binder.bind((Key) paramKey).toProvider(data.providers.get(p++));
587 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000588 }
sberlin4b919552010-10-17 01:28:06 +0000589
sberlin@gmail.com88f9a142010-07-17 16:39:00 +0000590 Constructor constructor = data.constructor;
591 // Constructor *should* always be non-null here,
592 // but if it isn't, we'll end up throwing a fairly good error
593 // message for the user.
594 if(constructor != null) {
sberlin23cf2d42011-01-29 20:42:22 +0000595 binder.bind(assistedReturnType)
596 .toConstructor(constructor, (TypeLiteral)data.implementationType)
597 .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
limpbizkit2d633cd2008-12-09 06:26:00 +0000598 }
599 }
600 };
601
602 Injector forCreate = injector.createChildInjector(assistedModule);
sberlin141f8002010-03-25 03:37:35 +0000603 Binding binding = forCreate.getBinding(assistedReturnType);
604 // If we have providers cached in data, cache the binding for future optimizations.
605 if(data.optimized) {
sberlin4b919552010-10-17 01:28:06 +0000606 data.cachedBinding = binding;
sberlin141f8002010-03-25 03:37:35 +0000607 }
608 return binding;
limpbizkit2d633cd2008-12-09 06:26:00 +0000609 }
610
611 /**
612 * When a factory method is invoked, we create a child injector that binds all parameters, then
613 * use that to get an instance of the return type.
614 */
615 public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
616 if (method.getDeclaringClass() == Object.class) {
617 return method.invoke(this, args);
618 }
619
sberlin141f8002010-03-25 03:37:35 +0000620 AssistData data = assistDataByMethod.get(method);
621 Provider<?> provider;
622 if(data.cachedBinding != null) { // Try to get optimized form...
623 provider = data.cachedBinding.getProvider();
624 } else {
625 provider = getBindingFromNewInjector(method, args, data).getProvider();
626 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000627 try {
sberlin141f8002010-03-25 03:37:35 +0000628 int p = 0;
629 for(ThreadLocalProvider tlp : data.providers) {
630 tlp.set(args[p++]);
631 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000632 return provider.get();
633 } catch (ProvisionException e) {
634 // if this is an exception declared by the factory method, throw it as-is
635 if (e.getErrorMessages().size() == 1) {
636 Message onlyError = getOnlyElement(e.getErrorMessages());
637 Throwable cause = onlyError.getCause();
638 if (cause != null && canRethrow(method, cause)) {
639 throw cause;
640 }
641 }
642 throw e;
sberlin141f8002010-03-25 03:37:35 +0000643 } finally {
644 for(ThreadLocalProvider tlp : data.providers) {
645 tlp.remove();
646 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000647 }
648 }
649
650 @Override public String toString() {
limpbizkitc1e65da2009-08-19 18:18:16 +0000651 return factory.getClass().getInterfaces()[0].getName();
limpbizkit2d633cd2008-12-09 06:26:00 +0000652 }
653
654 @Override public boolean equals(Object o) {
655 return o == this || o == factory;
656 }
657
658 /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
659 static boolean canRethrow(Method invoked, Throwable thrown) {
660 if (thrown instanceof Error || thrown instanceof RuntimeException) {
661 return true;
662 }
663
664 for (Class<?> declared : invoked.getExceptionTypes()) {
665 if (declared.isInstance(thrown)) {
666 return true;
667 }
668 }
669
670 return false;
671 }
sberlin4b919552010-10-17 01:28:06 +0000672
sberlin141f8002010-03-25 03:37:35 +0000673 // not <T> because we'll never know and this is easier than suppressing warnings.
sberlin4b919552010-10-17 01:28:06 +0000674 private static class ThreadLocalProvider extends ThreadLocal<Object> implements Provider<Object> {
sberlin141f8002010-03-25 03:37:35 +0000675 @Override
676 protected Object initialValue() {
677 throw new IllegalStateException(
678 "Cannot use optimized @Assisted provider outside the scope of the constructor."
679 + " (This should never happen. If it does, please report it.)");
680 }
681 }
limpbizkit2d633cd2008-12-09 06:26:00 +0000682}