FunctionPoint
FunctionPoint

Reputation: 193

Generic (JComponent) event handling in Java?

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

Answers (1)

Holger
Holger

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

  • The 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.
  • The 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.
  • The 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

Related Questions