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