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 java.io.Serializable;
013    import java.lang.reflect.Field;
014    import java.util.HashSet;
015    import java.util.Iterator;
016    import java.util.List;
017    
018    import org.picocontainer.ComponentAdapter;
019    import org.picocontainer.Parameter;
020    import org.picocontainer.PicoContainer;
021    import org.picocontainer.PicoVisitor;
022    
023    /**
024     * A BasicComponentParameter should be used to pass in a particular component as argument to a
025     * different component's constructor. This is particularly useful in cases where several
026     * components of the same type have been registered, but with a different key. Passing a
027     * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
028     * about what other component to use in the constructor. This Parameter will never resolve
029     * against a collecting type, that is not directly registered in the PicoContainer itself.
030     *
031     * @author Jon Tirsén
032     * @author Aslak Hellesøy
033     * @author Jörg Schaible
034     * @author Thomas Heller
035     * @version $Revision: 2817 $
036     */
037    public class BasicComponentParameter
038            implements Parameter, Serializable {
039    
040        /**
041         * <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor.
042         */
043        public static final BasicComponentParameter BASIC_DEFAULT = new BasicComponentParameter();
044    
045        private Object componentKey;
046    
047        /**
048         * Expect a parameter matching a component of a specific key.
049         *
050         * @param componentKey the key of the desired component
051         */
052        public BasicComponentParameter(Object componentKey) {
053            this.componentKey = componentKey;
054        }
055    
056        /**
057         * Expect any paramter of the appropriate type.
058         */
059        public BasicComponentParameter() {
060        }
061    
062        /**
063         * Check wether the given Parameter can be statisfied by the container.
064         *
065         * @return <code>true</code> if the Parameter can be verified.
066         * @throws org.picocontainer.PicoInitializationException {@inheritDoc}
067         * @see org.picocontainer.Parameter#isResolvable(org.picocontainer.PicoContainer,
068         *           org.picocontainer.ComponentAdapter, java.lang.Class)
069         */
070        public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
071            return resolveAdapter(container, adapter, expectedType) != null;
072        }
073    
074        public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
075            final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType);
076            if (componentAdapter != null) {
077                return container.getComponentInstance(componentAdapter.getComponentKey());
078            }
079            return null;
080        }
081    
082        public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
083            final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType);
084            if (componentAdapter == null) {
085                final HashSet set = new HashSet();
086                set.add(expectedType);
087                throw new UnsatisfiableDependenciesException(adapter, set, container);
088            }
089            componentAdapter.verify(container);
090        }
091    
092        /**
093         * Visit the current {@link Parameter}.
094         *
095         * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
096         */
097        public void accept(final PicoVisitor visitor) {
098            visitor.visitParameter(this);
099        }
100    
101        private ComponentAdapter resolveAdapter(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
102    
103            final ComponentAdapter result = getTargetAdapter(container, expectedType,adapter);
104            if (result == null) {
105                return null;
106            }
107    
108            if (!expectedType.isAssignableFrom(result.getComponentImplementation())) {
109                // check for primitive value
110                if (expectedType.isPrimitive()) {
111                    try {
112                        final Field field = result.getComponentImplementation().getField("TYPE");
113                        final Class type = (Class) field.get(result.getComponentInstance(null));
114                        if (expectedType.isAssignableFrom(type)) {
115                            return result;
116                        }
117                    } catch (NoSuchFieldException e) {
118                    } catch (IllegalArgumentException e) {
119                    } catch (IllegalAccessException e) {
120                    } catch (ClassCastException e) {
121                    }
122                }
123                return null;
124            }
125            return result;
126        }
127    
128        private ComponentAdapter getTargetAdapter(PicoContainer container, Class expectedType, ComponentAdapter excludeAdapter) {
129            if (componentKey != null) {
130                // key tells us where to look so we follow
131                return container.getComponentAdapter(componentKey);
132            } else if(excludeAdapter == null) {
133                return container.getComponentAdapterOfType(expectedType);
134            } else {
135                Object excludeKey = excludeAdapter.getComponentKey();
136                ComponentAdapter byKey = container.getComponentAdapter(expectedType);
137                if(byKey != null && !excludeKey.equals(byKey.getComponentKey())) {
138                    return byKey;
139                }
140                List found = container.getComponentAdaptersOfType(expectedType);
141                ComponentAdapter exclude = null;
142                for(Iterator iterator = found.iterator(); iterator.hasNext();) {
143                    ComponentAdapter work = (ComponentAdapter) iterator.next();
144                    if( work.getComponentKey().equals(excludeKey)) {
145                        exclude = work;
146                    }
147                }
148                found.remove(exclude);
149                if(found.size() == 0) {
150                    if( container.getParent() != null) {
151                        return container.getParent().getComponentAdapterOfType(expectedType);
152                    } else {
153                        return null;
154                    }
155                } else if(found.size() == 1) {
156                    return (ComponentAdapter)found.get(0);
157                } else {
158                    Class[] foundClasses = new Class[found.size()];
159                    for (int i = 0; i < foundClasses.length; i++) {
160                        foundClasses[i] = ((ComponentAdapter) found.get(i)).getComponentImplementation();
161                    }
162                    throw new AmbiguousComponentResolutionException(expectedType, foundClasses);
163                }
164            }
165        }
166    }