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    }