KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > cglib > reflect > MethodDelegate


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  */

16 package net.sf.cglib.reflect;
17
18 import java.lang.reflect.*;
19 import net.sf.cglib.*;
20 import net.sf.cglib.core.*;
21 import org.objectweb.asm.ClassVisitor;
22 import org.objectweb.asm.Type;
23
24 // TODO: don't require exact match for return type
25

26 /**
27  * <b>DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS</b>
28  *
29  * <p>
30  * Delegates are a typesafe pointer to another method. Since Java does not
31  * have language support for such a construct, this utility will construct
32  * a proxy that forwards method calls to any method with the same signature.
33  * This utility is inspired in part by the C# delegate mechanism. We
34  * implemented it in a Java-centric manner.
35  * </p>
36  *
37  * <h2>Delegate</h2>
38  * <p>
39  * Any interface with one method can become the interface for a delegate.
40  * Consider the example below:
41  * </p>
42  *
43  * <pre>
44  * public interface MainDelegate {
45  * int main(String[] args);
46  * }
47  * </pre>
48  *
49  * <p>
50  * The interface above is an example of an interface that can become a
51  * delegate. It has only one method, and the interface is public. In
52  * order to create a delegate for that method, all we have to do is
53  * call <code>MethodDelegate.create(this, "alternateMain", MainDelegate.class)</code>.
54  * The following program will show how to use it:
55  * </p>
56  *
57  * <pre>
58  * public class Main {
59  * public static int main( String[] args ) {
60  * Main newMain = new Main();
61  * MainDelegate start = (MainDelegate)
62  * MethodDelegate.create(newMain, "alternateMain", MainDelegate.class);
63  * return start.main( args );
64  * }
65  *
66  * public int alternateMain( String[] args ) {
67  * for (int i = 0; i < args.length; i++) {
68  * System.out.println( args[i] );
69  * }
70  * return args.length;
71  * }
72  * }
73  * </pre>
74  *
75  * <p>
76  * By themselves, delegates don't do much. Their true power lies in the fact that
77  * they can be treated like objects, and passed to other methods. In fact that is
78  * one of the key building blocks of building Intelligent Agents which in tern are
79  * the foundation of artificial intelligence. In the above program, we could have
80  * easily created the delegate to match the static <code>main</code> method by
81  * substituting the delegate creation call with this:
82  * <code>MethodDelegate.createStatic(getClass(), "main", MainDelegate.class)</code>.
83  * </p>
84  * <p>
85  * Another key use for Delegates is to register event listeners. It is much easier
86  * to have all the code for your events separated out into methods instead of individual
87  * classes. One of the ways Java gets around that is to create anonymous classes.
88  * They are particularly troublesome because many Debuggers do not know what to do
89  * with them. Anonymous classes tend to duplicate alot of code as well. We can
90  * use any interface with one declared method to forward events to any method that
91  * matches the signature (although the method name can be different).
92  * </p>
93  *
94  * <h3>Equality</h3>
95  * The criteria that we use to test if two delegates are equal are:
96  * <ul>
97  * <li>
98  * They both refer to the same instance. That is, the <code>instance</code>
99  * parameter passed to the newDelegate method was the same for both. The
100  * instances are compared with the identity equality operator, <code>==</code>.
101  * </li>
102  * <li>They refer to the same method as resolved by <code>Method.equals</code>.</li>
103  * </ul>
104  *
105  * @version $Id: MethodDelegate.java,v 1.25 2006/03/05 02:43:19 herbyderby Exp $
106  */

