    001    /*****************************************************************************
    002     * Copyright (c) PicoContainer Organization. All rights reserved.            *
    003     * ------------------------------------------------------------------------- *
    004     * The software in this package is published under the terms of the BSD      *
    005     * style license a copy of which has been included with this distribution in *
    006     * the LICENSE.txt file.                                                     *
    007     *                                                                           *
    008     *****************************************************************************/
    009    package org.picocontainer.injectors;
    010
    011    import org.picocontainer.ComponentMonitor;
    012    import org.picocontainer.LifecycleStrategy;
    013    import org.picocontainer.Parameter;
    014    import org.picocontainer.PicoContainer;
    015    import org.picocontainer.PicoCompositionException;
    016    import org.picocontainer.ComponentAdapter;
    017    import org.picocontainer.annotations.Bind;
    018
    019    import java.lang.annotation.Annotation;
    020    import java.lang.reflect.AccessibleObject;
    021    import java.lang.reflect.Type;
    022
    023    import com.thoughtworks.paranamer.CachingParanamer;
    024    import com.thoughtworks.paranamer.AdaptiveParanamer;
    025    import com.thoughtworks.paranamer.Paranamer;
    026
    027    /**
    028     * Injection will happen in a single member function on the component.
    029     *
    030     * @author Paul Hammant
    031     *
    032     */
    033    public abstract class SingleMemberInjector<T> extends AbstractInjector<T> {
    034
    035        private transient Paranamer paranamer;
    036
    037        public SingleMemberInjector(Object componentKey,
    038                                    Class componentImplementation,
    039                                    Parameter[] parameters,
    040                                    ComponentMonitor monitor,
    041                                    LifecycleStrategy lifecycleStrategy, boolean useNames) {
    042            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
    043        }
    044
    045        protected Paranamer getParanamer() {
    046            if (paranamer == null) {
    047                paranamer = new CachingParanamer(new AdaptiveParanamer());
    048            }
    049            return paranamer;
    050        }
    051
    052        @SuppressWarnings("unchecked")
    053        protected Object[] getMemberArguments(PicoContainer container, final AccessibleObject member, final Type[] parameterTypes, final Annotation[] bindings) {
    054            boxParameters(parameterTypes);
    055            Object[] result = new Object[parameterTypes.length];
    056            final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
    057
    058            for (int i = 0; i < currentParameters.length; i++) {
    059                result[i] = getParameter(container, member, i, parameterTypes[i], bindings[i], currentParameters[i], null);
    060            }
    061
    062            return result;
    063        }
    064
    065        protected void boxParameters(Type[] parameterTypes) {
    066            for (int i = 0; i < parameterTypes.length; i++) {
    067                parameterTypes[i] = box(parameterTypes[i]);
    068            }
    069        }
    070
    071        protected Object getParameter(PicoContainer container, AccessibleObject member, int i, Type parameterType, Annotation binding, Parameter currentParameter, ComponentAdapter<?> injecteeAdapter) {
    072            ParameterNameBinding expectedNameBinding = new ParameterNameBinding(getParanamer(), member, i);
    073            Object result = currentParameter.resolve(container, this, injecteeAdapter, parameterType, expectedNameBinding, useNames(), binding).resolveInstance();
    074            nullCheck(member, i, expectedNameBinding, result);
    075            return result;
    076        }
    077
    078        protected void nullCheck(AccessibleObject member, int i, ParameterNameBinding expectedNameBinding, Object result) {
    079            if (result == null && !isNullParamAllowed(member, i)) {
    080                throw new ParameterCannotBeNullException(i, member, expectedNameBinding.getName());
    081            }
    082        }
    083
    084        protected boolean isNullParamAllowed(AccessibleObject member, int i) {
    085            return false;
    086        }
    087
    088        protected Annotation[] getBindings(Annotation[][] annotationss) {
    089            Annotation[] retVal = new Annotation[annotationss.length];
    090            for (int i = 0; i < annotationss.length; i++) {
    091                Annotation[] annotations = annotationss[i];
    092                for (Annotation annotation : annotations) {
    093                    if (annotation.annotationType().getAnnotation(Bind.class) != null) {
    094                        retVal[i] = annotation;
    095                        break;
    096                    }
    097                }
    098            }
    099            return retVal;
    100        }
    101
    102        public static class ParameterCannotBeNullException extends PicoCompositionException {
    103            private final String name;
    104            private ParameterCannotBeNullException(int ix, AccessibleObject member, String name) {
    105                super("Parameter " + ix + " of '" + member + "' named '" + name + "' cannot be null");
    106                this.name = name;
    107            }
    108            public String getParameterName() {
    109                return name;
    110            }
    111        }
    112
    113    }