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.Behavior;
013 import org.picocontainer.ComponentAdapter;
014 import org.picocontainer.Converters;
015 import org.picocontainer.Converting;
016 import org.picocontainer.DefaultPicoContainer;
017 import org.picocontainer.LifecycleStrategy;
018 import org.picocontainer.NameBinding;
019 import org.picocontainer.Parameter;
020 import org.picocontainer.PicoContainer;
021 import org.picocontainer.PicoVisitor;
022 import org.picocontainer.adapters.InstanceAdapter;
023 import org.picocontainer.injectors.AbstractInjector;
024 import org.picocontainer.injectors.InjectInto;
025 import org.picocontainer.injectors.Provider;
026
027 import java.io.Serializable;
028 import java.lang.annotation.Annotation;
029 import java.lang.reflect.ParameterizedType;
030 import java.lang.reflect.Type;
031 import java.util.Collection;
032 import java.util.HashSet;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.Set;
036
037 /**
038 * A BasicComponentParameter should be used to pass in a particular component as argument to a
039 * different component's constructor. This is particularly useful in cases where several
040 * components of the same type have been registered, but with a different key. Passing a
041 * ComponentParameter as a parameter when registering a component will give PicoContainer a hint
042 * about what other component to use in the constructor. This Parameter will never resolve
043 * against a collecting type, that is not directly registered in the PicoContainer itself.
044 *
045 * @author Jon Tirsén
046 * @author Aslak Hellesøy
047 * @author Jörg Schaible
048 * @author Thomas Heller
049 */
050 @SuppressWarnings("serial")
051 public class BasicComponentParameter extends AbstractParameter implements Parameter, Serializable {
052
053 /** <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor. */
054 public static final BasicComponentParameter BASIC_DEFAULT = new BasicComponentParameter();
055
056 private Object componentKey;
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 BasicComponentParameter(Object componentKey) {
064 this.componentKey = componentKey;
065 }
066
067 /** Expect any parameter of the appropriate type. */
068 public BasicComponentParameter() {
069 }
070
071 /**
072 * Check whether the given Parameter can be satisfied by the container.
073 *
074 * @return <code>true</code> if the Parameter can be verified.
075 *
076 * @throws org.picocontainer.PicoCompositionException
077 * {@inheritDoc}
078 * @see Parameter#isResolvable(PicoContainer, ComponentAdapter, Class, NameBinding ,boolean, Annotation)
079 */
080 public Resolver resolve(final PicoContainer container,
081 final ComponentAdapter<?> forAdapter,
082 final ComponentAdapter<?> injecteeAdapter, final Type expectedType,
083 NameBinding expectedNameBinding, boolean useNames, Annotation binding) {
084
085 Class<?> resolvedClassType = null;
086 // TODO take this out for Pico3
087 if (!(expectedType instanceof Class)) {
088 if (expectedType instanceof ParameterizedType) {
089 resolvedClassType = (Class<?>) ((ParameterizedType)expectedType).getRawType();
090 } else {
091 return new Parameter.NotResolved();
092 }
093 } else {
094 resolvedClassType = (Class<?>)expectedType;
095 }
096 assert resolvedClassType != null;
097
098 ComponentAdapter<?> componentAdapter0;
099 if (injecteeAdapter == null) {
100 componentAdapter0 = resolveAdapter(container, forAdapter, resolvedClassType, expectedNameBinding, useNames, binding);
101 } else {
102 componentAdapter0 = injecteeAdapter;
103 }
104 final ComponentAdapter<?> componentAdapter = componentAdapter0;
105 return new Resolver() {
106 public boolean isResolved() {
107 return componentAdapter != null;
108 }
109 public Object resolveInstance() {
110 if (componentAdapter == null) {
111 return null;
112 }
113 if (componentAdapter instanceof DefaultPicoContainer.LateInstance) {
114 return convert(getConverters(container), ((DefaultPicoContainer.LateInstance) componentAdapter).getComponentInstance(), expectedType);
115 // } else if (injecteeAdapter != null && injecteeAdapter instanceof DefaultPicoContainer.KnowsContainerAdapter) {
116 // return convert(((DefaultPicoContainer.KnowsContainerAdapter) injecteeAdapter).getComponentInstance(makeInjectInto(forAdapter)), expectedType);
117 } else {
118 return convert(getConverters(container), container.getComponent(componentAdapter.getComponentKey(), makeInjectInto(forAdapter)), expectedType);
119 }
120 }
121
122 public ComponentAdapter<?> getComponentAdapter() {
123 return componentAdapter;
124 }
125 };
126 }
127
128 private Converters getConverters(PicoContainer container) {
129 return container instanceof Converting ? ((Converting) container).getConverters() : null;
130 }
131
132 private static InjectInto makeInjectInto(ComponentAdapter<?> forAdapter) {
133 return new InjectInto(forAdapter.getComponentImplementation(), forAdapter.getComponentKey());
134 }
135
136 private static Object convert(Converters converters, Object obj, Type expectedType) {
137 if (obj instanceof String && expectedType != String.class) {
138 obj = converters.convert((String) obj, expectedType);
139 }
140 return obj;
141 }
142
143 public void verify(PicoContainer container,
144 ComponentAdapter<?> forAdapter,
145 Type expectedType,
146 NameBinding expectedNameBinding, boolean useNames, Annotation binding) {
147 final ComponentAdapter componentAdapter =
148 resolveAdapter(container, forAdapter, (Class<?>)expectedType, expectedNameBinding, useNames, binding);
149 if (componentAdapter == null) {
150 final Set<Type> set = new HashSet<Type>();
151 set.add(expectedType);
152 throw new AbstractInjector.UnsatisfiableDependenciesException(
153 forAdapter.getComponentImplementation().getName() + " has unsatisfied dependencies: " + set + " from " + container);
154 }
155 componentAdapter.verify(container);
156 }
157
158 /**
159 * Visit the current {@link Parameter}.
160 *
161 * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
162 */
163 public void accept(final PicoVisitor visitor) {
164 visitor.visitParameter(this);
165 }
166
167 protected <T> ComponentAdapter<T> resolveAdapter(PicoContainer container,
168 ComponentAdapter adapter,
169 Class<T> expectedType,
170 NameBinding expectedNameBinding, boolean useNames, Annotation binding) {
171 Class type = expectedType;
172 if (type.isPrimitive()) {
173 String expectedTypeName = expectedType.getName();
174 if (expectedTypeName == "int") {
175 type = Integer.class;
176 } else if (expectedTypeName == "long") {
177 type = Long.class;
178 } else if (expectedTypeName == "float") {
179 type = Float.class;
180 } else if (expectedTypeName == "double") {
181 type = Double.class;
182 } else if (expectedTypeName == "boolean") {
183 type = Boolean.class;
184 } else if (expectedTypeName == "char") {
185 type = Character.class;
186 } else if (expectedTypeName == "short") {
187 type = Short.class;
188 } else if (expectedTypeName == "byte") {
189 type = Byte.class;
190 }
191 }
192
193 ComponentAdapter<T> result = null;
194 if (componentKey != null) {
195 // key tells us where to look so we follow
196 result = typeComponentAdapter(container.getComponentAdapter(componentKey));
197 } else if (adapter == null) {
198 result = container.getComponentAdapter(type, (NameBinding) null);
199 } else {
200 Object excludeKey = adapter.getComponentKey();
201 ComponentAdapter byKey = container.getComponentAdapter((Object)expectedType);
202 if (byKey != null && !excludeKey.equals(byKey.getComponentKey())) {
203 result = typeComponentAdapter(byKey);
204 }
205
206 if (result == null && useNames) {
207 ComponentAdapter found = container.getComponentAdapter(expectedNameBinding.getName());
208 if ((found != null) && areCompatible(container, expectedType, found) && found != adapter) {
209 result = found;
210 }
211 }
212
213 if (result == null) {
214 List<ComponentAdapter<T>> found = binding == null ? container.getComponentAdapters(expectedType) :
215 container.getComponentAdapters(expectedType, binding.annotationType());
216 removeExcludedAdapterIfApplicable(excludeKey, found);
217 if (found.size() == 0) {
218 result = noMatchingAdaptersFound(container, expectedType, expectedNameBinding, binding);
219 } else if (found.size() == 1) {
220 result = found.get(0);
221 } else {
222 throw tooManyMatchingAdaptersFound(expectedType, found);
223 }
224 }
225 }
226
227 if (result == null) {
228 return null;
229 }
230
231 if (!type.isAssignableFrom(result.getComponentImplementation())) {
232 // if (!(result.getComponentImplementation() == String.class && stringConverters.containsKey(type))) {
233 if (!(result.getComponentImplementation() == String.class && getConverters(container).canConvert(type))) {
234 return null;
235 }
236 }
237 return result;
238 }
239
240 @SuppressWarnings({ "unchecked" })
241 private static <T> ComponentAdapter<T> typeComponentAdapter(ComponentAdapter<?> componentAdapter) {
242 return (ComponentAdapter<T>)componentAdapter;
243 }
244
245 private <T> ComponentAdapter<T> noMatchingAdaptersFound(PicoContainer container, Class<T> expectedType,
246 NameBinding expectedNameBinding, Annotation binding) {
247 if (container.getParent() != null) {
248 if (binding != null) {
249 return container.getParent().getComponentAdapter(expectedType, binding.getClass());
250 } else {
251 return container.getParent().getComponentAdapter(expectedType, expectedNameBinding);
252 }
253 } else {
254 return null;
255 }
256 }
257
258 private <T> AbstractInjector.AmbiguousComponentResolutionException tooManyMatchingAdaptersFound(Class<T> expectedType, List<ComponentAdapter<T>> found) {
259 String[] foundStrings = makeFoundAmbiguousStrings(found);
260 return new AbstractInjector.AmbiguousComponentResolutionException(expectedType, foundStrings);
261 }
262
263 public static <T> String[] makeFoundAmbiguousStrings(Collection<ComponentAdapter<T>> found) {
264 String[] foundStrings = new String[found.size()];
265 int ix = 0;
266 for (ComponentAdapter<?> f : found) {
267 f = findInjectorOrInstanceAdapter(f);
268 foundStrings[ix++] = f.toString();
269 }
270 return foundStrings;
271 }
272
273 public static ComponentAdapter<?> findInjectorOrInstanceAdapter(ComponentAdapter<?> f) {
274 while (f instanceof Behavior
275 || (f instanceof LifecycleStrategy && !(f instanceof InstanceAdapter) && !(f instanceof Provider))) {
276 f = f.getDelegate();
277 }
278 return f;
279 }
280
281 private <T> void removeExcludedAdapterIfApplicable(Object excludeKey, List<ComponentAdapter<T>> found) {
282 ComponentAdapter exclude = null;
283 for (ComponentAdapter work : found) {
284 if (work.getComponentKey().equals(excludeKey)) {
285 exclude = work;
286 break;
287 }
288 }
289 found.remove(exclude);
290 }
291
292 private <T> boolean areCompatible(PicoContainer container, Class<T> expectedType, ComponentAdapter found) {
293 Class foundImpl = found.getComponentImplementation();
294 return expectedType.isAssignableFrom(foundImpl) ||
295 //(foundImpl == String.class && stringConverters.containsKey(expectedType)) ;
296 (foundImpl == String.class && getConverters(container) != null
297 && getConverters(container).canConvert(expectedType)) ;
298 }
299 }