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 }