    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 Resolver resolve(final PicoContainer container, final ComponentAdapter<?> forAdapter,
    116                                final ComponentAdapter<?> injecteeAdapter, final Type expectedType, final NameBinding expectedNameBinding,
    117                                final boolean useNames, final Annotation binding) {
    118
    119            return new Resolver() {
    120                final Resolver resolver = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding, useNames, binding);
    121                public boolean isResolved() {
    122                    boolean superResolved = resolver.isResolved();
    123                    if (!superResolved) {
    124                        if (collectionParameter != null) {
    125                            return collectionParameter.resolve(container, forAdapter, null, expectedType, expectedNameBinding,
    126                                                                    useNames, binding).isResolved();
    127                        }
    128                        return false;
    129                    }
    130                    return superResolved;
    131                }
    132
    133                public Object resolveInstance() {
    134                    Object result = null;
    135                    if (expectedType instanceof Class) {
    136                        result = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding, useNames, binding).resolveInstance();
    137                    } else if (expectedType instanceof ParameterizedType) {
    138                        result = ComponentParameter.super.resolve(container, forAdapter, injecteeAdapter, ((ParameterizedType) expectedType).getRawType(), expectedNameBinding, useNames, binding).resolveInstance();
    139                    }
    140                    if (result == null && collectionParameter != null) {
    141                        result = collectionParameter.resolve(container, forAdapter, injecteeAdapter, expectedType, expectedNameBinding,
    142                                                                     useNames, binding).resolveInstance();
    143                    }
    144                    return result;
    145                }
    146
    147                public ComponentAdapter<?> getComponentAdapter() {
    148                    return resolver.getComponentAdapter();
    149                }
    150            };
    151        }
    152
    153        public void verify(PicoContainer container,
    154                           ComponentAdapter<?> adapter,
    155                           Type expectedType,
    156                           NameBinding expectedNameBinding,
    157                           boolean useNames, Annotation binding) {
    158            try {
    159                super.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
    160            } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
    161                if (collectionParameter != null) {
    162                    collectionParameter.verify(container, adapter, expectedType, expectedNameBinding, useNames, binding);
    163                    return;
    164                }
    165                throw e;
    166            }
    167        }
    168
    169        /**
    170         * Accept the visitor for the current {@link Parameter}. If internally a
    171         * {@link CollectionComponentParameter}is used, it is visited also.
    172         *
    173         * @see BasicComponentParameter#accept(org.picocontainer.PicoVisitor)
    174         */
    175        public void accept(PicoVisitor visitor) {
    176            super.accept(visitor);
    177            if (collectionParameter != null) {
    178                collectionParameter.accept(visitor);
    179            }
    180        }
    181    }