package com.google.inject.internal;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Exposed;
import com.google.inject.Key;
import com.google.inject.PrivateBinder;
import com.google.inject.Provides;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.ProvidesMethodBinding;
import com.google.inject.spi.ProvidesMethodTargetVisitor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
public abstract class ProviderMethod<T> extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
implements HasDependencies, ProvidesMethodBinding<T>, ProviderWithExtensionVisitor<T> {
static <T> ProviderMethod<T> create(
Key<T> key,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
boolean skipFastClassGeneration,
Annotation annotation) {
int modifiers = method.getModifiers();
if (!skipFastClassGeneration) {
try {
net.sf.cglib.reflect.FastClass fc = BytecodeGen.newFastClassForMember(method);
if (fc != null) {
return new FastClassProviderMethod<T>(
key, fc, method, instance, dependencies, scopeAnnotation, annotation);
}
} catch (net.sf.cglib.core.CodeGenerationException e) {
}
}
if (!Modifier.isPublic(modifiers)
|| !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
return new ReflectionProviderMethod<T>(
key, method, instance, dependencies, scopeAnnotation, annotation);
}
protected final Object instance;
protected final Method method;
private final Key<T> key;
private final Class<? extends Annotation> scopeAnnotation;
private final ImmutableSet<Dependency<?>> dependencies;
private final boolean exposed;
private final Annotation annotation;
private SingleParameterInjector<?>[] parameterInjectors;
private ProviderMethod(
Key<T> key,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(InitializationTiming.EAGER);
this.key = key;
this.scopeAnnotation = scopeAnnotation;
this.instance = instance;
this.dependencies = dependencies;
this.method = method;
this.exposed = method.isAnnotationPresent(Exposed.class);
this.annotation = annotation;
}
@Override
public Key<T> getKey() {
return key;
}
@Override
public Method getMethod() {
return method;
}
public Object getInstance() {
return instance;
}
@Override
public Object getEnclosingInstance() {
return instance;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
public void configure(Binder binder) {
binder = binder.withSource(method);
if (scopeAnnotation != null) {
binder.bind(key).toProvider(this).in(scopeAnnotation);
} else {
binder.bind(key).toProvider(this);
}
if (exposed) {
((PrivateBinder) binder).expose(key);
}
}
@Override
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
}
@Override
protected T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
try {
T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
}
return t;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
}
}
abstract T doProvision(Object[] parameters)
throws IllegalAccessException, InvocationTargetException;
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
@SuppressWarnings("unchecked")
public <B, V> V acceptExtensionVisitor(
BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof ProvidesMethodTargetVisitor) {
return ((ProvidesMethodTargetVisitor<T, V>) visitor).visit(this);
}
return visitor.visit(binding);
}
@Override
public String toString() {
String annotationString = annotation.toString();
if (annotation.annotationType() == Provides.class) {
annotationString = "@Provides";
} else if (annotationString.endsWith("()")) {
annotationString = annotationString.substring(0, annotationString.length() - 2);
}
return annotationString + " " + StackTraceElements.forMember(method);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ProviderMethod) {
ProviderMethod<?> o = (ProviderMethod<?>) obj;
return method.equals(o.method)
&& instance.equals(o.instance)
&& annotation.equals(o.annotation);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(method, annotation);
}
private static final class FastClassProviderMethod<T> extends ProviderMethod<T> {
final net.sf.cglib.reflect.FastClass fastClass;
final int methodIndex;
FastClassProviderMethod(
Key<T> key,
net.sf.cglib.reflect.FastClass fc,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(key, method, instance, dependencies, scopeAnnotation, annotation);
this.fastClass = fc;
this.methodIndex = fc.getMethod(method).getIndex();
}
@SuppressWarnings("unchecked")
@Override
public T doProvision(Object[] parameters)
throws IllegalAccessException, InvocationTargetException {
return (T) fastClass.invoke(methodIndex, instance, parameters);
}
}
private static final class ReflectionProviderMethod<T> extends ProviderMethod<T> {
ReflectionProviderMethod(
Key<T> key,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(key, method, instance, dependencies, scopeAnnotation, annotation);
}
@SuppressWarnings("unchecked")
@Override
T doProvision(Object[] parameters) throws IllegalAccessException, InvocationTargetException {
return (T) method.invoke(instance, parameters);
}
}
}