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.defaults; 011 012 import java.io.Serializable; 013 import java.lang.reflect.Field; 014 import java.util.HashSet; 015 import java.util.Iterator; 016 import java.util.List; 017 018 import org.picocontainer.ComponentAdapter; 019 import org.picocontainer.Parameter; 020 import org.picocontainer.PicoContainer; 021 import org.picocontainer.PicoVisitor; 022 023 /** 024 * A BasicComponentParameter should be used to pass in a particular component as argument to a 025 * different component's constructor. This is particularly useful in cases where several 026 * components of the same type have been registered, but with a different key. Passing a 027 * ComponentParameter as a parameter when registering a component will give PicoContainer a hint 028 * about what other component to use in the constructor. This Parameter will never resolve 029 * against a collecting type, that is not directly registered in the PicoContainer itself. 030 * 031 * @author Jon Tirsén 032 * @author Aslak Hellesøy 033 * @author Jörg Schaible 034 * @author Thomas Heller 035 * @version $Revision: 2817 $ 036 */ 037 public class BasicComponentParameter 038 implements Parameter, Serializable { 039 040 /** 041 * <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor. 042 */ 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 component 051 */ 052 public BasicComponentParameter(Object componentKey) { 053 this.componentKey = componentKey; 054 } 055 056 /** 057 * Expect any paramter of the appropriate type. 058 */ 059 public BasicComponentParameter() { 060 } 061 062 /** 063 * Check wether the given Parameter can be statisfied by the container. 064 * 065 * @return <code>true</code> if the Parameter can be verified. 066 * @throws org.picocontainer.PicoInitializationException {@inheritDoc} 067 * @see org.picocontainer.Parameter#isResolvable(org.picocontainer.PicoContainer, 068 * org.picocontainer.ComponentAdapter, java.lang.Class) 069 */ 070 public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType) { 071 return resolveAdapter(container, adapter, expectedType) != null; 072 } 073 074 public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType) { 075 final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType); 076 if (componentAdapter != null) { 077 return container.getComponentInstance(componentAdapter.getComponentKey()); 078 } 079 return null; 080 } 081 082 public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType) { 083 final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType); 084 if (componentAdapter == null) { 085 final HashSet set = new HashSet(); 086 set.add(expectedType); 087 throw new UnsatisfiableDependenciesException(adapter, set, container); 088 } 089 componentAdapter.verify(container); 090 } 091 092 /** 093 * Visit the current {@link Parameter}. 094 * 095 * @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor) 096 */ 097 public void accept(final PicoVisitor visitor) { 098 visitor.visitParameter(this); 099 } 100 101 private ComponentAdapter resolveAdapter(PicoContainer container, ComponentAdapter adapter, Class expectedType) { 102 103 final ComponentAdapter result = getTargetAdapter(container, expectedType,adapter); 104 if (result == null) { 105 return null; 106 } 107 108 if (!expectedType.isAssignableFrom(result.getComponentImplementation())) { 109 // check for primitive value 110 if (expectedType.isPrimitive()) { 111 try { 112 final Field field = result.getComponentImplementation().getField("TYPE"); 113 final Class type = (Class) field.get(result.getComponentInstance(null)); 114 if (expectedType.isAssignableFrom(type)) { 115 return result; 116 } 117 } catch (NoSuchFieldException e) { 118 } catch (IllegalArgumentException e) { 119 } catch (IllegalAccessException e) { 120 } catch (ClassCastException e) { 121 } 122 } 123 return null; 124 } 125 return result; 126 } 127 128 private ComponentAdapter getTargetAdapter(PicoContainer container, Class expectedType, ComponentAdapter excludeAdapter) { 129 if (componentKey != null) { 130 // key tells us where to look so we follow 131 return container.getComponentAdapter(componentKey); 132 } else if(excludeAdapter == null) { 133 return container.getComponentAdapterOfType(expectedType); 134 } else { 135 Object excludeKey = excludeAdapter.getComponentKey(); 136 ComponentAdapter byKey = container.getComponentAdapter(expectedType); 137 if(byKey != null && !excludeKey.equals(byKey.getComponentKey())) { 138 return byKey; 139 } 140 List found = container.getComponentAdaptersOfType(expectedType); 141 ComponentAdapter exclude = null; 142 for(Iterator iterator = found.iterator(); iterator.hasNext();) { 143 ComponentAdapter work = (ComponentAdapter) iterator.next(); 144 if( work.getComponentKey().equals(excludeKey)) { 145 exclude = work; 146 } 147 } 148 found.remove(exclude); 149 if(found.size() == 0) { 150 if( container.getParent() != null) { 151 return container.getParent().getComponentAdapterOfType(expectedType); 152 } else { 153 return null; 154 } 155 } else if(found.size() == 1) { 156 return (ComponentAdapter)found.get(0); 157 } else { 158 Class[] foundClasses = new Class[found.size()]; 159 for (int i = 0; i < foundClasses.length; i++) { 160 foundClasses[i] = ((ComponentAdapter) found.get(i)).getComponentImplementation(); 161 } 162 throw new AmbiguousComponentResolutionException(expectedType, foundClasses); 163 } 164 } 165 } 166 }