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 *****************************************************************************/
010package org.picocontainer.injectors;
011
012import org.picocontainer.ComponentMonitor;
013import org.picocontainer.Parameter;
014import org.picocontainer.PicoContainer;
015import org.picocontainer.behaviors.PropertyApplicator;
016import org.picocontainer.behaviors.Cached;
017
018import java.lang.reflect.Method;
019import java.lang.reflect.Type;
020import java.lang.reflect.AccessibleObject;
021import java.lang.reflect.InvocationTargetException;
022import java.util.List;
023import 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")
042public 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}