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én
034     * @author Aslak Hellesøy
035     * @author Jö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    }