001    /*
002     * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003     * All rights reserved.
004     *
005     * The software in this package is published under the terms of the BSD
006     * style license a copy of which has been included with this distribution in
007     * the LICENSE.txt file.
008     * 
009     * Created on 14-May-2004
010     */
011    package com.thoughtworks.proxy.toys.multicast;
012    
013    import java.util.Set;
014    
015    import com.thoughtworks.proxy.ProxyFactory;
016    import com.thoughtworks.proxy.factory.StandardProxyFactory;
017    import com.thoughtworks.proxy.kit.ReflectionUtils;
018    
019    /**
020     * Toy factory to create proxies delegating a call to multiple objects and managing the individual results.
021     *
022     * @author Dan North
023     * @author Aslak Hellesøy
024     * @author Jörg Schaible
025     * @author Juan Li
026     * @author Paul Hammant
027     * @see com.thoughtworks.proxy.toys.multicast
028     * @since 0.1
029     */
030    public class Multicasting<T> {
031        private Class<?>[] types;
032        private Object[] delegates;
033    
034        private Multicasting(Object... delegates) {
035            this.delegates = delegates;
036        }
037    
038        private Multicasting(Class<?> primaryType, Class<?>... types) {
039            this.types = ReflectionUtils.makeTypesArray(primaryType, types);
040        }
041    
042        /**
043         * Creates a factory for proxy instances delegating a call to multiple objects and managing the individual results.
044         *
045         * @param primaryType the primary type implemented by the proxy
046         * @param types other types that are implemented by the proxy
047         * @return a factory that will proxy instances of the supplied type.
048         * @since 1.0
049         */
050        public static <T> MulticastingWith<T> proxy(Class<T> primaryType, Class<?>... types) {
051            return new MulticastingWith<T>(primaryType, types);
052        }
053    
054        /**
055         * Creates a factory for proxy instances delegating a call to multiple objects and managing the individual results.
056         *
057         * @param targets targets the target objects
058         * @return a factory that will proxy instances of the supplied type.
059         * @since 1.0
060         */
061        public static MulticastingBuild<Multicast> proxy(Object... targets) {
062            return new MulticastingBuild<Multicast>(targets);
063        }
064    
065        public static class MulticastingWith<T> {
066            Multicasting<T> multicasting;
067    
068            private MulticastingWith(Class<T> primaryType, Class<?>[] types) {
069                multicasting = new Multicasting<T>(primaryType, types);
070            }
071    
072            /**
073             * With these target Objects
074             * @param targets targets the target objects
075             * @return the factory
076             * @since 1.0
077             */
078            public MulticastingBuild<T> with(Object... targets) {
079                multicasting.delegates = targets;
080                return new MulticastingBuild<T>(multicasting);
081            }
082        }
083    
084        public static class MulticastingBuild<T> {
085            private final Multicasting<T> multicasting;
086    
087            private MulticastingBuild(Object[] targets) {
088                multicasting = new Multicasting<T>(targets);
089            }
090    
091            private MulticastingBuild(Multicasting<T> multicasting) {
092                this.multicasting = multicasting;
093            }
094    
095            /**
096             * @return the proxy using StandardProxyFactory
097             * @since 1.0
098             */
099            public T build() {
100                return multicasting.build();
101            }
102    
103            /**
104             * Generate a proxy for the specified types calling the methods on the given targets using the {@link StandardProxyFactory}.
105             * <p>
106             * Note, that the method will only return a proxy if necessary. If there is only one target instance and this
107             * instance implements all of the specified types, then there is no point in creating a proxy.
108             * </p>
109             *
110             * @param factory the factory used to generate the proxy
111             * @return the new proxy implementing {@link Multicast} or the only target
112             * @since 1.0
113             */
114            public T build(ProxyFactory factory) {
115                return multicasting.build(factory);
116            }
117        }
118    
119        private T build() {
120            return build(new StandardProxyFactory());
121        }
122    
123        private T build(ProxyFactory factory) {
124            if (types == null) {
125                return buildWithNoTypesInput(factory);
126            }
127    
128            if (delegates.length == 1) {
129                int i;
130                for (i = 0; i < types.length; i++) {
131                    if (types[i] == Multicast.class) {
132                        continue;
133                    }
134                    if (!types[i].isAssignableFrom(delegates[0].getClass())) {
135                        break;
136                    }
137                }
138                if (i == types.length) {
139                    @SuppressWarnings("unchecked")
140                    final T instance = (T) delegates[0];
141                    return instance;
142                }
143            }
144            return new MulticastingInvoker<T>(types, factory, delegates).proxy();
145        }
146    
147        private T buildWithNoTypesInput(ProxyFactory factory) {
148            if (delegates.length > 1) {
149                final Class<?> superclass = ReflectionUtils.getMostCommonSuperclass(delegates);
150                final Set<Class<?>> interfaces = ReflectionUtils.getAllInterfaces(delegates);
151                ReflectionUtils.addIfClassProxyingSupportedAndNotObject(superclass, interfaces, factory);
152                this.types = interfaces.toArray(new Class<?>[interfaces.size()]);
153                return new MulticastingInvoker<T>(types, factory, delegates).proxy();
154            }
155            @SuppressWarnings("unchecked")
156            final T instance = (T) delegates[0];
157            return instance;
158        }
159    }