107 abstract public class MethodDelegate {
108     private static final MethodDelegateKey KEY_FACTORY =
109       (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME);
110
111     protected Object JavaDoc target;
112     protected String JavaDoc eqMethod;
113
114     interface MethodDelegateKey {
115         Object JavaDoc newInstance(Class JavaDoc delegateClass, String JavaDoc methodName, Class JavaDoc iface);
116     }
117
118     public static MethodDelegate createStatic(Class JavaDoc targetClass, String JavaDoc methodName, Class JavaDoc iface) {
119         Generator gen = new Generator();
120         gen.setTargetClass(targetClass);
121         gen.setMethodName(methodName);
122         gen.setInterface(iface);
123         return gen.create();
124     }
125
126     public static MethodDelegate create(Object JavaDoc target, String JavaDoc methodName, Class JavaDoc iface) {
127         Generator gen = new Generator();
128         gen.setTarget(target);
129         gen.setMethodName(methodName);
130         gen.setInterface(iface);
131         return gen.create();
132     }
133
134     public boolean equals(Object JavaDoc obj) {
135         MethodDelegate other = (MethodDelegate)obj;
136         return target == other.target && eqMethod.equals(other.eqMethod);
137     }
138
139     public int hashCode() {
140         return target.hashCode() ^ eqMethod.hashCode();
141     }
142
143     public Object JavaDoc getTarget() {
144         return target;
145     }
146
147     abstract public MethodDelegate newInstance(Object JavaDoc target);
148
149     public static class Generator extends AbstractClassGenerator {
150         private static final Source SOURCE = new Source(MethodDelegate.class.getName());
151         private static final Type METHOD_DELEGATE =
152           TypeUtils.parseType("net.sf.cglib.reflect.MethodDelegate");
153         private static final Signature NEW_INSTANCE =
154           new Signature("newInstance", METHOD_DELEGATE, new Type[]{ Constants.TYPE_OBJECT });
155         
156         private Object JavaDoc target;
157         private Class JavaDoc targetClass;
158         private String JavaDoc methodName;
159         private Class JavaDoc iface;
160         
161         public Generator() {
162             super(SOURCE);
163         }
164
165         public void setTarget(Object JavaDoc target) {
166             this.target = target;
167             this.targetClass = target.getClass();
168         }
169
170         public void setTargetClass(Class JavaDoc targetClass) {
171             this.targetClass = targetClass;
172         }
173
174         public void setMethodName(String JavaDoc methodName) {
175             this.methodName = methodName;
176         }
177
178         public void setInterface(Class JavaDoc iface) {
179             this.iface = iface;
180         }
181
182         protected ClassLoader JavaDoc getDefaultClassLoader() {
183             return targetClass.getClassLoader();
184         }
185
186         public MethodDelegate create() {
187             setNamePrefix(targetClass.getName());
188             Object JavaDoc key = KEY_FACTORY.newInstance(targetClass, methodName, iface);
189             return (MethodDelegate)super.create(key);
190         }
191
192         protected Object JavaDoc firstInstance(Class JavaDoc type) {
193             return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target);
194         }
195
196         protected Object JavaDoc nextInstance(Object JavaDoc instance) {
197             return ((MethodDelegate)instance).newInstance(target);
198         }
199
200         public void generateClass(ClassVisitor v) throws NoSuchMethodException JavaDoc {
201             Method proxy = ReflectUtils.findInterfaceMethod(iface);
202             final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes());
203             if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) {
204                 throw new IllegalArgumentException JavaDoc("incompatible return types");
205             }
206
207             MethodInfo methodInfo = ReflectUtils.getMethodInfo(method);
208
209             boolean isStatic = TypeUtils.isStatic(methodInfo.getModifiers());
210             if ((target == null) ^ isStatic) {
211                 throw new IllegalArgumentException JavaDoc("Static method " + (isStatic ? "not " : "") + "expected");
212             }
213
214             ClassEmitter ce = new ClassEmitter(v);
215             CodeEmitter e;
216             ce.begin_class(Constants.V1_2,
217                            Constants.ACC_PUBLIC,
218                            getClassName(),
219                            METHOD_DELEGATE,
220                            new Type[]{ Type.getType(iface) },
221                            Constants.SOURCE_FILE);
222             ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null);
223             EmitUtils.null_constructor(ce);
224
225             // generate proxied method
226
MethodInfo proxied = ReflectUtils.getMethodInfo(iface.getDeclaredMethods()[0]);
227             e = EmitUtils.begin_method(ce, proxied, Constants.ACC_PUBLIC);
228             e.load_this();
229             e.super_getfield("target", Constants.TYPE_OBJECT);
230             e.checkcast(methodInfo.getClassInfo().getType());
231             e.load_args();
232             e.invoke(methodInfo);
233             e.return_value();
234             e.end_method();
235
236             // newInstance
237
e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
238             e.new_instance_this();
239             e.dup();
240             e.dup2();
241             e.invoke_constructor_this();
242             e.getfield("eqMethod");
243             e.super_putfield("eqMethod", Constants.TYPE_STRING);
244             e.load_arg(0);
245             e.super_putfield("target", Constants.TYPE_OBJECT);
246             e.return_value();
247             e.end_method();
248
249             // static initializer
250
e = ce.begin_static();
251             e.push(methodInfo.getSignature().toString());
252             e.putfield("eqMethod");
253             e.return_value();
254             e.end_method();
255             
256             ce.end_class();
257         }
258     }
259 }
260
Popular Tags