Desing document covering architecture of solutions in actions system

$Revision: 1.2 $
Changes: available in CVS
Status: issue 17597 Dependency Tree: of issue 17597


This document discusses the solution to above mentioned problems that is the best according to current knowledge and respects the current state of the system. It is expected that this solution will be implemented as standalone module. Other modules will then be offered its services, but their usage will be voluntary. Thus backward compatibility will be easier to keep because changes in current Actions API will be minimized. The 3.3 style of actions will continue to work, but module owners should find switching to the new API atractive because of its declarative nature and all its improvements including performance. It is expected that about 90% of existing actions can rewritten to the new architecture, still using the old style continues to work for actions with enhanced and complicated presenters (component palette, memory view actions, workspace switchers, etc.).

Note: Consult this document with current implementation doc.

Declarative actions
In order to lower the requirements on VM and allow declarative definition of modules' actions it seems reasonable to reuse the XML layer of modules and allow actions to be defined there.

One action will be represented as one file. From its attributes and/or content an instance of javax.swing.Action will be constructed including localized name, action icon and controls checking the state (enabled/disabled) and performing actual action when action is invoked.

XML Layer files already come with ability to have localized name and special icon, the same mechanism is used for icon and name of the action. The appropriate piece of XML layer will look like this:

    <file name="InvokeMyWizard.action">
<!-- to give the localized name & icon -->
<attr name="SystemFileSystem.localizingBundle" stringvalue="..." />

<!-- invoke can either point to a static method that will do the action -->
<attr name="action" methodvalue="" />

<!-- or describe how to create an action for that will handle all requests -->
<attr name="action" newvalue="mymodule.MyWizardAction" />

Comments from jglick: prefer choice between the attribute action creating an Action that can be delegated to, and creating an ActionListener that can be invoked as the action body. In other words you must return an ActionListener which will be run when the action presenter is selected; and if it is in fact an Action as well then this will additionally be used to service all further Action methods (name, icon, selection state, listeners, and so on) using delegation. This is cleaner than relying on method side effects, permits the ActionEvent to be passed even in the simple mode, probably easier to explain, and will still work if some filesystem layer caches file attributes. Avoiding extra class creation for the ActionListener is not difficult if you implement it on classes which would exist anyway, use switch statements, etc.

Comments from dsimonek: I would take Jesse's note further (get inspired from Petr Nejedly). Let's don't use methodvalue, use stringvalue and let action system to invoke method itself, so that actions system is under control where and when is method invoked. This is safer as client code should be viewed as dangerous from synchronization point of view.

The extension .action is recognized as description of an action and specified attributes are read to create appropriate instance of the Swing action.

There will be a declarative way how to specify when an action is enabled or not. The goal is to provide rich framework that will compute the enabled state without invoking any custom code and loading unnecessary classes to VM.

Today, when action is represented by an instance of a class (and moreover subclass of SharedClassObject) we do not encounter problems with references: One action can be present in menu, toolbar, popup menu and still we deal just with one action. The reason is simple - file Action.instance is itself a reference to the actual action class - but this is no longer true for .action files because they share the same class for all actions.

That is why there has to be mechanism how different presentation of the same action (menu, toolbar, shortcut) really share the same action reference.

All actions are stored in some action area which provides natural pool of all actions sorted by cathegories (and thus suitable for presentation to the user) and all actions in menu, toolbars, etc. refer to this pool forcing each module to register its action in the pool (which right now is not necessary - users are not able to find all actions there). The cathegories actions are stored in also provide natural naming space that UI occurences of actions can use to refer to them:

    <folder name="Menu/File">
reference included in a name when name describes the
cathegory and name of action
<file name="Wizards-InvokeMyWizard.action" />
<!-- or in an attribute where name can be anything -->
<file name="StartMyWizard.action">
<attr name="ref" stringvalue="Wizards/InvokeMyWizard" />
The action pool is stored on system file system in folder Templates/Actions so other modules can add new actions into it using XML layer and its possible to offer them to user in New popup menu in Options/.../Menu and Options/../Toolbar configuration nodes.
Callback actions
Several actions in current Actions API - Cut, Copy, Paste, Delete, Find - did a lot of work (according to a profiler) turning these actions on and off even when they are not added to any visible component (i.e. turn off all toolbars, so they should be removeNotify()d). The only reason for this was that a different class computed their enablement: in the Explorer, ExplorerActions for the first four, and SearchHook for the last--which did not know whether the actions were visible or not, and could not find out if this changed.

