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