Tuesday 23 September 2008

Decorating a Swing Action

I've got into the decorator pattern since reading Swing Hacks as a way to add some functionality to a class or even an interface (see the JTable sorting hack!) without changing it. I recently needed a way of adding functionality to Swing Actions that was specific to where they were used, this time disposing a JFrame but only when its used in that JFrame. The solution is to create a wrapper for the action that delegates all method calls to the wrapped action but has method hooks that that are called before and after the wrapped action's actionPerformed method is called which are overridable.


public class AbstractWrapperAction extends AbstractAction {
Action wrappedAction;

public AbstractWrapperAction(Action wrappedAction) {
this.wrappedAction = wrappedAction;
}

public void actionPerformed(ActionEvent e) {
preWrappedAction(e);

if(wrappedAction != null)
{
wrappedAction.actionPerformed(e);
}
postWrappedAction(e);
}

public void preWrappedAction(ActionEvent e) { }

public void postWrappedAction(ActionEvent e) { }

@Override
public void setEnabled(boolean b) {
wrappedAction.setEnabled(b);
}

@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
wrappedAction.removePropertyChangeListener(listener);
}

@Override
public void putValue(String key, Object value) {
wrappedAction.putValue(key, value);
}

@Override
public boolean isEnabled() {
return wrappedAction.isEnabled();
}

@Override
public Object getValue(String key) {
return wrappedAction.getValue(key);
}

@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
wrappedAction.addPropertyChangeListener(listener);
}
}


And an example usage is below...


Action wrapperAction = new AbstractWrapperAction(action) {
@Override public void postWrappedAction(ActionEvent e) {
((Window)getValue(FRAME_KEY)).dispose();
}
};
wrapperAction.putValue(FRAME_KEY, myJFrame);


Here myJFrame is an instantiated JFrame and action is your original action implementation, myJFrame will now be disposed once the action is triggered.