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     * Original code by                                                          *
009     *****************************************************************************/
010    package org.picocontainer.injectors;
011    
012    import org.picocontainer.ComponentMonitor;
013    import org.picocontainer.Parameter;
014    import org.picocontainer.PicoContainer;
015    import org.picocontainer.behaviors.PropertyApplicator;
016    import org.picocontainer.behaviors.Cached;
017    
018    import java.lang.reflect.Method;
019    import java.lang.reflect.Type;
020    import java.lang.reflect.AccessibleObject;
021    import java.lang.reflect.InvocationTargetException;
022    import java.util.List;
023    import java.util.Set;
024    
025    /**
026     * Instantiates components using empty constructors and
027     * <a href="http://picocontainer.org/setter-injection.html">Setter Injection</a>.
028     * For easy setting of primitive properties, also see {@link PropertyApplicator}.
029     * <p/>
030     * <em>
031     * Note that this class doesn't cache instances. If you want caching,
032     * use a {@link Cached} around this one.
033     * </em>
034     * </p>
035     *
036     * @author Aslak Helles&oslash;y
037     * @author J&ouml;rg Schaible
038     * @author Mauro Talevi
039     * @author Paul Hammant
040     */
041    @SuppressWarnings("serial")
042    public class SetterInjector<T> extends IterativeInjector<T> {
043    
044        protected final String prefix;
045        private final boolean optional;
046        private final String notThisOneThough;
047    
048        /**
049         * Constructs a SetterInjector
050         *
051         *
052         * @param componentKey            the search key for this implementation
053         * @param componentImplementation the concrete implementation
054         * @param parameters              the parameters to use for the initialization
055         * @param monitor                 the component monitor used by this addAdapter
056         * @param prefix                  the prefix to use (e.g. 'set')
057         * @param notThisOneThough        a setter name that's not for injecting through
058         * @param optional                not all setters need to be injected
059         * @param useNames @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
060         *                              if the implementation is not a concrete class.
061         * @throws NullPointerException if one of the parameters is <code>null</code>
062         */
063        public SetterInjector(final Object componentKey,
064                              final Class componentImplementation,
065                              Parameter[] parameters,
066                              ComponentMonitor monitor,
067                              String prefix, String notThisOneThough,
068                              boolean optional, boolean useNames) throws  NotConcreteRegistrationException {
069            super(componentKey, componentImplementation, parameters, monitor, useNames);
070            this.prefix = prefix;
071            this.optional = optional;
072            this.notThisOneThough = notThisOneThough != null ? notThisOneThough : "";
073        }
074    
075        protected Object memberInvocationReturn(Object lastReturn, AccessibleObject member, Object instance) {
076            return member != null && ((Method)member).getReturnType()!=void.class ? lastReturn : instance;
077        }
078    
079        @Override
080        protected Object injectIntoMember(AccessibleObject member, Object componentInstance, Object toInject)
081            throws IllegalAccessException, InvocationTargetException {
082            return ((Method)member).invoke(componentInstance, toInject);
083        }
084    
085        @Override
086        protected boolean isInjectorMethod(Method method) {
087            String methodName = method.getName();
088            return methodName.length() >= getInjectorPrefix().length() + 1 // long enough
089                    && methodName.startsWith(getInjectorPrefix())
090                    && !methodName.equals(notThisOneThough)
091                    && Character.isUpperCase(methodName.charAt(getInjectorPrefix().length()));        
092        }
093    
094        protected String getInjectorPrefix() {
095            return prefix;
096        }
097    
098        @Override
099        public String getDescriptor() {
100            return "SetterInjector-"; 
101        }
102    
103        @Override
104        protected void unsatisfiedDependencies(PicoContainer container, Set<Type> unsatisfiableDependencyTypes, List<AccessibleObject> unsatisfiableDependencyMembers) {
105            if (!optional) {
106                throw new UnsatisfiableDependenciesException(this.getComponentImplementation().getName() + " has unsatisfied dependencies " + unsatisfiableDependencyTypes
107                        + " for members " + unsatisfiableDependencyMembers + " from " + container);
108            }
109        }
110    
111    }