MulticastDelegate.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard/*
2a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard * Copyright 2003,2004 The Apache Software Foundation
3a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard *
4a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard *  Licensed under the Apache License, Version 2.0 (the "License");
5a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard * you may not use this file except in compliance with the License.
6a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard * You may obtain a copy of the License at
7a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard *
8a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard *      http://www.apache.org/licenses/LICENSE-2.0
9a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard *
10fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard *  Unless required by applicable law or agreed to in writing, software
11fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard * distributed under the License is distributed on an "AS IS" BASIS,
12fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard * See the License for the specific language governing permissions and
14fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard * limitations under the License.
15a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard */
16a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellardpackage org.mockito.cglib.reflect;
17a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
18fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellardimport java.lang.reflect.*;
19fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellardimport java.util.*;
20fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
21a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellardimport org.mockito.asm.ClassVisitor;
22fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellardimport org.mockito.asm.MethodVisitor;
23a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellardimport org.mockito.asm.Type;
24fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellardimport org.mockito.cglib.core.*;
25fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
26fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellardabstract public class MulticastDelegate implements Cloneable {
27a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    protected Object[] targets = {};
28a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
29a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    protected MulticastDelegate() {
30a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    }
31a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
32a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    public List getTargets() {
33a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        return new ArrayList(Arrays.asList(targets));
34fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard    }
35fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
36a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    abstract public MulticastDelegate add(Object target);
37a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
38a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    protected MulticastDelegate addHelper(Object target) {
39f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        MulticastDelegate copy = newInstance();
40f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        copy.targets = new Object[targets.length + 1];
41a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        System.arraycopy(targets, 0, copy.targets, 0, targets.length);
42f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        copy.targets[targets.length] = target;
43fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard        return copy;
44f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard    }
45f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
46f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard    public MulticastDelegate remove(Object target) {
47fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard        for (int i = targets.length - 1; i >= 0; i--) {
48f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            if (targets[i].equals(target)) {
49f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                MulticastDelegate copy = newInstance();
50f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                copy.targets = new Object[targets.length - 1];
51f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                System.arraycopy(targets, 0, copy.targets, 0, i);
52a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                System.arraycopy(targets, i + 1, copy.targets, i, targets.length - i - 1);
53f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                return copy;
54fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            }
55f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        }
56f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        return this;
57fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard    }
58f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
59fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard    abstract public MulticastDelegate newInstance();
60f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
61a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard    public static MulticastDelegate create(Class iface) {
62a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        Generator gen = new Generator();
63f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        gen.setInterface(iface);
64a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        return gen.create();
65f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard    }
66fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
67fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard    public static class Generator extends AbstractClassGenerator {
68a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        private static final Source SOURCE = new Source(MulticastDelegate.class.getName());
69f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private static final Type MULTICAST_DELEGATE =
70f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard          TypeUtils.parseType("org.mockito.cglib.reflect.MulticastDelegate");
71f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private static final Signature NEW_INSTANCE =
72f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard          new Signature("newInstance", MULTICAST_DELEGATE, new Type[0]);
73f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private static final Signature ADD_DELEGATE =
74f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard          new Signature("add", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
75f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private static final Signature ADD_HELPER =
76f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard          new Signature("addHelper", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
77f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
78f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private Class iface;
79f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
80a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        public Generator() {
81a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            super(SOURCE);
82a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
83fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
84fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard        protected ClassLoader getDefaultClassLoader() {
85a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            return iface.getClassLoader();
86a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
87a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
88a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        public void setInterface(Class iface) {
89a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            this.iface = iface;
90a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
91a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
92a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        public MulticastDelegate create() {
93fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            setNamePrefix(MulticastDelegate.class.getName());
94fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            return (MulticastDelegate)super.create(iface.getName());
95a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
96f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
97f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        public void generateClass(ClassVisitor cv) {
98f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface));
99fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
1002ad8608cb3e6a8d2f375ad2295504167b082711fTom Stellard            ClassEmitter ce = new ClassEmitter(cv);
101fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            ce.begin_class(Constants.V1_2,
102fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard                           Constants.ACC_PUBLIC,
103fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard                           getClassName(),
104f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                           MULTICAST_DELEGATE,
1052ad8608cb3e6a8d2f375ad2295504167b082711fTom Stellard                           new Type[]{ Type.getType(iface) },
106fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard                           Constants.SOURCE_FILE);
107fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            EmitUtils.null_constructor(ce);
108fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard
109f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            // generate proxied method
110f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            emitProxy(ce, method);
111f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
112fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            // newInstance
113fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
114fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e.new_instance_this();
115fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e.dup();
116fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e.invoke_constructor_this();
117fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e.return_value();
118fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e.end_method();
119f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
120f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            // add
121fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            e = ce.begin_method(Constants.ACC_PUBLIC, ADD_DELEGATE, null);
122f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.load_this();
123f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.load_arg(0);
124f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.checkcast(Type.getType(iface));
125a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            e.invoke_virtual_this(ADD_HELPER);
126a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            e.return_value();
127a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            e.end_method();
128f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
129a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            ce.end_class();
130a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
131f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard
132f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        private void emitProxy(ClassEmitter ce, final MethodInfo method) {
133a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            final CodeEmitter e = EmitUtils.begin_method(ce, method, Constants.ACC_PUBLIC);
134a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            Type returnType = method.getSignature().getReturnType();
135a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            final boolean returns = returnType != Type.VOID_TYPE;
136f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            Local result = null;
137a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            if (returns) {
138a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                result = e.make_local(returnType);
139f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                e.zero_or_null(returnType);
14085a68814ee98d649e92223a42bab9db7392649baTom Stellard                e.store_local(result);
14185a68814ee98d649e92223a42bab9db7392649baTom Stellard            }
14285a68814ee98d649e92223a42bab9db7392649baTom Stellard            e.load_this();
143f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.super_getfield("targets", Constants.TYPE_OBJECT_ARRAY);
144a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            final Local result2 = result;
145a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            EmitUtils.process_array(e, Constants.TYPE_OBJECT_ARRAY, new ProcessArrayCallback() {
146a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                public void processElement(Type type) {
147f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                    e.checkcast(Type.getType(iface));
1482ad8608cb3e6a8d2f375ad2295504167b082711fTom Stellard                    e.load_args();
149a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                    e.invoke(method);
150a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                    if (returns) {
151a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                        e.store_local(result2);
152f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                    }
153a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard                }
154a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            });
155a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            if (returns) {
156f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard                e.load_local(result);
157fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            }
158f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.return_value();
159f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            e.end_method();
160f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        }
161a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
162f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        protected Object firstInstance(Class type) {
163f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard            // make a new instance in case first object is used with a long list of targets
164a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard            return ((MulticastDelegate)ReflectUtils.newInstance(type)).newInstance();
165a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        }
166a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard
167a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard        protected Object nextInstance(Object instance) {
168fa63f976522bd4faf19249e8c9ac4d3edda498d9Tom Stellard            return ((MulticastDelegate)instance).newInstance();
169f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard        }
170f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard    }
171a75c6163e605f35b14f26930dd9227e4f337ec9eTom Stellard}
172f323c6260d54357d2408f1993a21e08f091b1e27Tom Stellard