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