Metawidget BeanUtils auto-update binding
I have been using Metawidget (which is a really cool library by the way) on a project lately. I wanted to avoid the need for a save button by having changes automatically propagated to the beans when changes are made to the widgets. However I am using BeanUtils (from apache) which does not support binding internally.
I did try BeanBinding but it was really slow (I don’t know why and I didn’t have time to debug it). So I created an extension to the BeanUtilsBinding class (from Metawidget) that adds auto-save support. It’s a really ugly hackish implementation but it works reliably (though I think there may be corner cases where the save does not trigger when it should). It is also quite fast. It doesn’t run the save until all the messages currently in the swing queue are processed (using SwingUtilities.invokeLater
) and then it only runs it once so even if a lot of changes happen at the same time only one save will be done.
The implementation is below. Feel free to use it. It is copyright Arthur Peters under the GNU GPL v3.
import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EventListener; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.metawidget.swing.SwingMetawidget; import org.metawidget.swing.propertybinding.beanutils.BeanUtilsBinding; public class BeanUtilsAutoUpdateBinding extends BeanUtilsBinding { @SuppressWarnings("unused") private static final Logger log = Logger .getLogger("BeanUtilsAutoUpdateBinding"); protected static final class ListenToRecord { private final Component component; private final String propertyName; public ListenToRecord(Component component, String propertyName) { this.component = component; this.propertyName = propertyName; } public Component getComponent() { return component; } public String getPropertyName() { return propertyName; } } private List listeningTo = new ArrayList(); private ChangeListener changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { doSave(); } }; private ActionListener actionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { doSave(); } }; /*private DocumentListener documentListener = new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { doSave(); } @Override public void insertUpdate(DocumentEvent e) { doSave(); } @Override public void removeUpdate(DocumentEvent e) { doSave(); } }; */ private KeyListener keyListener = new KeyListener() { @Override public void keyPressed(KeyEvent e) { doSave(); } @Override public void keyReleased(KeyEvent e) { doSave(); } @Override public void keyTyped(KeyEvent e) { doSave(); } }; private MouseListener mouseListener = new MouseListener() { @Override public void mouseClicked(MouseEvent e) { doSave(); } @Override public void mouseEntered(MouseEvent e) { // Ignore } @Override public void mouseExited(MouseEvent e) { // Ignore } @Override public void mousePressed(MouseEvent e) { // Ignore } @Override public void mouseReleased(MouseEvent e) { doSave(); } }; private FocusListener focusListener = new FocusListener() { @Override public void focusGained(FocusEvent e) { // Ignore } @Override public void focusLost(FocusEvent e) { doSave(); } }; private boolean saveAlreadyInEventQueue; public BeanUtilsAutoUpdateBinding(SwingMetawidget metawidget) { super(metawidget); } @Override public void bindProperty(Component component, Map attributes, String path) { super.bindProperty(component, attributes, path); String valueProperty = getMetawidget().getValueProperty(component); // component.addPropertyChangeListener(valueProperty, this); attemptToCall(component, "addChangeListener", ChangeListener.class, changeListener); attemptToCall(component, "addActionListener", ActionListener.class, actionListener); // attemptToCall(component, "addDocumentListener", // DocumentListener.class, documentListener); component.addKeyListener(keyListener); component.addMouseListener(mouseListener); component.addFocusListener(focusListener); listeningTo.add(new ListenToRecord(component, valueProperty)); } private void attemptToCall(Component component, String methodName, Class eventListenerCls, EventListener eventListener) { try { Class cls = component.getClass(); Method method = null; while (method == null && cls != Object.class) { try { method = cls.getMethod(methodName, eventListenerCls); } catch (NoSuchMethodException e) { // Ignore } cls = cls.getSuperclass(); } method.invoke(component, eventListener); } catch (Exception e) { // Ignore it and return. This function is an attempt. log.log(Level.FINE, "Failed to call " + methodName + " on " + component, e); } } @Override public void unbindProperties() { super.unbindProperties(); for (ListenToRecord listenToRecord : listeningTo) { Component component = listenToRecord.getComponent(); // component.removePropertyChangeListener(listenToRecord.getPropertyName(), // this); component.removeKeyListener(keyListener); component.removeMouseListener(mouseListener); attemptToCall(component, "removeChangeListener", ChangeListener.class, changeListener); attemptToCall(component, "removeActionListener", ActionListener.class, actionListener); // attemptToCall(component, "removeDocumentListener", // DocumentListener.class, documentListener); } listeningTo.clear(); } protected void doSave() { synchronized (this) { if (!saveAlreadyInEventQueue) { saveAlreadyInEventQueue = true; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getMetawidget().save(); synchronized (BeanUtilsAutoUpdateBinding.this) { saveAlreadyInEventQueue = false; } } }); } } } }Posted in Java, Programming
Leave a comment