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 }