The solution to this problem is inspired by InputMaps and ActionMaps introduced by Swing in JDK1.3. We do not need to convert a KeyStroke to action and that is why we can leave InputMap out of our focus for now, but it seems excelent to reuse ActionMap for our callback performers.

Every component (TopComponent) in the system has the possibility to define its own ActionMap that maps a key to appropriate Action. When a menu item or a toolbar button of some action registered via .action file is pressed (thus action is invoked), a key for the action is retrieved (to be defined later) and current context (TopComponent) is examined to contain association between the key and some action in ActionMap. If some Action is found, its actionPeformed method is invoked and thus the action serves as our old ActionPerformer.

The action's key will be taken from an attribute of .action file or from the name of the action in the action pool:

      <file name="InvokeMyWizard.action">
attribute specifying special key that can be ommited in such
case the key="Wizards/InvokeMyWizard"
<attr name="key" stringvalue="wizardKey" />
When any component needs to override the behaviour of InvokeMyWizard action in its context it can just define
    TopComponent tc;
tc.getActionMap ().put ("wizardKey", new MySpecialAction ());
and invoking the action in context of component ends with invoking of MySpecialAction instead of the global system one.

Note: There is already an implementation of context aware callback action, even not in declarative way up to now.

Global Shortcuts
Right now the dispatch of keyevents to global shortcuts is handled in a way that does not seem to be the one suggested by Swing team - we register our FocusManager and handle not consumed actions by consulting our global Keymap. This was the only way how to make things work under JDK1.2, but JDK1.3 has come with a concept of InputMaps and ActionMaps that seem to be more suitable for implementation of global shorcuts. Moreover implementation of FocusManager has been enhanced in JDK1.4 and causes us compatibility problems.

That is why we should investigate the possibility to create one global InputMap holding all our global shortcuts and attach it to each TopComponent. If successful we will not need to implement anything more - the dispatch process will be handled by Swing.

One source of problems in current implementation of Actions API is that the state of actions is global. Actions have listened on global state of the system, updated itself according to this state and reflected it. This is necessary for menu and toolbar, but for popup menus or scripting invocation a better behaviour is needed - state of action should reflect its context (a window, selection of nodes, etc.).

First thing that has to be done is the definition of context. The callback actions sections uses ActionMap to locate the current performer action. That is why ActionMap has to be part of the context.

Our possition is a bit complicated because we are building our framework on Swing's Actions API and these actions are not ready to update their context - they receive context at creation time. We have to enhance the framework to allow results of actionPeformed and isEnabled to be based on the actual context of the caller.

The context for a selected TopComponent will contain the component itself, its ActionMap, all its activated nodes and also all cookies of those nodes. By enhancing API in this way, any action can obtain its current context and easily query cookie state without need to work with nodes. Instead of having to deep down to each node, the context is now flat. This raises a question how to efectively define declarative queries over cookies and other content of the Lookup (yet to be specified).

A popup menu for given context can be constructed by cloning actions and we do not need to worry about focus problems anymore.

Note: Have a look for current context implementation.

Local Action Pools
The most often example of cooperation between different modules is the ability to add new actions to foreign components. Editor's popup menu is such example. It defines its own directory where other modules can put references to its actions, specify their order and later editor obtain them and construct its own popup menu
Note: More specifically, there needs to be created an association between certain TopComponent type and its action pool in layer.

Every module should be able to define such local action pool and it should be easy and well defined how to construct the menu, toolbar or popup menu from it. In NetBeans 3.3 there is an easy way how to construct

menubar and toolbar pool toolbar pool, popup menu is yet to be added.

Problems of the solution

  • Tools/Keyboard Shortcuts dialog has to be updated to deal with new action style
  • Usage of SystemAction should be replaced by javax.swing.Action in the FileSystem API and Nodes API in order to allow nodes to feed popup menus
  • How to replace well known Callback actions (Cut, Copy, Paste, Find) so they will be able to use advantages of the new architecture?
  • If the actions pool is to be stored in Templates/Actions/, we need to keep UI compatibility with older modules putting (system) actions into Actions/ This should be solved by Templates/Actions/Old.shadow should point here so that these will still be available.
  • ToolsAction will continue to work, but should be mostly replaced by local action pools - specifying actions in manifest will no longer be suggested

Send comments and patches to or attach them to issue 17597. Please create the patches to current version of the document.

Project Features

About this Project

openide was started in November 2009, is owned by Antonin Nebuzelsky, and has 92 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20160708.bf2ac18). © 2014, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
Please Confirm