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