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