MethodDelegate.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.*; 19 20import org.mockito.asm.ClassVisitor; 21import org.mockito.asm.Type; 22import org.mockito.cglib.*; 23import org.mockito.cglib.core.*; 24 25// TODO: don't require exact match for return type 26 27/** 28 * <b>DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS</b> 29 * 30 * <p> 31 * Delegates are a typesafe pointer to another method. Since Java does not 32 * have language support for such a construct, this utility will construct 33 * a proxy that forwards method calls to any method with the same signature. 34 * This utility is inspired in part by the C# delegate mechanism. We 35 * implemented it in a Java-centric manner. 36 * </p> 37 * 38 * <h2>Delegate</h2> 39 * <p> 40 * Any interface with one method can become the interface for a delegate. 41 * Consider the example below: 42 * </p> 43 * 44 * <pre> 45 * public interface MainDelegate { 46 * int main(String[] args); 47 * } 48 * </pre> 49 * 50 * <p> 51 * The interface above is an example of an interface that can become a 52 * delegate. It has only one method, and the interface is public. In 53 * order to create a delegate for that method, all we have to do is 54 * call <code>MethodDelegate.create(this, "alternateMain", MainDelegate.class)</code>. 55 * The following program will show how to use it: 56 * </p> 57 * 58 * <pre> 59 * public class Main { 60 * public static int main( String[] args ) { 61 * Main newMain = new Main(); 62 * MainDelegate start = (MainDelegate) 63 * MethodDelegate.create(newMain, "alternateMain", MainDelegate.class); 64 * return start.main( args ); 65 * } 66 * 67 * public int alternateMain( String[] args ) { 68 * for (int i = 0; i < args.length; i++) { 69 * System.out.println( args[i] ); 70 * } 71 * return args.length; 72 * } 73 * } 74 * </pre> 75 * 76 * <p> 77 * By themselves, delegates don't do much. Their true power lies in the fact that 78 * they can be treated like objects, and passed to other methods. In fact that is 79 * one of the key building blocks of building Intelligent Agents which in tern are 80 * the foundation of artificial intelligence. In the above program, we could have 81 * easily created the delegate to match the static <code>main</code> method by 82 * substituting the delegate creation call with this: 83 * <code>MethodDelegate.createStatic(getClass(), "main", MainDelegate.class)</code>. 84 * </p> 85 * <p> 86 * Another key use for Delegates is to register event listeners. It is much easier 87 * to have all the code for your events separated out into methods instead of individual 88 * classes. One of the ways Java gets around that is to create anonymous classes. 89 * They are particularly troublesome because many Debuggers do not know what to do 90 * with them. Anonymous classes tend to duplicate alot of code as well. We can 91 * use any interface with one declared method to forward events to any method that 92 * matches the signature (although the method name can be different). 93 * </p> 94 * 95 * <h3>Equality</h3> 96 * The criteria that we use to test if two delegates are equal are: 97 * <ul> 98 * <li> 99 * They both refer to the same instance. That is, the <code>instance</code> 100 * parameter passed to the newDelegate method was the same for both. The 101 * instances are compared with the identity equality operator, <code>==</code>. 102 * </li> 103 * <li>They refer to the same method as resolved by <code>Method.equals</code>.</li> 104 * </ul> 105 * 106 * @version $Id: MethodDelegate.java,v 1.25 2006/03/05 02:43:19 herbyderby Exp $ 107 */ 108abstract public class MethodDelegate { 109 private static final MethodDelegateKey KEY_FACTORY = 110 (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME); 111 112 protected Object target; 113 protected String eqMethod; 114 115 interface MethodDelegateKey { 116 Object newInstance(Class delegateClass, String methodName, Class iface); 117 } 118 119 public static MethodDelegate createStatic(Class targetClass, String methodName, Class iface) { 120 Generator gen = new Generator(); 121 gen.setTargetClass(targetClass); 122 gen.setMethodName(methodName); 123 gen.setInterface(iface); 124 return gen.create(); 125 } 126 127 public static MethodDelegate create(Object target, String methodName, Class iface) { 128 Generator gen = new Generator(); 129 gen.setTarget(target); 130 gen.setMethodName(methodName); 131 gen.setInterface(iface); 132 return gen.create(); 133 } 134 135 public boolean equals(Object obj) { 136 MethodDelegate other = (MethodDelegate)obj; 137 return target == other.target && eqMethod.equals(other.eqMethod); 138 } 139 140 public int hashCode() { 141 return target.hashCode() ^ eqMethod.hashCode(); 142 } 143 144 public Object getTarget() { 145 return target; 146 } 147 148 abstract public MethodDelegate newInstance(Object target); 149 150 public static class Generator extends AbstractClassGenerator { 151 private static final Source SOURCE = new Source(MethodDelegate.class.getName()); 152 private static final Type METHOD_DELEGATE = 153 TypeUtils.parseType("org.mockito.cglib.reflect.MethodDelegate"); 154 private static final Signature NEW_INSTANCE = 155 new Signature("newInstance", METHOD_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); 156 157 private Object target; 158 private Class targetClass; 159 private String methodName; 160 private Class iface; 161 162 public Generator() { 163 super(SOURCE); 164 } 165 166 public void setTarget(Object target) { 167 this.target = target; 168 this.targetClass = target.getClass(); 169 } 170 171 public void setTargetClass(Class targetClass) { 172 this.targetClass = targetClass; 173 } 174 175 public void setMethodName(String methodName) { 176 this.methodName = methodName; 177 } 178 179 public void setInterface(Class iface) { 180 this.iface = iface; 181 } 182 183 protected ClassLoader getDefaultClassLoader() { 184 return targetClass.getClassLoader(); 185 } 186 187 public MethodDelegate create() { 188 setNamePrefix(targetClass.getName()); 189 Object key = KEY_FACTORY.newInstance(targetClass, methodName, iface); 190 return (MethodDelegate)super.create(key); 191 } 192 193 protected Object firstInstance(Class type) { 194 return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target); 195 } 196 197 protected Object nextInstance(Object instance) { 198 return ((MethodDelegate)instance).newInstance(target); 199 } 200 201 public void generateClass(ClassVisitor v) throws NoSuchMethodException { 202 Method proxy = ReflectUtils.findInterfaceMethod(iface); 203 final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes()); 204 if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) { 205 throw new IllegalArgumentException("incompatible return types"); 206 } 207 208 MethodInfo methodInfo = ReflectUtils.getMethodInfo(method); 209 210 boolean isStatic = TypeUtils.isStatic(methodInfo.getModifiers()); 211 if ((target == null) ^ isStatic) { 212 throw new IllegalArgumentException("Static method " + (isStatic ? "not " : "") + "expected"); 213 } 214 215 ClassEmitter ce = new ClassEmitter(v); 216 CodeEmitter e; 217 ce.begin_class(Constants.V1_2, 218 Constants.ACC_PUBLIC, 219 getClassName(), 220 METHOD_DELEGATE, 221 new Type[]{ Type.getType(iface) }, 222 Constants.SOURCE_FILE); 223 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null); 224 EmitUtils.null_constructor(ce); 225 226 // generate proxied method 227 MethodInfo proxied = ReflectUtils.getMethodInfo(iface.getDeclaredMethods()[0]); 228 e = EmitUtils.begin_method(ce, proxied, Constants.ACC_PUBLIC); 229 e.load_this(); 230 e.super_getfield("target", Constants.TYPE_OBJECT); 231 e.checkcast(methodInfo.getClassInfo().getType()); 232 e.load_args(); 233 e.invoke(methodInfo); 234 e.return_value(); 235 e.end_method(); 236 237 // newInstance 238 e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); 239 e.new_instance_this(); 240 e.dup(); 241 e.dup2(); 242 e.invoke_constructor_this(); 243 e.getfield("eqMethod"); 244 e.super_putfield("eqMethod", Constants.TYPE_STRING); 245 e.load_arg(0); 246 e.super_putfield("target", Constants.TYPE_OBJECT); 247 e.return_value(); 248 e.end_method(); 249 250 // static initializer 251 e = ce.begin_static(); 252 e.push(methodInfo.getSignature().toString()); 253 e.putfield("eqMethod"); 254 e.return_value(); 255 e.end_method(); 256 257 ce.end_class(); 258 } 259 } 260} 261