/*****************************************************************************
* Copyright (C) PicoContainer Organization. All rights reserved. *
* ------------------------------------------------------------------------- *
* The software in this package is published under the terms of the BSD *
* style license a copy of which has been included with this distribution in *
* the LICENSE.txt file. *
* *
* Original code by *
*****************************************************************************/
package org.picocontainer.defaults;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.Parameter;
import org.picocontainer.PicoContainer;
import org.picocontainer.PicoInstantiationException;
import org.picocontainer.PicoIntrospectionException;
import org.picocontainer.PicoVisitor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Iterator;
/**
* A BasicComponentParameter should be used to pass in a particular component as argument to a
* different component's constructor. This is particularly useful in cases where several
* components of the same type have been registered, but with a different key. Passing a
* ComponentParameter as a parameter when registering a component will give PicoContainer a hint
* about what other component to use in the constructor. This Parameter will never resolve
* against a collecting type, that is not directly registered in the PicoContainer itself.
*
* @author Jon Tirsén
* @author Aslak Hellesøy
* @author Jörg Schaible
* @author Thomas Heller
* @version $Revision: 1944 $
*/
public class BasicComponentParameter
implements Parameter, Serializable {
/**
* <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor.
*/
public static final BasicComponentParameter BASIC_DEFAULT = new BasicComponentParameter();
private Object componentKey;
/**
* Expect a parameter matching a component of a specific key.
*
* @param componentKey the key of the desired component
*/
public BasicComponentParameter(Object componentKey) {
this.componentKey = componentKey;
}
/**
* Expect any paramter of the appropriate type.
*/
public BasicComponentParameter() {
}
/**
* Check wether the given Parameter can be statisfied by the container.
*
* @return <code>true</code> if the Parameter can be verified.
* @see org.picocontainer.Parameter#isResolvable(org.picocontainer.PicoContainer,
* org.picocontainer.ComponentAdapter, java.lang.Class)
*/
public boolean isResolvable(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
return resolveAdapter(container, adapter, expectedType) != null;
}
public Object resolveInstance(PicoContainer container, ComponentAdapter adapter, Class expectedType)
throws PicoInstantiationException {
final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType);
if (componentAdapter != null) {
return container.getComponentInstance(componentAdapter.getComponentKey());
}
return null;
}
public void verify(PicoContainer container, ComponentAdapter adapter, Class expectedType) throws PicoIntrospectionException {
final ComponentAdapter componentAdapter = resolveAdapter(container, adapter, expectedType);
if (componentAdapter == null) {
final HashSet set = new HashSet();
set.add(Arrays.asList(new Class[] {expectedType}));
throw new UnsatisfiableDependenciesException(adapter, set);
}
componentAdapter.verify(container);
}
/**
* Visit the current {@link Parameter}.
*
* @see org.picocontainer.Parameter#accept(org.picocontainer.PicoVisitor)
*/
public void accept(final PicoVisitor visitor) {
visitor.visitParameter(this);
}
private ComponentAdapter resolveAdapter(PicoContainer container, ComponentAdapter adapter, Class expectedType) {
final ComponentAdapter result = getTargetAdapter(container, expectedType,adapter);
if (result == null) {
return null;
}
if (!expectedType.isAssignableFrom(result.getComponentImplementation())) {
// check for primitive value
if (expectedType.isPrimitive()) {
try {
final Field field = result.getComponentImplementation().getField("TYPE");
final Class type = (Class) field.get(result.getComponentInstance(null));
if (expectedType.isAssignableFrom(type)) {
return result;
}
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (ClassCastException e) {
}
}
return null;
}
return result;
}
private ComponentAdapter getTargetAdapter(PicoContainer container, Class expectedType, ComponentAdapter excludeAdapter) {
if (componentKey != null) {
// key tells us where to look so we follow
return container.getComponentAdapter(componentKey);
} else if(excludeAdapter == null) {
return container.getComponentAdapterOfType(expectedType);
} else {
Object excludeKey = excludeAdapter.getComponentKey();
ComponentAdapter byKey = container.getComponentAdapter(expectedType);
if(byKey != null ) {
if( byKey.getComponentKey().equals(excludeKey)) {
return null;
}
return byKey;
}
List found = container.getComponentAdaptersOfType(expectedType);
ComponentAdapter exclude = null;
ComponentAdapter work;
for(Iterator iterator = found.iterator(); iterator.hasNext();) {
work = (ComponentAdapter) iterator.next();
if( work.getComponentKey().equals(excludeKey)) {
exclude = work;
}
}
found.remove(exclude);
if(found.size() == 0) {
if( container.getParent() != null) {
return container.getParent().getComponentAdapterOfType(expectedType);
} else {
return null;
}
} else if(found.size() == 1) {
return (ComponentAdapter)found.get(0);
} else {
Class[] foundClasses = new Class[found.size()];
for (int i = 0; i < foundClasses.length; i++) {
foundClasses[i] = ((ComponentAdapter) found.get(i)).getComponentImplementation();
}
throw new AmbiguousComponentResolutionException(expectedType, foundClasses);
}
}
}
}