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