Reputation: 193
I'm implementing a scripting language on top of the Java VM. Using reflection, I'm able to dynamically create Java helper objects, access their fields and invoke their methods. However, I have now hard-coded handling for specific event types. E.g.: JScrollBar addAdjustmentListener() using the Adjustable interface, JFrame windowClosing() using the WindowAdapter interface, JButton addActionListener using the ActionListener interface. On the event receiving end in the scripting language, an anonymous function is called with the event data, with 0, 1 or 2 parameters of arbitrary types. My question is: Is there a (reflective) way in Java to handle arbitrary events? Generically handling all JComponent subclass events would be a good start too.
Upvotes: 0
Views: 69
Reputation: 298409
The following code may serve you as a starting point:
public static void main(String[] args) {
JFrame f = new JFrame("example");
listenToAllEvents(f, System.out, "println");
JButton b = new JButton("click me");
listenToAllEvents(b, System.out, "println");
f.getContentPane().add(b, BorderLayout.PAGE_START);
f.pack();
f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
f.addWindowListener(
EventHandler.create(WindowListener.class, f, "dispose", null, "windowClosing"));
f.setVisible(true);
}
static void listenToAllEvents(Object component, Object listener, String method) {
BeanInfo bi;
try {
bi = Introspector.getBeanInfo(component.getClass());
} catch (IntrospectionException ex) {
LOGGER.log(Level.SEVERE, null, ex);
return;
}
for(EventSetDescriptor esd: bi.getEventSetDescriptors()) try {
esd.getAddListenerMethod().invoke(component,
EventHandler.create(esd.getListenerType(), listener, method, ""));
} catch(ReflectiveOperationException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
The key points are
BeanInfo
returned by Introspector.getBeanInfo(…)
allows you to discover dynamically which events a component supports, i.e. which methods it declares matching the event source pattern.EventHandler
class allows generating listeners which invoke a particular target method when an event occurs. It also allows passing the result of invoking a method on the event as the argument to the target method. Also, as demonstrated with the windowClosing→dispose example, no-arg methods and specifying particular listener methods, are supported.EventHandler
is based on the Proxy
facility, which allows implementing arbitrary interfaces via a general InvocationHandler
, which might be quiet useful for other purposes in the context of your scripting language, unless you want to go the route of generating bytecode.Note that the example code printing all events can make the UI quite slow. This does not happen when you use other actions or limit the events you’re listening to.
Upvotes: 2