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 }