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 }