    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.parameters;
    011
    012    import org.picocontainer.ComponentAdapter;
    013    import org.picocontainer.Parameter;
    014    import org.picocontainer.PicoContainer;
    015    import org.picocontainer.PicoVisitor;
    016    import org.picocontainer.NameBinding;
    017    import org.picocontainer.injectors.AbstractInjector;
    018
    019    import java.lang.annotation.Annotation;
    020    import java.lang.reflect.ParameterizedType;
    021    import java.lang.reflect.Type;
    022
    023
    024    /**
    025     * A ComponentParameter should be used to pass in a particular component as argument to a
    026     * different component's constructor. This is particularly useful in cases where several
    027     * components of the same type have been registered, but with a different key. Passing a
    028     * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
    029     * about what other component to use in the constructor. Collecting parameter types are
    030     * supported for {@link java.lang.reflect.Array},{@link java.util.Collection}and
    031     * {@link java.util.Map}.
    032     *
    033     * @author Jon Tirs&eacute;n
    034     * @author Aslak Helles&oslash;y
    035     * @author J&ouml;rg Schaible
    036     * @author Thomas Heller
    037     */
    038    @SuppressWarnings("serial")
    039    public class ComponentParameter
    040            extends BasicComponentParameter {
    041
    042        /**
    043         * <code>DEFAULT</code> is an instance of ComponentParameter using the default constructor.
    044         */
    045        public static final ComponentParameter DEFAULT = new ComponentParameter();
    046        /**
    047         * Use <code>ARRAY</code> as {@link Parameter}for an Array that must have elements.
    048         */
    049        public static final ComponentParameter ARRAY = new ComponentParameter(false);
    050        /**
    051         * Use <code>ARRAY_ALLOW_EMPTY</code> as {@link Parameter}for an Array that may have no
    052         * elements.
    053         */
    054        public static final ComponentParameter ARRAY_ALLOW_EMPTY = new ComponentParameter(true);
    055
    056        private final Parameter collectionParameter;
    057
    058        /**
    059         * Expect a parameter matching a component of a specific key.
    060         *
    061         * @param componentKey the key of the desired addComponent
    062         */
    063        public ComponentParameter(Object componentKey) {
    064            this(componentKey, null);
    065        }
    066
    067        /**
    068         * Expect any scalar parameter of the appropriate type or an {@link java.lang.reflect.Array}.
    069         */
    070        public ComponentParameter() {
    071            this(false);
    072        }
    073
    074        /**
    075         * Expect any scalar parameter of the appropriate type or an {@link java.lang.reflect.Array}.
    076         * Resolve the parameter even if no compnoent is of the array's component type.
    077         *
    078         * @param emptyCollection <code>true</code> allows an Array to be empty
    079         */
    080        public ComponentParameter(boolean emptyCollection) {
    081            this(null, emptyCollection ? CollectionComponentParameter.ARRAY_ALLOW_EMPTY : CollectionComponentParameter.ARRAY);
    082        }
    083
    084        /**
    085         * Expect any scalar parameter of the appropriate type or the collecting type
    086         * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
    087         * The components in the collection will be of the specified type.
    088         *
    089         * @param componentValueType the component's type (ignored for an Array)
    090         * @param emptyCollection <code>true</code> allows the collection to be empty
    091         */
    092        public ComponentParameter(Class componentValueType, boolean emptyCollection) {
    093            this(null, new CollectionComponentParameter(componentValueType, emptyCollection));
    094        }
    095
    096        /**
    097         * Expect any scalar parameter of the appropriate type or the collecting type
    098         * {@link java.lang.reflect.Array},{@link java.util.Collection}or {@link java.util.Map}.
    099         * The components in the collection will be of the specified type and their adapter's key
    100         * must have a particular type.
    101         *
    102         * @param componentKeyType the component adapter's key type
    103         * @param componentValueType the component's type (ignored for an Array)
    104         * @param emptyCollection <code>true</code> allows the collection to be empty
    105         */
    106        public ComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) {
    107            this(null, new CollectionComponentParameter(componentKeyType, componentValueType, emptyCollection));
    108        }
    109
    110        private ComponentParameter(Object componentKey, Parameter collectionParameter) {
    111            super(componentKey);
    112            this.collectionParameter = collectionParameter;
    113        }
    114
    115        public Object resolveInstance(PicoContainer container,
    116                                      ComponentAdapter<?> adapter,
    117                                      Type expectedType,
    118                                      NameBinding expectedNameBinding,
    119                                      boolean useNames, Annotation binding) {
    120            // type check is done in isResolvable
    121            Object result = null;
    122            if (expectedType instanceof Class) {
    123                result = super.resolveInstance(container, adapter, expectedType, expectedNameBinding, useNames, binding);
    124            } else if (expectedType instanceof ParameterizedType) {
    125                    result = super.resolveInstance(container, adapter, ((ParameterizedType) expectedType).getRawType(), expectedNameBinding, useNames, binding);
    126            }
    127            if (result == null && collectionParameter != null) {
    128                result = collectionParameter.resolveInstance(container, adapter, expectedType, expectedNameBinding,
    129                                                             useNames, binding);
    130            }
    131            return result;
    132        }
    133
    134        public boolean isResolvable(PicoContainer container, ComponentAdapter<?> adapter,
    135                                    Type expectedType, NameBinding expectedNameBinding,
    136                                    boolean useNames, Annotation binding) {
    137            if (!super.isResolvable(container, adapter, expectedType, expectedNameBinding, useNames, binding)) {
    138                if (collectionParameter != null) {
    139                    return collectionParameter.isResolvable(container, adapter, expectedType, expectedNameBinding,
    140                                                            useNames, binding);
    141                }
    142                return false;
    143            }
    144            return true;
    145        }
    146
    147        public void verify(PicoContainer container,
    148                           ComponentAdapter<?> adapter,
    149                           Type expectedType,
    150                           NameBinding expectedNameBinding,
    151                           boolean useNames, Annotation binding) {
    152            try {
    153                super.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
    154            } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
    155                if (collectionParameter != null) {
    156                    collectionParameter.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
    157                    return;
    158                }
    159                throw e;
    160            }
    161        }
    162
    163        /**
    164         * Accept the visitor for the current {@link Parameter}. If internally a
    165         * {@link CollectionComponentParameter}is used, it is visited also.
    166         *
    167         * @see BasicComponentParameter#accept(org.picocontainer.PicoVisitor)
    168         */
    169        public void accept(PicoVisitor visitor) {
    170            super.accept(visitor);
    171            if (collectionParameter != null) {
    172                collectionParameter.accept(visitor);
    173            }
    174        }
    175    }