001: /*
002: * @(#)EventAdapter.java 1.3 05/03/18
003: *
004: * Copyright (c) 2002-2004 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package org.pnuts.beans;
010:
011: import pnuts.compiler.*;
012: import java.io.*;
013: import java.security.*;
014: import java.lang.reflect.*;
015:
016: /**
017: * This class generates an event adapter for a particular listener type and callback method.
018: *
019: * <pre>In Pnuts,
020: * import("java.awt.event.*")
021: *
022: * adapterClass = generateEventAdapter(ActionListener, "actionPerformed")
023: * adapter = adapterClass(function (e) ... , getContext())
024: * </pre>
025: */
026: public class EventAdapter {
027:
028: static boolean hasJava2Security = false;
029: static {
030: try {
031: Class.class
032: .getMethod("getProtectionDomain", new Class[] {});
033: hasJava2Security = true;
034: } catch (Exception e) {
035: }
036: }
037:
038: /**
039: * Generate an adapter class for bean events.
040: * The generated class implements the the specified <em>listenerType</em>.
041: * The constructor has two arguments (PnutsFunction, Context). The method <em>methodName</em>
042: * calls a function which is specified as a parameter of the constructor.
043: *
044: * @param listenerType a Class object of a EventListener subclass
045: * @param methodName a method name of the listener type.
046: * @return a Class object of the generated class
047: */
048: public static Class generateEventAdapter(Class listenerType,
049: String methodName) {
050: try {
051: ByteArrayOutputStream bout = new ByteArrayOutputStream();
052: ClassFile cf = getClassFileForEventAdapter(
053: "__event_adapter__", listenerType, methodName);
054: cf.write(bout);
055: return defineClass(cf.getClassName(), bout.toByteArray(),
056: EventAdapter.class.getClassLoader());
057: } catch (IOException io) {
058: return null;
059: }
060: }
061:
062: /**
063: * Creates a class file of a event adapter that implements the specified listenerType.
064: * The constructor of the created class takes PnutsFunction and Context as the parameters.
065: * When the corresponding event is observed, the PnutsFunction is called in the Context.
066: *
067: * @param className the class name of the event adapter
068: * @param listenerType a Class object of a EventListener subclass
069: * @param methodName a method name of the listener type.
070: */
071: public static ClassFile getClassFileForEventAdapter(
072: String className, Class listenerType, String methodName) {
073: /*
074: if (!EventListener.class.isAssignableFrom(listenerType)){
075: throw new RuntimeException("EventListener type is required:" + listenerType);
076: }
077: */
078: ClassFile cf = new ClassFile(className, "java.lang.Object",
079: null, Constants.ACC_PUBLIC);
080: cf.addInterface(listenerType.getName());
081: cf.addField("context", "Lpnuts/lang/Context;",
082: Constants.ACC_PRIVATE);
083: cf.addField("function", "Lpnuts/lang/PnutsFunction;",
084: Constants.ACC_PRIVATE);
085:
086: cf.openMethod("<init>",
087: "(Lpnuts/lang/PnutsFunction;Lpnuts/lang/Context;)V",
088: Constants.ACC_PUBLIC);
089: cf.add(Opcode.ALOAD_0);
090: cf.add(Opcode.INVOKESPECIAL, "java.lang.Object", "<init>",
091: "()", "V");
092: cf.add(Opcode.ALOAD_0);
093: cf.add(Opcode.ALOAD_1);
094: cf.add(Opcode.PUTFIELD, cf.getClassName(), "function",
095: "Lpnuts/lang/PnutsFunction;");
096: cf.add(Opcode.ALOAD_0);
097: cf.add(Opcode.ALOAD_2);
098: cf.add(Opcode.PUTFIELD, cf.getClassName(), "context",
099: "Lpnuts/lang/Context;");
100: cf.add(Opcode.RETURN);
101: cf.closeMethod();
102:
103: Method[] methods = listenerType.getMethods();
104: for (int i = 0; i < methods.length; i++) {
105: Method m = methods[i];
106: Class[] types = m.getParameterTypes();
107: if (m.getName().equals(methodName)) {
108: cf.openMethod(methodName, makeSignature(types,
109: void.class), Constants.ACC_PUBLIC);
110: Label catchStart = cf.getLabel(true);
111:
112: cf.add(Opcode.ALOAD_0);
113: cf.add(Opcode.GETFIELD, cf.getClassName(), "function",
114: "Lpnuts/lang/PnutsFunction;");
115: cf.add(Opcode.ICONST_1);
116: cf.add(Opcode.ANEWARRAY, "java.lang.Object");
117: cf.add(Opcode.DUP);
118: cf.add(Opcode.ICONST_0);
119: cf.add(Opcode.ALOAD_1);
120: cf.add(Opcode.AASTORE);
121: cf.add(Opcode.ALOAD_0);
122: cf.add(Opcode.GETFIELD, cf.getClassName(), "context",
123: "Lpnuts/lang/Context;");
124: cf.add(Opcode.INVOKEVIRTUAL,
125: "pnuts.lang.PnutsFunction", "call",
126: "([Ljava/lang/Object;Lpnuts/lang/Context;)",
127: "Ljava/lang/Object;");
128: cf.add(Opcode.POP);
129: cf.add(Opcode.RETURN);
130: Label catchEnd = cf.getLabel(true);
131: Label catchTarget = cf.getLabel(true);
132: cf.reserveStack(1);
133: cf.add(Opcode.ALOAD_0);
134: cf.add(Opcode.GETFIELD, cf.getClassName(), "context",
135: "Lpnuts/lang/Context;");
136: cf.add(Opcode.INVOKESTATIC, "pnuts.lang.Runtime",
137: "printError",
138: "(Ljava/lang/Throwable;Lpnuts/lang/Context;)",
139: "V");
140: cf.add(Opcode.RETURN);
141:
142: cf.addExceptionHandler(catchStart, catchEnd,
143: catchTarget, "java.lang.Throwable");
144: cf.closeMethod();
145: } else {
146: cf.openMethod(m.getName(), makeSignature(types,
147: void.class), Constants.ACC_PUBLIC);
148: cf.add(Opcode.RETURN);
149: cf.closeMethod();
150: }
151: }
152: return cf;
153: }
154:
155: private static String makeSignature(Class[] parameterTypes,
156: Class returnType) {
157: StringBuffer sbuf = new StringBuffer();
158: sbuf.append('(');
159: for (int i = 0; i < parameterTypes.length; i++) {
160: sbuf.append(ClassFile.signature(parameterTypes[i]));
161: }
162: sbuf.append(')');
163: sbuf.append(ClassFile.signature(returnType));
164: return sbuf.toString();
165: }
166:
167: private static Class defineClass(String name, byte[] array,
168: final ClassLoader parent) {
169: ByteCodeLoader loader = null;
170: if (hasJava2Security) {
171: loader = (ByteCodeLoader) AccessController
172: .doPrivileged(new PrivilegedAction() {
173: public Object run() {
174: return new ByteCodeLoader(parent);
175: }
176: });
177: } else {
178: loader = new ByteCodeLoader(parent);
179: }
180: return loader.define(name, array, 0, array.length);
181: }
182: }
|