com.google.inject.internal.ProviderMethod Maven / Gradle / Ivy
/**
* 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.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.Provider;
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.util.List;
import java.util.Set;
/**
* A provider that invokes a method and returns its result.
*
* @author [email protected] (Jesse Wilson)
*/
public class ProviderMethod implements ProviderWithExtensionVisitor, HasDependencies,
ProvidesMethodBinding {
private final Key key;
private final Class extends Annotation> scopeAnnotation;
private final Object instance;
private final Method method;
private final ImmutableSet> dependencies;
private final List> parameterProviders;
private final boolean exposed;
/**
* @param method the method to invoke. It's return type must be the same type as {@code key}.
*/
ProviderMethod(Key key, Method method, Object instance,
ImmutableSet> dependencies, List> parameterProviders,
Class extends Annotation> scopeAnnotation) {
this.key = key;
this.scopeAnnotation = scopeAnnotation;
this.instance = instance;
this.dependencies = dependencies;
this.method = method;
this.parameterProviders = parameterProviders;
this.exposed = method.isAnnotationPresent(Exposed.class);
method.setAccessible(true);
}
public Key getKey() {
return key;
}
public Method getMethod() {
return method;
}
// exposed for GIN
public Object getInstance() {
return instance;
}
public Object getEnclosingInstance() {
return instance;
}
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) {
// the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a
// misplaced @Exposed, calling this will add an error to the binder's error queue
((PrivateBinder) binder).expose(key);
}
}
public T get() {
Object[] parameters = new Object[parameterProviders.size()];
for (int i = 0; i < parameters.length; i++) {
parameters[i] = parameterProviders.get(i).get();
}
try {
// We know this cast is safe becase T is the method's return type.
@SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
T result = (T) method.invoke(instance, parameters);
return result;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
throw Exceptions.rethrowCause(e);
}
}
public Set> getDependencies() {
return dependencies;
}
@SuppressWarnings("unchecked")
public V acceptExtensionVisitor(BindingTargetVisitor visitor,
ProviderInstanceBinding extends B> binding) {
if (visitor instanceof ProvidesMethodTargetVisitor) {
return ((ProvidesMethodTargetVisitor)visitor).visit(this);
}
return visitor.visit(binding);
}
@Override public String toString() {
return "@Provides " + StackTraceElements.forMember(method).toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ProviderMethod) {
ProviderMethod o = (ProviderMethod)obj;
return method.equals(o.method)
&& instance.equals(o.instance);
} else {
return false;
}
}
@Override
public int hashCode() {
// Avoid calling hashCode on 'instance', which is a user-object
// that might not be expecting it.
// (We need to call equals, so we do. But we can avoid hashCode.)
return Objects.hashCode(method);
}
}