KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > cglib > reflect > MethodDelegate


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2002 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  * if any, must include the following acknowledgment:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowledgment may appear in the software itself,
24  * if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  * not be used to endorse or promote products derived from this
28  * software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  * nor may "Apache" appear in their name, without prior written
33  * permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation. For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */

54 package org.logicalcobwebs.cglib.reflect;
55
56 import java.lang.reflect.*;
57 import org.logicalcobwebs.cglib.*;
58 import org.logicalcobwebs.cglib.core.*;
59 import org.logicalcobwebs.asm.ClassVisitor;
60 import org.logicalcobwebs.asm.Type;
61
62 // TODO: don't require exact match for return type
63

64 /**
65  * <b>DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS</b>
66  *
67  * <p>
68  * Delegates are a typesafe pointer to another method. Since Java does not
69  * have language support for such a construct, this utility will construct
70  * a proxy that forwards method calls to any method with the same signature.
71  * This utility is inspired in part by the C# delegate mechanism. We
72  * implemented it in a Java-centric manner.
73  * </p>
74  *
75  * <h2>Delegate</h2>
76  * <p>
77  * Any interface with one method can become the interface for a delegate.
78  * Consider the example below:
79  * </p>
80  *
81  * <pre>
82  * public interface MainDelegate {
83  * int main(String[] args);
84  * }
85  * </pre>
86  *
87  * <p>
88  * The interface above is an example of an interface that can become a
89  * delegate. It has only one method, and the interface is public. In
90  * order to create a delegate for that method, all we have to do is
91  * call <code>MethodDelegate.create(this, "alternateMain", MainDelegate.class)</code>.
92  * The following program will show how to use it:
93  * </p>
94  *
95  * <pre>
96  * public class Main {
97  * public static int main( String[] args ) {
98  * Main newMain = new Main();
99  * MainDelegate start = (MainDelegate)
100  * MethodDelegate.create(newMain, "alternateMain", MainDelegate.class);
101  * return start.main( args );
102  * }
103  *
104  * public int alternateMain( String[] args ) {
105  * for (int i = 0; i < args.length; i++) {
106  * System.out.println( args[i] );
107  * }
108  * return args.length;
109  * }
110  * }
111  * </pre>
112  *
113  * <p>
114  * By themselves, delegates don't do much. Their true power lies in the fact that
115  * they can be treated like objects, and passed to other methods. In fact that is
116  * one of the key building blocks of building Intelligent Agents which in tern are
117  * the foundation of artificial intelligence. In the above program, we could have
118  * easily created the delegate to match the static <code>main</code> method by
119  * substituting the delegate creation call with this:
120  * <code>MethodDelegate.createStatic(getClass(), "main", MainDelegate.class)</code>.
121  * </p>
122  * <p>
123  * Another key use for Delegates is to register event listeners. It is much easier
124  * to have all the code for your events separated out into methods instead of individual
125  * classes. One of the ways Java gets around that is to create anonymous classes.
126  * They are particularly troublesome because many Debuggers do not know what to do
127  * with them. Anonymous classes tend to duplicate alot of code as well. We can
128  * use any interface with one declared method to forward events to any method that
129  * matches the signature (although the method name can be different).
130  * </p>
131  *
132  * <h3>Equality</h3>
133  * The criteria that we use to test if two delegates are equal are:
134  * <ul>
135  * <li>
136  * They both refer to the same instance. That is, the <code>instance</code>
137  * parameter passed to the newDelegate method was the same for both. The
138  * instances are compared with the identity equality operator, <code>==</code>.
139  * </li>
140  * <li>They refer to the same method as resolved by <code>Method.equals</code>.</li>
141  * </ul>
142  *
143  * @version $Id: MethodDelegate.java,v 1.1 2003/12/12 19:28:13 billhorsman Exp $
144  */

