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