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 net.sf.cglib.reflect;
55
56 import java.lang.reflect.*;
57 import net.sf.cglib.*;
58 import net.sf.cglib.core.*;
59 import org.objectweb.asm.ClassVisitor;
60 import org.objectweb.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.20 2003/11/06 05:10:52 herbyderby 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 target;
150 protected String eqMethod;
151
152 interface MethodDelegateKey {
153 Object newInstance(Class delegateClass, String methodName, Class iface);
154 }
155
156 public static MethodDelegate createStatic(Class targetClass, String methodName, Class 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 target, String methodName, Class 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 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 getTarget() {
182 return target;
183 }
184
185 abstract public MethodDelegate newInstance(Object 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("net.sf.cglib.reflect.MethodDelegate newInstance(Object)");
191 private static final Type METHOD_DELEGATE =
192 TypeUtils.parseType("net.sf.cglib.reflect.MethodDelegate");
193
194 private Object target;
195 private Class targetClass;
196 private String methodName;
197 private Class iface;
198
199 public Generator() {
200 super(SOURCE);
201 }
202
203 public void setTarget(Object target) {
204 this.target = target;
205 this.targetClass = target.getClass();
206 }
207
208 public void setTargetClass(Class targetClass) {
209 this.targetClass = targetClass;
210 }
211
212 public void setMethodName(String methodName) {
213 this.methodName = methodName;
214 }
215
216 public void setInterface(Class iface) {
217 this.iface = iface;
218 }
219
220 protected ClassLoader getDefaultClassLoader() {
221 return targetClass.getClassLoader();
222 }
223
224 public MethodDelegate create() {
225 setNamePrefix(targetClass.getName());
226 Object key = KEY_FACTORY.newInstance(targetClass, methodName, iface);
227 return (MethodDelegate)super.create(key);
228 }
229
230 protected Object firstInstance(Class type) {
231 return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target);
232 }
233
234 protected Object nextInstance(Object instance) {
235 return ((MethodDelegate)instance).newInstance(target);
236 }
237
238 public void generateClass(ClassVisitor v) throws NoSuchMethodException {
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("incompatible return types");
243 }
244
245 boolean isStatic = Modifier.isStatic(method.getModifiers());
246 if ((target == null) ^ isStatic) {
247 throw new IllegalArgumentException("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 }
This page was automatically generated by Maven