I added an interesting hack to JComponent.processKeyBinding(). I now
store the action command (from the InputMap) with which an Actions is
going to be called in the Action instance as undocumented property
__command__.
This allows for an optimization in the UI classes. Usually we have a
couple of Action instances, each one handles one InputMap binding. The
design of the InputMap/ActionMap is a little unfortunate, because the
Action doesn't know for which InputMap binding it is called. This is why
we need many instances of Action to do the keyboard work. Now if I store
the action command in the Action before calling its actionPerformed()
method, the Action can determine which keybinding is activated and
execute different methods, which allows to have only one Action instance
to do the work for all InputMap bindings for one type of component (the
one instance is ususally shared between all components of that type, via
a UI ActionMap). This should improve startup time of Swing
significantly, when we changed all the UI classes to use this.
I think Sun has seen similar problems. AFAICS they use a LazyActionMap
which presumably defers creation of the ActionMap to later when it is
really used. I don't think we need to do this when we create only one
Action instance per UI class. It only makes sense when a whole bunch of
Action instances has to be created.
Here is a plan for how to handle Actions in UI classes:
- All the private Actions in a given UI should be crunched into one
Action which multiplexes between different methods depending on the
__command__ property.
- For public and protected Action classes (there are a couple) we should
also do this, and use one private Action class for everything, and let
the public/protected classes delegate to this private class for
compatibility.
2006-07-26 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/JComponent.java
(processKeyBinding): Store the action command as property
in the Action instance that we call. This allows for
improvement on the side of the Action.
/Roman
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.140
diff -u -1 -2 -r1.140 JComponent.java
--- javax/swing/JComponent.java 19 Jul 2006 19:37:18 -0000 1.140
+++ javax/swing/JComponent.java 26 Jul 2006 09:32:24 -0000
@@ -2572,38 +2572,55 @@
if (KeyboardManager.getManager().processKeyStroke(current, keyStroke, e))
e.consume();
}
protected boolean processKeyBinding(KeyStroke ks,
KeyEvent e,
int condition,
boolean pressed)
{
if (isEnabled())
{
Action act = null;
+ Object cmd = null;
InputMap map = getInputMap(condition);
if (map != null)
{
- Object cmd = map.get(ks);
+ cmd = map.get(ks);
if (cmd != null)
{
if (cmd instanceof ActionListenerProxy)
act = (Action) cmd;
else
act = (Action) getActionMap().get(cmd);
}
}
if (act != null && act.isEnabled())
- return SwingUtilities.notifyAction(act, ks, e, this, e.getModifiers());
+ {
+ // Need to synchronize here so we don't get in trouble with
+ // our __command__ hack.
+ synchronized (act)
+ {
+ // We add the command as value to the action, so that
+ // the action can later determine the command with which it
+ // was called. This is undocumented, but shouldn't affect
+ // compatibility. It allows us to use only one Action instance
+ // to do the work for all components of one type, instead of
+ // having loads of small Actions. This effectivly saves startup
+ // time of Swing.
+ act.putValue("__command__", cmd);
+ return SwingUtilities.notifyAction(act, ks, e, this,
+ e.getModifiers());
+ }
+ }
}
return false;
}
/**
* Remove a keyboard action registry.
*
* @param aKeyStroke The keystroke to unregister
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
* @see #getConditionForKeyStroke
* @see #resetKeyboardActions