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 *****************************************************************************/ 010package org.picocontainer.parameters; 011 012import org.picocontainer.Behavior; 013import org.picocontainer.ComponentAdapter; 014import org.picocontainer.Converters; 015import org.picocontainer.Converting; 016import org.picocontainer.DefaultPicoContainer; 017import org.picocontainer.LifecycleStrategy; 018import org.picocontainer.NameBinding; 019import org.picocontainer.Parameter; 020import org.picocontainer.PicoContainer; 021import org.picocontainer.PicoVisitor; 022import org.picocontainer.adapters.InstanceAdapter; 023import org.picocontainer.injectors.AbstractInjector; 024import org.picocontainer.injectors.InjectInto; 025import org.picocontainer.injectors.Provider; 026 027import java.io.Serializable; 028import java.lang.annotation.Annotation; 029import java.lang.reflect.ParameterizedType; 030import java.lang.reflect.Type; 031import java.util.Collection; 032import java.util.HashSet; 033import java.util.Iterator; 034import java.util.List; 035import 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") 051public 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}