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