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 */
011package com.thoughtworks.proxy.toys.multicast;
012
013import java.util.Set;
014
015import com.thoughtworks.proxy.ProxyFactory;
016import com.thoughtworks.proxy.factory.StandardProxyFactory;
017import 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 */
030public 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}