Skip to content

Instantly share code, notes, and snippets.

@cessationoftime
Created September 11, 2011 23:32
Show Gist options
  • Save cessationoftime/1210287 to your computer and use it in GitHub Desktop.
Save cessationoftime/1210287 to your computer and use it in GitHub Desktop.
Guice FactoryProviders for RoboGuice 1.2
/**
* 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.assistedinject;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import java.lang.annotation.Annotation;
/**
* Provides a factory that combines the caller's arguments with injector-supplied values to
* construct objects.
*
* <h3>Defining a factory</h3>
* Create an interface whose methods return the constructed type, or any of its supertypes. The
* method's parameters are the arguments required to build the constructed type.
*
* <pre>public interface PaymentFactory {
* Payment create(Date startDate, Money amount);
* }</pre>
*
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
* or <i>newPayment</i>.
*
* <h3>Creating a type that accepts factory parameters</h3>
* {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject
* Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor
* should have parameters that match each of the factory method's parameters. Each factory-supplied
* parameter requires an {@literal @}{@link Assisted} annotation. This serves to document that the
* parameter is not bound by your application's modules.
*
* <pre>public class RealPayment implements Payment {
* {@literal @}Inject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted Date startDate</strong>,
* <strong>{@literal @}Assisted Money amount</strong>) {
* ...
* }
* }</pre>
*
* <h3>Multiple factory methods for the same type</h3>
* If the factory contains many methods that return the same type, you can create multiple
* constructors in your concrete class, each constructor marked with with
* {@literal @}{@link AssistedInject}, in order to match the different parameters types of the
* factory methods.
*
* <pre>public interface PaymentFactory {
* Payment create(Date startDate, Money amount);
* Payment createWithoutDate(Money amount);
* }
*
* public class RealPayment implements Payment {
* {@literal @}AssistedInject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted Date startDate</strong>,
* <strong>{@literal @}Assisted Money amount</strong>) {
* ...
* }
*
* {@literal @}AssistedInject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted Money amount</strong>) {
* ...
* }
* }</pre>
*
* <h3>Configuring simple factories</h3>
* In your {@link Module module}, install a {@code RoboFactoryModuleBuilder} that creates the
* factory:
*
* <pre>install(new RoboFactoryModuleBuilder()
* .implement(Payment.class, RealPayment.class)
* .build(PaymentFactory.class);</pre>
*
* As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
* factory cannot be used until the injector has been initialized.
*
* <h3>Configuring complex factories</h3>
* Factories can create an arbitrary number of objects, one per each method. Each factory
* method can be configured using <code>.implement</code>.
*
* <pre>public interface OrderFactory {
* Payment create(Date startDate, Money amount);
* Shipment create(Customer customer, Item item);
* Receipt create(Payment payment, Shipment shipment);
* }
*
* [...]
*
* install(new RoboFactoryModuleBuilder()
* .implement(Payment.class, RealPayment.class)
* // excluding .implement for Shipment means the implementation class
* // will be 'Shipment' itself, which is legal if it's not an interface.
* .implement(Receipt.class, RealReceipt.class)
* .build(OrderFactory.class);</pre>
* </pre>
*
* <h3>Using the factory</h3>
* Inject your factory into your application classes. When you use the factory, your arguments
* will be combined with values from the injector to construct an instance.
*
* <pre>public class PaymentAction {
* {@literal @}Inject private PaymentFactory paymentFactory;
*
* public void doPayment(Money amount) {
* Payment payment = paymentFactory.create(new Date(), amount);
* payment.apply();
* }
* }</pre>
*
* <h3>Making parameter types distinct</h3>
* The types of the factory method's parameters must be distinct. To use multiple parameters of
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
* parameters. The names must be applied to the factory method's parameters:
*
* <pre>public interface PaymentFactory {
* Payment create(
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
* Money amount);
* } </pre>
*
* ...and to the concrete type's constructor parameters:
*
* <pre>public class RealPayment implements Payment {
* {@literal @}Inject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
* <strong>{@literal @}Assisted</strong> Money amount) {
* ...
* }
* }</pre>
*
* <h3>Values are created by Guice</h3>
* Returned factories use child injectors to create values. The values are eligible for method
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
* returned.
*
* <h3>More configuration options</h3>
* In addition to simply specifying an implementation class for any returned type, factories' return
* values can be automatic or can be configured to use annotations:
* <p/>
* If you just want to return the types specified in the factory, do not configure any
* implementations:
*
* <pre>public interface FruitFactory {
* Apple getApple(Color color);
* }
* ...
* protected void configure() {
* install(new RoboFactoryModuleBuilder().build(FruitFactory.class));
* }</pre>
*
* Note that any type returned by the factory in this manner needs to be an implementation class.
* <p/>
* To return two different implementations for the same interface from your factory, use binding
* annotations on your return types:
*
* <pre>interface CarFactory {
* {@literal @}Named("fast") Car getFastCar(Color color);
* {@literal @}Named("clean") Car getCleanCar(Color color);
* }
* ...
* protected void configure() {
* install(new RoboFactoryModuleBuilder()
* .implement(Car.class, Names.named("fast"), Porsche.class)
* .implement(Car.class, Names.named("clean"), Prius.class)
* .build(CarFactory.class));
* }</pre>
*
* <h3>Implementation limitations</h3>
* As a limitation of the implementation, it is prohibited to declare a factory method that
* accepts a {@code Provider} as one of its arguments.
*
* @since 3.0
* @author [email protected] (Peter Schmitt)
*/
public final class RoboFactoryModuleBuilder {
private final BindingCollector bindings = new BindingCollector();
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source,
TypeLiteral<? extends T> target) {
return implement(Key.get(source), target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source, Annotation annotation,
Class<? extends T> target) {
return implement(source, annotation, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source, Annotation annotation,
TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), annotation, target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
Class<? extends T> target) {
return implement(source, annotation, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
TypeLiteral<? extends T> target) {
return implement(Key.get(source, annotation), target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source,
Class<? extends Annotation> annotationType, Class<? extends T> target) {
return implement(source, annotationType, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Class<T> source,
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), annotationType, target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source,
Class<? extends Annotation> annotationType, Class<? extends T> target) {
return implement(source, annotationType, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(TypeLiteral<T> source,
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
return implement(Key.get(source, annotationType), target);
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <T> RoboFactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) {
bindings.addBinding(source, target);
return this;
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <F> Module build(Class<F> factoryInterface) {
return build(TypeLiteral.get(factoryInterface));
}
/**
* See the factory configuration examples at {@link RoboFactoryModuleBuilder}.
*/
public <F> Module build(TypeLiteral<F> factoryInterface) {
return build(Key.get(factoryInterface));
}
public <F> Module build(final Key<F> factoryInterface) {
return new AbstractModule() {
@Override protected void configure() {
Provider<F> provider = new RoboFactoryProvider2<F>(factoryInterface, bindings);
bind(factoryInterface).toProvider(provider);
}
};
}
}
/**
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject.assistedinject;
import static com.google.inject.internal.Annotations.getKey;
import android.content.Context;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.BytecodeGen;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.Message;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
import roboguice.inject.ContextScope;
/**
* <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
* additional capability.
*
* <p>Provides a factory that combines the caller's arguments with injector-supplied values to
* construct objects.
*
* <h3>Defining a factory</h3>
* Create an interface whose methods return the constructed type, or any of its supertypes. The
* method's parameters are the arguments required to build the constructed type.
* <pre>public interface PaymentFactory {
* Payment create(Date startDate, Money amount);
* }</pre>
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
* or <i>newPayment</i>.
*
* <h3>Creating a type that accepts factory parameters</h3>
* {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
* constructor. In addition to injector-supplied parameters, the constructor should have
* parameters that match each of the factory method's parameters. Each factory-supplied parameter
* requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
* is not bound by your application's modules.
* <pre>public class RealPayment implements Payment {
* {@literal @}Inject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted Date startDate</strong>,
* <strong>{@literal @}Assisted Money amount</strong>) {
* ...
* }
* }</pre>
* Any parameter that permits a null value should also be annotated {@code @Nullable}.
*
* <h3>Configuring factories</h3>
* In your {@link com.google.inject.Module module}, bind the factory interface to the returned
* factory:
* <pre>bind(PaymentFactory.class).toProvider(
* RoboFactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
* As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
* factory cannot be used until the injector has been initialized.
*
* <h3>Using the factory</h3>
* Inject your factory into your application classes. When you use the factory, your arguments
* will be combined with values from the injector to construct an instance.
* <pre>public class PaymentAction {
* {@literal @}Inject private PaymentFactory paymentFactory;
*
* public void doPayment(Money amount) {
* Payment payment = paymentFactory.create(new Date(), amount);
* payment.apply();
* }
* }</pre>
*
* <h3>Making parameter types distinct</h3>
* The types of the factory method's parameters must be distinct. To use multiple parameters of
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
* parameters. The names must be applied to the factory method's parameters:
*
* <pre>public interface PaymentFactory {
* Payment create(
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
* Money amount);
* } </pre>
* ...and to the concrete type's constructor parameters:
* <pre>public class RealPayment implements Payment {
* {@literal @}Inject
* public RealPayment(
* CreditService creditService,
* AuthService authService,
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
* <strong>{@literal @}Assisted</strong> Money amount) {
* ...
* }
* }</pre>
*
* <h3>Values are created by Guice</h3>
* Returned factories use child injectors to create values. The values are eligible for method
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
* returned.
*
* <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
* Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
* {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
*
* <p>Instead of matching factory method arguments to constructor parameters using their names, the
* <strong>parameters are matched by their order</strong>. The first factory method argument is
* used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no
* effect.
*
* <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
* method interception. They do receive post-construction member injection.
*
* @param <F> The factory interface
*
* @author [email protected] (Jerome Mourits)
* @author [email protected] (Jesse Wilson)
* @author [email protected] (Daniel Martin)
*
* @deprecated use {@link FactoryModuleBuilder} instead.
*/
@Deprecated
public class RoboFactoryProvider<F> implements Provider<F>, HasDependencies {
/*
* This class implements the old @AssistedInject implementation that manually matches constructors
* to factory methods. The new child injector implementation lives in RoboFactoryProvider2.
*/
private Injector injector;
private final TypeLiteral<F> factoryType;
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType){
return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
}
public static <F> Provider<F> newFactory(
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
= createMethodMapping(factoryType, implementationType);
if (!factoryMethodToConstructor.isEmpty()) {
return new RoboFactoryProvider<F>(factoryType, factoryMethodToConstructor);
} else {
BindingCollector collector = new BindingCollector();
// Preserving backwards-compatibility: Map all return types in a factory
// interface to the passed implementation type.
Errors errors = new Errors();
Key<?> implementationKey = Key.get(implementationType);
if (implementationType != null) {
try {
for (Method method : factoryType.getRawType().getMethods()) {
Key<?> returnType = getKey(factoryType.getReturnType(method), method,
method.getAnnotations(), errors);
if (!implementationKey.equals(returnType)) {
collector.addBinding(returnType, implementationType);
}
}
} catch (ErrorsException e) {
throw new ConfigurationException(e.getErrors().getMessages());
}
}
return new RoboFactoryProvider2<F>(Key.get(factoryType), collector);
}
}
private RoboFactoryProvider(TypeLiteral<F> factoryType,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
this.factoryType = factoryType;
this.factoryMethodToConstructor = factoryMethodToConstructor;
checkDeclaredExceptionsMatch();
}
@Inject
void setInjectorAndCheckUnboundParametersAreInjectable(Injector injector) {
this.injector = injector;
for (AssistedConstructor<?> c : factoryMethodToConstructor.values()) {
for (Parameter p : c.getAllParameters()) {
if(!p.isProvidedByFactory() && !paramCanBeInjected(p, injector)) {
// this is lame - we're not using the proper mechanism to add an
// error to the injector. Throughout this class we throw exceptions
// to add errors, which isn't really the best way in Guice
throw newConfigurationException("Parameter of type '%s' is not injectable or annotated "
+ "with @Assisted for Constructor '%s'", p, c);
}
}
}
}
private void checkDeclaredExceptionsMatch() {
for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) {
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
if (!isConstructorExceptionCompatibleWithFactoryExeception(
constructorException, entry.getKey().getExceptionTypes())) {
throw newConfigurationException("Constructor %s declares an exception, but no compatible "
+ "exception is thrown by the factory method %s", entry.getValue(), entry.getKey());
}
}
}
}
private boolean isConstructorExceptionCompatibleWithFactoryExeception(
Class<?> constructorException, Class<?>[] factoryExceptions) {
for (Class<?> factoryException : factoryExceptions) {
if (factoryException.isAssignableFrom(constructorException)) {
return true;
}
}
return false;
}
private boolean paramCanBeInjected(Parameter parameter, Injector injector) {
return parameter.isBound(injector);
}
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
List<AssistedConstructor<?>> constructors = Lists.newArrayList();
for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
if (constructor.getAnnotation(AssistedInject.class) != null) {
@SuppressWarnings("unchecked") // the constructor type and implementation type agree
AssistedConstructor assistedConstructor = new AssistedConstructor(
constructor, implementationType.getParameterTypes(constructor));
constructors.add(assistedConstructor);
}
}
if (constructors.isEmpty()) {
return ImmutableMap.of();
}
Method[] factoryMethods = factoryType.getRawType().getMethods();
if (constructors.size() != factoryMethods.length) {
throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
+ "constructors, factory %s has %s creation methods", implementationType,
constructors.size(), factoryType, factoryMethods.length);
}
Map<ParameterListKey, AssistedConstructor> paramsToConstructor = Maps.newHashMap();
for (AssistedConstructor c : constructors) {
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
throw new RuntimeException("Duplicate constructor, " + c);
}
paramsToConstructor.put(c.getAssistedParameters(), c);
}
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
for (Method method : factoryMethods) {
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
throw newConfigurationException("Return type of method %s is not assignable from %s",
method, implementationType);
}
List<Type> parameterTypes = Lists.newArrayList();
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
parameterTypes.add(parameterType.getType());
}
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
if (!paramsToConstructor.containsKey(methodParams)) {
throw newConfigurationException("%s has no @AssistInject constructor that takes the "
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
implementationType, methodParams, paramsToConstructor.values());
}
method.getParameterAnnotations();
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation.annotationType() == Assisted.class) {
throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
+ "is incompatible with the deprecated @AssistedInject annotation. Please replace "
+ "@AssistedInject with @Inject on the %s constructor.",
method, implementationType);
}
}
}
AssistedConstructor matchingConstructor = paramsToConstructor.remove(methodParams);
result.put(method, matchingConstructor);
}
return result;
}
public Set<Dependency<?>> getDependencies() {
List<Dependency<?>> dependencies = Lists.newArrayList();
for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
for (Parameter parameter : constructor.getAllParameters()) {
if (!parameter.isProvidedByFactory()) {
dependencies.add(Dependency.get(parameter.getPrimaryBindingKey()));
}
}
}
return ImmutableSet.copyOf(dependencies);
}
public F get() {
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
Context context = null;
if (Context.class.isInstance(creationArgs[0])) {
context = (Context)creationArgs[0];
} else {
String classNames = "";
for(Object o : creationArgs) {
if (o==null) classNames = "null " + classNames;
else classNames = o.getClass().getName() + " " + classNames;
}
throw new RuntimeException("RoboFactory for: " + method.getReturnType().getName() + " must be supplied with android.content.Context to the factory creation methods " + ", args: " + classNames);
}
ContextScope scope = injector.getInstance(ContextScope.class);
// pass methods from Object.class to the proxy
if (method.getDeclaringClass().equals(Object.class)) {
synchronized (ContextScope.class) {
scope.enter(context);
try {
return method.invoke(this, creationArgs);
} finally {
scope.exit(context);
}
}
}
AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
Object[] constructorArgs = gatherArgsForConstructor(constructor, creationArgs);
Object objectToReturn = constructor.newInstance(constructorArgs);
injector.injectMembers(objectToReturn);
return objectToReturn;
}
public Object[] gatherArgsForConstructor(
AssistedConstructor<?> constructor,
Object[] factoryArgs) {
int numParams = constructor.getAllParameters().size();
int argPosition = 0;
Object[] result = new Object[numParams];
for (int i = 0; i < numParams; i++) {
Parameter parameter = constructor.getAllParameters().get(i);
if (parameter.isProvidedByFactory()) {
result[i] = factoryArgs[argPosition];
argPosition++;
} else {
result[i] = parameter.getValue(injector);
}
}
return result;
}
};
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
Class<F> factoryRawType = (Class) factoryType.getRawType();
return factoryRawType.cast(Proxy.newProxyInstance(BytecodeGen.getClassLoader(factoryRawType),
new Class[] { factoryRawType }, invocationHandler));
}
private static ConfigurationException newConfigurationException(String format, Object... args) {
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
}
}
/**
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject.assistedinject;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import android.content.Context;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.BytecodeGen;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import roboguice.inject.ContextScope;
/**
* The newer implementation of factory provider. This implementation uses a child injector to
* create values.
*
* @author [email protected] (Jesse Wilson)
* @author [email protected] (Daniel Martin)
* @author [email protected] (Peter Schmitt)
* @author [email protected] (Sam Berlin)
*/
final class RoboFactoryProvider2 <F> implements InvocationHandler,
ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> {
/** if a factory method parameter isn't annotated, it gets this annotation. */
static final Assisted DEFAULT_ANNOTATION = new Assisted() {
public String value() {
return "";
}
public Class<? extends Annotation> annotationType() {
return Assisted.class;
}
@Override public boolean equals(Object o) {
return o instanceof Assisted
&& ((Assisted) o).value().equals("");
}
@Override public int hashCode() {
return 127 * "value".hashCode() ^ "".hashCode();
}
@Override public String toString() {
return "@" + Assisted.class.getName() + "(value=)";
}
};
/** All the data necessary to perform an assisted inject. */
private static class AssistData implements AssistedMethod {
/** the constructor the implementation is constructed with. */
final Constructor<?> constructor;
/** the return type in the factory method that the constructor is bound to. */
final Key<?> returnType;
/** the parameters in the factory method associated with this data. */
final ImmutableList<Key<?>> paramTypes;
/** the type of the implementation constructed */
final TypeLiteral<?> implementationType;
/** All non-assisted dependencies required by this method. */
final Set<Dependency<?>> dependencies;
/** The factory method associated with this data*/
final Method factoryMethod;
/** true if {@link #validForOptimizedAssistedInject} returned true. */
final boolean optimized;
/** the list of optimized providers, empty if not optimized. */
final List<ThreadLocalProvider> providers;
/** used to perform optimized factory creations. */
volatile Binding<?> cachedBinding; // TODO: volatile necessary?
AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes,
TypeLiteral<?> implementationType, Method factoryMethod,
Set<Dependency<?>> dependencies,
boolean optimized, List<ThreadLocalProvider> providers) {
this.constructor = constructor;
this.returnType = returnType;
this.paramTypes = paramTypes;
this.implementationType = implementationType;
this.factoryMethod = factoryMethod;
this.dependencies = dependencies;
this.optimized = optimized;
this.providers = providers;
}
@Override
public String toString() {
return Objects.toStringHelper(getClass())
.add("ctor", constructor)
.add("return type", returnType)
.add("param type", paramTypes)
.add("implementation type", implementationType)
.add("dependencies", dependencies)
.add("factory method", factoryMethod)
.add("optimized", optimized)
.add("providers", providers)
.add("cached binding", cachedBinding)
.toString();
}
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
public Method getFactoryMethod() {
return factoryMethod;
}
public Constructor<?> getImplementationConstructor() {
return constructor;
}
public TypeLiteral<?> getImplementationType() {
return implementationType;
}
}
/** Mapping from method to the data about how the method will be assisted. */
private final ImmutableMap<Method, AssistData> assistDataByMethod;
/** the hosting injector, or null if we haven't been initialized yet */
private Injector injector;
/** the factory interface, implemented and provided */
private final F factory;
/** The key that this is bound to. */
private final Key<F> factoryKey;
/**
* @param factoryType a Java interface that defines one or more create methods.
* @param collector binding configuration that maps method return types to
* implementation types.
*/
RoboFactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
this.factoryKey = factoryKey;
TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
Errors errors = new Errors();
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
Class<F> factoryRawType = (Class) factoryType.getRawType();
try {
if(!factoryRawType.isInterface()) {
throw errors.addMessage("%s must be an interface.", factoryRawType).toException();
}
ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
// TODO: also grab methods from superinterfaces
for (Method method : factoryRawType.getMethods()) {
TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
Key<?> returnType;
try {
returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
} catch(ConfigurationException ce) {
// If this was an error due to returnTypeLiteral not being specified, rephrase
// it as our factory not being specified, so it makes more sense to users.
if(isTypeNotSpecified(returnTypeLiteral, ce)) {
throw errors.keyNotFullySpecified(TypeLiteral.get(factoryRawType)).toException();
} else {
throw ce;
}
}
validateFactoryReturnType(errors, returnType.getTypeLiteral().getRawType(), factoryRawType);
List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
Annotation[][] paramAnnotations = method.getParameterAnnotations();
int p = 0;
List<Key<?>> keys = Lists.newArrayList();
for (TypeLiteral<?> param : params) {
Key<?> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors);
Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
if (underlylingType.equals(Provider.class)
|| underlylingType.equals(javax.inject.Provider.class)) {
errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject."
+ "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
p, paramKey, method);
}
keys.add(assistKey(method, paramKey, errors));
}
ImmutableList<Key<?>> immutableParamList = ImmutableList.copyOf(keys);
// try to match up the method to the constructor
TypeLiteral<?> implementation = collector.getBindings().get(returnType);
if(implementation == null) {
implementation = returnType.getTypeLiteral();
}
InjectionPoint ctorInjectionPoint;
try {
ctorInjectionPoint =
findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList);
} catch(ErrorsException ee) {
errors.merge(ee.getErrors());
continue;
}
Constructor<?> constructor = (Constructor)ctorInjectionPoint.getMember();
List<ThreadLocalProvider> providers = Collections.emptyList();
Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
boolean optimized = false;
// Now go through all dependencies of the implementation and see if it is OK to
// use an optimized form of assistedinject2. The optimized form requires that
// all injections directly inject the object itself (and not a Provider of the object,
// or an Injector), because it caches a single child injector and mutates the Provider
// of the arguments in a ThreadLocal.
if(isValidForOptimizedAssistedInject(deps)) {
ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
for(int i = 0; i < params.size(); i++) {
providerListBuilder.add(new ThreadLocalProvider());
}
providers = providerListBuilder.build();
optimized = true;
}
assistDataBuilder.put(method,
new AssistData(constructor, returnType, immutableParamList, implementation,
method, removeAssistedDeps(deps), optimized, providers));
}
// If we generated any errors (from finding matching constructors, for instance), throw an exception.
if(errors.hasErrors()) {
throw errors.toException();
}
assistDataByMethod = assistDataBuilder.build();
} catch (ErrorsException e) {
throw new ConfigurationException(e.getErrors().getMessages());
}
factory = factoryRawType.cast(Proxy.newProxyInstance(BytecodeGen.getClassLoader(factoryRawType),
new Class[] { factoryRawType }, this));
}
public F get() {
return factory;
}
public Set<Dependency<?>> getDependencies() {
Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>();
for(AssistData data : assistDataByMethod.values()) {
combinedDeps.addAll(data.dependencies);
}
return ImmutableSet.copyOf(combinedDeps);
}
public Key<F> getKey() {
return factoryKey;
}
// safe cast because values are typed to AssistedData, which is an AssistedMethod
@SuppressWarnings("unchecked")
public Collection<AssistedMethod> getAssistedMethods() {
return (Collection)assistDataByMethod.values();
}
@SuppressWarnings("unchecked")
public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor,
ProviderInstanceBinding<? extends T> binding) {
if (visitor instanceof AssistedInjectTargetVisitor) {
return ((AssistedInjectTargetVisitor<T, V>)visitor).visit((AssistedInjectBinding<T>)this);
}
return visitor.visit(binding);
}
private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
if (Modifier.isPublic(factoryType.getModifiers())
&& !Modifier.isPublic(returnType.getModifiers())) {
errors.addMessage("%s is public, but has a method that returns a non-public type: %s. "
+ "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
+ "Please either make the factory non-public or the return type public.",
factoryType, returnType);
}
}
/**
* Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
* specified.
*/
private boolean isTypeNotSpecified(TypeLiteral typeLiteral, ConfigurationException ce) {
Collection<Message> messages = ce.getErrorMessages();
if (messages.size() == 1) {
Message msg = Iterables.getOnlyElement(
new Errors().keyNotFullySpecified(typeLiteral).getMessages());
return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
} else {
return false;
}
}
/**
* Finds a constructor suitable for the method. If the implementation contained any constructors
* marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
* match the parameters (in any order) listed in the method. Otherwise, if no
* {@link AssistedInject} constructors exist, this will default to looking for an
* {@literal @}{@link Inject} constructor.
*/
private InjectionPoint findMatchingConstructorInjectionPoint(
Method method, Key<?> returnType, TypeLiteral<?> implementation, List<Key<?>> paramList)
throws ErrorsException {
Errors errors = new Errors(method);
if(returnType.getTypeLiteral().equals(implementation)) {
errors = errors.withSource(implementation);
} else {
errors = errors.withSource(returnType).withSource(implementation);
}
Class<?> rawType = implementation.getRawType();
if (Modifier.isInterface(rawType.getModifiers())) {
errors.addMessage(
"%s is an interface, not a concrete class. Unable to create AssistedInject factory.",
implementation);
throw errors.toException();
} else if (Modifier.isAbstract(rawType.getModifiers())) {
errors.addMessage(
"%s is abstract, not a concrete class. Unable to create AssistedInject factory.",
implementation);
throw errors.toException();
} else if (isInnerClass(rawType)) {
errors.cannotInjectInnerClass(rawType);
throw errors.toException();
}
Constructor<?> matchingConstructor = null;
boolean anyAssistedInjectConstructors = false;
// Look for AssistedInject constructors...
for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
if (constructor.isAnnotationPresent(AssistedInject.class)) {
anyAssistedInjectConstructors = true;
if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
if (matchingConstructor != null) {
errors
.addMessage(
"%s has more than one constructor annotated with @AssistedInject"
+ " that matches the parameters in method %s. Unable to create AssistedInject factory.",
implementation, method);
throw errors.toException();
} else {
matchingConstructor = constructor;
}
}
}
}
if(!anyAssistedInjectConstructors) {
// If none existed, use @Inject.
try {
return InjectionPoint.forConstructorOf(implementation);
} catch(ConfigurationException e) {
errors.merge(e.getErrorMessages());
throw errors.toException();
}
} else {
// Otherwise, use it or fail with a good error message.
if(matchingConstructor != null) {
// safe because we got the constructor from this implementation.
@SuppressWarnings("unchecked")
InjectionPoint ip = InjectionPoint.forConstructor(
(Constructor)matchingConstructor, implementation);
return ip;
} else {
errors.addMessage(
"%s has @AssistedInject constructors, but none of them match the"
+ " parameters in method %s. Unable to create AssistedInject factory.",
implementation, method);
throw errors.toException();
}
}
}
/**
* Matching logic for constructors annotated with AssistedInject.
* This returns true if and only if all @Assisted parameters in the
* constructor exactly match (in any order) all @Assisted parameters
* the method's parameter.
*/
private boolean constructorHasMatchingParams(TypeLiteral<?> type,
Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
throws ErrorsException {
List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
int p = 0;
List<Key<?>> constructorKeys = Lists.newArrayList();
for (TypeLiteral<?> param : params) {
Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++],
errors);
constructorKeys.add(paramKey);
}
// Require that every key exist in the constructor to match up exactly.
for (Key<?> key : paramList) {
// If it didn't exist in the constructor set, we can't use it.
if (!constructorKeys.remove(key)) {
return false;
}
}
// If any keys remain and their annotation is Assisted, we can't use it.
for (Key<?> key : constructorKeys) {
if (key.getAnnotationType() == Assisted.class) {
return false;
}
}
// All @Assisted params match up to the method's parameters.
return true;
}
/** Calculates all dependencies required by the implementation and constructor. */
private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
builder.addAll(ctorPoint.getDependencies());
if (!implementation.getRawType().isInterface()) {
for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
builder.addAll(ip.getDependencies());
}
}
return builder.build();
}
/** Return all non-assisted dependencies. */
private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
for(Dependency<?> dep : deps) {
Class annotationType = dep.getKey().getAnnotationType();
if (annotationType == null || !annotationType.equals(Assisted.class)) {
builder.add(dep);
}
}
return builder.build();
}
/**
* Returns true if all dependencies are suitable for the optimized version of AssistedInject. The
* optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if
* the assisted bindings are immediately provided. This looks for hints that the values may be
* lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
*/
private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies) {
for (Dependency<?> dep : dependencies) {
if (isInjectorOrAssistedProvider(dep)) {
return false;
}
}
return true;
}
/**
* Returns true if the dependency is for {@link Injector} or if the dependency
* is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}.
*/
private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
Class annotationType = dependency.getKey().getAnnotationType();
if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider...
return true;
}
} else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector...
return true;
}
return false;
}
/**
* Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
* This fails if another binding annotation is clobbered in the process. If the key already has
* the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
*/
private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
if (key.getAnnotationType() == null) {
return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
} else if (key.getAnnotationType() == Assisted.class) {
return key;
} else {
errors.withSource(method).addMessage(
"Only @Assisted is allowed for factory parameters, but found @%s",
key.getAnnotationType());
throw errors.toException();
}
}
/**
* At injector-creation time, we initialize the invocation handler. At this time we make sure
* all factory methods will be able to build the target types.
*/
@Inject @Toolable
void initialize(Injector injector) {
if (this.injector != null) {
throw new ConfigurationException(ImmutableList.of(new Message(RoboFactoryProvider2.class,
"Factories.create() factories may only be used in one Injector!")));
}
this.injector = injector;
for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) {
Method method = entry.getKey();
AssistData data = entry.getValue();
Object[] args;
if(!data.optimized) {
args = new Object[method.getParameterTypes().length];
Arrays.fill(args, "dummy object for validating Factories");
} else {
args = null; // won't be used -- instead will bind to data.providers.
}
getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured
}
}
/**
* Creates a child injector that binds the args, and returns the binding for the method's result.
*/
public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args, final AssistData data) {
checkState(injector != null,
"Factories.create() factories cannot be used until they're initialized by Guice.");
final Key<?> returnType = data.returnType;
// We ignore any pre-existing binding annotation.
final Key<?> assistedReturnType = Key.get(returnType.getTypeLiteral(), Assisted.class);
Module assistedModule = new AbstractModule() {
@Override @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
protected void configure() {
Binder binder = binder().withSource(method);
int p = 0;
if(!data.optimized) {
for (Key<?> paramKey : data.paramTypes) {
// Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
}
} else {
for (Key<?> paramKey : data.paramTypes) {
// Bind to our ThreadLocalProviders.
binder.bind((Key) paramKey).toProvider(data.providers.get(p++));
}
}
Constructor constructor = data.constructor;
// Constructor *should* always be non-null here,
// but if it isn't, we'll end up throwing a fairly good error
// message for the user.
if(constructor != null) {
binder.bind(assistedReturnType)
.toConstructor(constructor, (TypeLiteral)data.implementationType)
.in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
}
}
};
Injector forCreate = injector.createChildInjector(assistedModule);
Binding binding = forCreate.getBinding(assistedReturnType);
// If we have providers cached in data, cache the binding for future optimizations.
if(data.optimized) {
data.cachedBinding = binding;
}
return binding;
}
/**
* When a factory method is invoked, we create a child injector that binds all parameters, then
* use that to get an instance of the return type.
*/
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
Context context = null;
if (Context.class.isInstance(args[0])) {
context = (Context)args[0];
} else {
String classNames = "";
for(Object o : args) {
if (o==null) classNames = "null " + classNames;
else classNames = o.getClass().getName() + " " + classNames;
}
throw new RuntimeException("RoboFactory for: " + method.getReturnType().getName() + " must be have android.content.Context as arg[0], args: " + classNames);
}
ContextScope scope = injector.getInstance(ContextScope.class);
if (method.getDeclaringClass() == Object.class) {
synchronized (ContextScope.class) {
scope.enter(context);
try {
return method.invoke(this, args);
} finally {
scope.exit(context);
}
}
}
AssistData data = assistDataByMethod.get(method);
Provider<?> provider;
if(data.cachedBinding != null) { // Try to get optimized form...
provider = data.cachedBinding.getProvider();
} else {
provider = getBindingFromNewInjector(method, args, data).getProvider();
}
try {
int p = 0;
for(ThreadLocalProvider tlp : data.providers) {
tlp.set(args[p++]);
}
synchronized (ContextScope.class) {
scope.enter(context);
try {
return provider.get();
} finally {
scope.exit(context);
}
}
} catch (ProvisionException e) {
// if this is an exception declared by the factory method, throw it as-is
if (e.getErrorMessages().size() == 1) {
Message onlyError = getOnlyElement(e.getErrorMessages());
Throwable cause = onlyError.getCause();
if (cause != null && canRethrow(method, cause)) {
throw cause;
}
}
throw e;
} finally {
for(ThreadLocalProvider tlp : data.providers) {
tlp.remove();
}
}
}
@Override public String toString() {
return factory.getClass().getInterfaces()[0].getName();
}
@Override public boolean equals(Object o) {
return o == this || o == factory;
}
/** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
static boolean canRethrow(Method invoked, Throwable thrown) {
if (thrown instanceof Error || thrown instanceof RuntimeException) {
return true;
}
for (Class<?> declared : invoked.getExceptionTypes()) {
if (declared.isInstance(thrown)) {
return true;
}
}
return false;
}
public static boolean isInnerClass(Class<?> clazz) {
return !Modifier.isStatic(clazz.getModifiers())
&& clazz.getEnclosingClass() != null;
}
// not <T> because we'll never know and this is easier than suppressing warnings.
private static class ThreadLocalProvider extends ThreadLocal<Object> implements Provider<Object> {
@Override
protected Object initialValue() {
throw new IllegalStateException(
"Cannot use optimized @Assisted provider outside the scope of the constructor."
+ " (This should never happen. If it does, please report it.)");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment