All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.ProviderMethod Maven / Gradle / Ivy

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.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;

/**
 * A provider that invokes a method and returns its result.
 *
 */
public abstract class ProviderMethod
        extends InternalProviderInstanceBindingImpl.CyclicFactory
        implements HasDependencies, ProviderWithExtensionVisitor, ProvidesMethodBinding {

    protected final Object instance;

    protected final Method method;

    private final Key key;

    private final Class scopeAnnotation;

    private final ImmutableSet> dependencies;

    private final boolean exposed;

    private final Annotation annotation;

    /**
     * Set by {@link #initialize(InjectorImpl, Errors)} so it is always available prior to injection.
     */
    private SingleParameterInjector[] parameterInjectors;

    /**
     * @param method the method to invoke. It's return type must be the same type as {@code key}.
     */
    private ProviderMethod(Key key,
                           Method method,
                           Object instance,
                           ImmutableSet> dependencies,
                           Class scopeAnnotation,
                           Annotation annotation) {
        super(InternalProviderInstanceBindingImpl.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;
    }

    /**
     * Creates a {@link ProviderMethod}.
     */
    static  ProviderMethod create(Key key,
                                        Method method,
                                        Object instance,
                                        ImmutableSet> dependencies,
                                        Class scopeAnnotation,
                                        boolean skipFastClassGeneration,
                                        Annotation annotation) {
        int modifiers = method.getModifiers();
        if (!Modifier.isPublic(modifiers) ||
                !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
            method.setAccessible(true);
        }
        return new ReflectionProviderMethod<>(key, method, instance, dependencies, scopeAnnotation, annotation);
    }

    @Override
    public Key getKey() {
        return key;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    // exposed for GIN
    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) {
            // 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);
        }
    }

    @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());
        }
    }

    /**
     * Extension point for our subclasses to implement the provisioning strategy.
     */
    abstract T doProvision(Object[] parameters)
            throws IllegalAccessException, InvocationTargetException;

    @Override
    public Set> getDependencies() {
        return dependencies;
    }

    @Override
    @SuppressWarnings("unchecked")
    public  V acceptExtensionVisitor(BindingTargetVisitor visitor,
                                           ProviderInstanceBinding binding) {
        if (visitor instanceof ProvidesMethodTargetVisitor) {
            return ((ProvidesMethodTargetVisitor) visitor).visit(this);
        }
        return visitor.visit(binding);
    }

    @Override
    public String toString() {
        String annotationString = annotation.toString();
        // Show @Provides w/o the com.google.inject prefix.
        if (annotation.annotationType() == Provides.class) {
            annotationString = "@Provides";
        } else if (annotationString.endsWith("()")) {
            // Remove the common "()" suffix if there are no values.
            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() {
        // 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, annotation);
    }

    /**
     * A {@link ProviderMethod} implementation that invokes the method using normal java reflection.
     */
    private static final class ReflectionProviderMethod extends ProviderMethod {
        ReflectionProviderMethod(Key key,
                                 Method method,
                                 Object instance,
                                 ImmutableSet> dependencies,
                                 Class 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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy