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 }