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