001 /***************************************************************************** 002 * Copyright (C) NanoContainer 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 Joerg Schaibe * 009 *****************************************************************************/ 010 011 package org.picocontainer.gems.adapters; 012 013 import com.thoughtworks.proxy.ProxyFactory; 014 import com.thoughtworks.proxy.factory.StandardProxyFactory; 015 import com.thoughtworks.proxy.toys.delegate.Delegating; 016 017 import org.picocontainer.ComponentAdapter; 018 import org.picocontainer.PicoContainer; 019 import org.picocontainer.PicoInitializationException; 020 import org.picocontainer.PicoIntrospectionException; 021 import org.picocontainer.defaults.DecoratingComponentAdapter; 022 023 import java.lang.reflect.Method; 024 025 026 /** 027 * ComponentAdapter, that assimilates a component for a specific type. 028 * <p> 029 * Allows the instance of another {@link ComponentAdapter} to be converted into interfacte <code>type</code>, that the 030 * instance is not assignable from. In other words the instance of the delegated adapter does NOT necessarily implement the 031 * <code>type</code> interface. 032 * </p> 033 * <p> 034 * For Example: 035 * </p> 036 * <code><pre> 037 * public interface Foo { 038 * int size(); 039 * } 040 * 041 * public class Bar { 042 * public int size() { 043 * return 1; 044 * } 045 * } 046 * 047 * new AssimilatingComponentAdapter(Foo.class, new InstanceComponentAdapter(new Bar())); 048 * </pre></code> 049 * <p> 050 * Notice how Bar does not implement the interface Foo. But Bar does have an identical <code>size()</code> method. 051 * </p> 052 * 053 * @author Jörg Schaible 054 * @author Michael Ward 055 * @since 1.2 056 */ 057 public class AssimilatingComponentAdapter extends DecoratingComponentAdapter { 058 059 private Class type; 060 private ProxyFactory proxyFactory; 061 private boolean isCompatible; 062 063 /** 064 * Construct an AssimilatingComponentAdapter. The <code>type</code> may not implement the type of the component instance. 065 * If the component instance <b>does</b> implement the interface, no proxy is used though. 066 * 067 * @param type The class type used as key. 068 * @param delegate The delegated {@link ComponentAdapter}. 069 * @param proxyFactory The {@link ProxyFactory} to use. 070 * @throws PicoIntrospectionException Thrown if the <code>type</code> is not compatible and cannot be proxied. 071 */ 072 public AssimilatingComponentAdapter(final Class type, final ComponentAdapter delegate, final ProxyFactory proxyFactory) 073 throws PicoIntrospectionException { 074 super(delegate); 075 this.type = type; 076 this.proxyFactory = proxyFactory; 077 final Class delegationType = delegate.getComponentImplementation(); 078 this.isCompatible = type.isAssignableFrom(delegationType); 079 if (!isCompatible) { 080 if (!proxyFactory.canProxy(type)) { 081 throw new PicoIntrospectionException("Cannot create proxy for type " + type.getName()); 082 } 083 final Method[] methods = type.getMethods(); 084 for (int i = 0; i < methods.length; i++) { 085 final Method method = methods[i]; 086 try { 087 delegationType.getMethod(method.getName(), method.getParameterTypes()); 088 } catch (final NoSuchMethodException e) { 089 throw new PicoIntrospectionException("Cannot create proxy for type " 090 + type.getName() 091 + ", because of incompatible method " 092 + method.toString()); 093 } 094 } 095 } 096 } 097 098 /** 099 * Construct an AssimilatingComponentAdapter. The <code>type</code> may not implement the type of the component instance. 100 * The implementation will use JDK {@link java.lang.reflect.Proxy} instances. If the component instant <b>does </b> 101 * implement the interface, no proxy is used anyway. 102 * 103 * @param type The class type used as key. 104 * @param delegate The delegated {@link ComponentAdapter}. 105 */ 106 public AssimilatingComponentAdapter(final Class type, final ComponentAdapter delegate) { 107 this(type, delegate, new StandardProxyFactory()); 108 } 109 110 /** 111 * Create and return a component instance. If the component instance and the type to assimilate is not compatible, a proxy 112 * for the instance is generated, that implements the assimilated type. 113 * 114 * @see org.picocontainer.defaults.DecoratingComponentAdapter#getComponentInstance(org.picocontainer.PicoContainer) 115 */ 116 public Object getComponentInstance(final PicoContainer container) 117 throws PicoInitializationException, PicoIntrospectionException { 118 return isCompatible ? super.getComponentInstance(container) : Delegating.object( 119 type, super.getComponentInstance(container), proxyFactory); 120 } 121 122 /** 123 * Return the type of the component. If the component type is not compatible with the type to assimilate, the assimilated 124 * type is returned. 125 * 126 * @see org.picocontainer.defaults.DecoratingComponentAdapter#getComponentImplementation() 127 */ 128 public Class getComponentImplementation() { 129 return isCompatible ? super.getComponentImplementation() : type; 130 } 131 132 /** 133 * Return the key of the component. If the key of the delegated component is a type, that is not compatible with the type to 134 * assimilate, then the assimilated type replaces the original type. 135 * 136 * @see org.picocontainer.defaults.DecoratingComponentAdapter#getComponentKey() 137 */ 138 public Object getComponentKey() { 139 final Object key = super.getComponentKey(); 140 if (key instanceof Class && (!isCompatible || !type.isAssignableFrom((Class)key))) { 141 return type; 142 } 143 return key; 144 } 145 }