145 abstract public class MethodDelegate {
146     private static final MethodDelegateKey KEY_FACTORY =
147       (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME);
148
149     protected Object JavaDoc target;
150     protected String JavaDoc eqMethod;
151
152     interface MethodDelegateKey {
153         Object JavaDoc newInstance(Class JavaDoc delegateClass, String JavaDoc methodName, Class JavaDoc iface);
154     }
155
156     public static MethodDelegate createStatic(Class JavaDoc targetClass, String JavaDoc methodName, Class JavaDoc iface) {
157         Generator gen = new Generator();
158         gen.setTargetClass(targetClass);
159         gen.setMethodName(methodName);
160         gen.setInterface(iface);
161         return gen.create();
162     }
163
164     public static MethodDelegate create(Object JavaDoc target, String JavaDoc methodName, Class JavaDoc iface) {
165         Generator gen = new Generator();
166         gen.setTarget(target);
167         gen.setMethodName(methodName);
168         gen.setInterface(iface);
169         return gen.create();
170     }
171
172     public boolean equals(Object JavaDoc obj) {
173         MethodDelegate other = (MethodDelegate)obj;
174         return target == other.target && eqMethod.equals(other.eqMethod);
175     }
176
177     public int hashCode() {
178         return target.hashCode() ^ eqMethod.hashCode();
179     }
180
181     public Object JavaDoc getTarget() {
182         return target;
183     }
184
185     abstract public MethodDelegate newInstance(Object JavaDoc target);
186
187     public static class Generator extends AbstractClassGenerator {
188         private static final Source SOURCE = new Source(MethodDelegate.class.getName());
189         private static final Signature NEW_INSTANCE =
190           TypeUtils.parseSignature("org.logicalcobwebs.cglib.reflect.MethodDelegate newInstance(Object)");
191         private static final Type METHOD_DELEGATE =
192           TypeUtils.parseType("org.logicalcobwebs.cglib.reflect.MethodDelegate");
193         
194         private Object JavaDoc target;
195         private Class JavaDoc targetClass;
196         private String JavaDoc methodName;
197         private Class JavaDoc iface;
198         
199         public Generator() {
200             super(SOURCE);
201         }
202
203         public void setTarget(Object JavaDoc target) {
204             this.target = target;
205             this.targetClass = target.getClass();
206         }
207
208         public void setTargetClass(Class JavaDoc targetClass) {
209             this.targetClass = targetClass;
210         }
211
212         public void setMethodName(String JavaDoc methodName) {
213             this.methodName = methodName;
214         }
215
216         public void setInterface(Class JavaDoc iface) {
217             this.iface = iface;
218         }
219
220         protected ClassLoader JavaDoc getDefaultClassLoader() {
221             return targetClass.getClassLoader();
222         }
223
224         public MethodDelegate create() {
225             setNamePrefix(targetClass.getName());
226             Object JavaDoc key = KEY_FACTORY.newInstance(targetClass, methodName, iface);
227             return (MethodDelegate)super.create(key);
228         }
229
230         protected Object JavaDoc firstInstance(Class JavaDoc type) {
231             return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target);
232         }
233
234         protected Object JavaDoc nextInstance(Object JavaDoc instance) {
235             return ((MethodDelegate)instance).newInstance(target);
236         }
237
238         public void generateClass(ClassVisitor v) throws NoSuchMethodException JavaDoc {
239             Method proxy = ReflectUtils.findInterfaceMethod(iface);
240             final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes());
241             if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) {
242                 throw new IllegalArgumentException JavaDoc("incompatible return types");
243             }
244
245             boolean isStatic = Modifier.isStatic(method.getModifiers());
246             if ((target == null) ^ isStatic) {
247                 throw new IllegalArgumentException JavaDoc("Static method " + (isStatic ? "not " : "") + "expected");
248             }
249
250             ClassEmitter ce = new ClassEmitter(v);
251             CodeEmitter e;
252             ce.begin_class(Constants.ACC_PUBLIC,
253                            getClassName(),
254                            METHOD_DELEGATE,
255                            new Type[]{ Type.getType(iface) },
256                            Constants.SOURCE_FILE);
257             ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null, null);
258             EmitUtils.null_constructor(ce);
259
260             // generate proxied method
261
Method proxied = iface.getDeclaredMethods()[0];
262             e = ce.begin_method(Constants.ACC_PUBLIC,
263                                 ReflectUtils.getSignature(proxied),
264                                 ReflectUtils.getExceptionTypes(proxied),
265                                 null);
266             e.load_this();
267             e.super_getfield("target", Constants.TYPE_OBJECT);
268             e.checkcast(Type.getType(method.getDeclaringClass()));
269             e.load_args();
270             e.invoke(method);
271             e.return_value();
272             e.end_method();
273
274             // newInstance
275
e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null, null);
276             e.new_instance_this();
277             e.dup();
278             e.dup2();
279             e.invoke_constructor_this();
280             e.getfield("eqMethod");
281             e.super_putfield("eqMethod", Constants.TYPE_STRING);
282             e.load_arg(0);
283             e.super_putfield("target", Constants.TYPE_OBJECT);
284             e.return_value();
285             e.end_method();
286
287             // static initializer
288
e = ce.begin_static();
289             Signature sig = ReflectUtils.getSignature(method);
290             e.push(sig.getName() + sig.getDescriptor());
291             e.putfield("eqMethod");
292             e.return_value();
293             e.end_method();
294             
295             ce.end_class();
296         }
297     }
298 }
299
Popular Tags