DeclarativeActionRegistration

Allow Definition of Actions in Declarative Way

Status: IZ 166658 - Approved for integration (steps core, apply and easyofuse) with TCRs:

  • TCR1 - keep SaveCookie, rewrite SaveAction not to depend on nodes
  • TCA1 - look for DynamicMenuContent impls, categorize them, decide what to do
  • TCA2 - no public .context(...) factory method in java API


The primary sponsor of this enhancement in performance team. The goals seen from performance point of view include:

  1. further elimination of loaded classes
    1. includes elimination of SystemAction & co. where possible
  2. runtime effective behaviour
    1. achieved by common infrastructure
    2. includes elimination of CookieAction and NodeAction where possible

The other motivation to work on declarative actions is driven by needs of NetBeans Platform users and API designers. These poses additional requirements like:

  1. ease of use via DeclarativeRegistrationUsingAnnotations
  2. support in apisupport
  3. end to end solution

Addressing all the above stated issues at once may grow over capacities that can be dedicated to do the work in any release. As such we need to think about dividing the whole task into individual, yet beneficial steps that can be handled in given time and still deliver measurable improvements.

Image:dependencies_DeclarativeActionRegistration.png


Step Core: Advance Existing Common Infrastructure

NetBeans already provides support for declarative creation of alwaysEnabledAction which decreased the need to load and even create trivial action classes into the system.

Everything in this proposal is based around the assumption that we will enhance the set of offerings with additional methods to define callback and context aware factory methods for declarative actions. Following methods will serve as core of the infrastructure:

    // already present
    public static Action alwaysEnabled(
        ActionListener delegate, 
        String displayName, String iconBase, boolean noIconInMenu
    )
    // two new factories:
    public static ContextAwareAction callback(
        String key,
        String displayName, String iconBase, boolean noIconInMenu,
        boolean surviveFocusChange,
        Action fallback
    )
    public static ContextAwareAction context(
        Class<?> type,
        Class<? extends ActionListener> injectable,
        String displayName, String iconBase, boolean noIconInMenu,
        boolean surviveFocusChange
    )
    // obviously with associated layer based registrations

Step Beautify: @ActionRegistration

This part of the proposal is tracked as a separate Issue 183794 and yet (at the time of 6.9 release) needs to be implemented.

Define @org.openide.awt.ActionRegistration and processor that allows one to register:

  1. alwaysEnabled action
  2. callback action
  3. context aware action

into the Actions folder without use of layer. Original inception review version of the proposed code available here.

Step References: @ActionRegistration and paths

TBD: Not part of Issue 183794. Will likely be defered for later. Meanwhile just use .shadow files define in layers.

Enhance the declaration of @ActionRegistration or create other annotations that will allow to place the declarated actions into various places in UI. Menu, toolbar, DataObject's actions, etc.

This can either take a simple form of @ActionReference(whichAction, path) or it can get quite complex as described by Jesse in DeclarativeRegistrationUsingAnnotations section dedicated to actions.

Declare context action available for singletons

To require Integer (in scope of Issue 183794):

@ActionRegistration(
  displayName="#BUNDLE_KEY"
)
@ActionID(
  category="System",
  id="int.action" 
)
// the above means the action will be created as Actions/System/int-action.instance
public class MyAction implements ActionListener {
  public MyAction(Integer ctx) {
    this.ctx = ctx;
  }
  public void actionPerformed(ActionEvent ev) {
    System.out.println(ctx);
  }
}

Declare context action available multi selection and register in menu

The action reference may not be in scope of Issue 183794, but is in scope of Issue 189558:


@ActionRegistration(
  displayName="#BUNDLE_KEY"
)
@ActionID(
  category="System",
  id="int.action"
)
@ActionReference(path="Menu/File")
// the above knows to create .shadow that links to 
// Actions/System/int-action.instance
public class MyAction implements ActionListener {
  public MyAction(Collection<Integer> ctx) {
    this.ctx = ctx;
  }
  public void actionPerformed(ActionEvent ev) {
    for (Integer i : this.ctx) {
      System.out.println(i);
    }
  }
}

Declare always enabled action and register it in toolbar and menu and shortcut

In scope of Issue 189558:

@ActionRegistration(
  displayName="#BUNDLE_KEY"
)
@ActionID(category="System", id="my.toolbar.action")
@ActionReferences({
  @ActionReference(path="Toolbar/Build", position=300),
  @ActionReference(path="Shortcuts", name="C-F2"),
  @ActionReference(path="Menu/Edit", position=500)
})
public class MyAction implements ActionListener {
  public void actionPerformed(ActionEvent ev) {
      System.out.println("Hello!");
  }
}

Declare callback action with default

In scope of Issue 183794:

@ActionRegistration(
  displayName="#BUNDLE_KEY",
  key="org.openide.actions.FindAction" 
// potencially this could be replaced by
// @ActionKey("org.openide.actions.FindAction")
)
@ActionID(
  category="System",
  id="org.openide.actions.FindAction"
)
public static ActionListener create(final Collection<SearchInfo> ctx) {
  return new ActionListener() {
    public void actionPerformed(ActionEvent ev) {
      for (SearchInfo si : ctx) {
         // do search on this node
      }
    }
  };
}

Declare callback action without default

In scope of Issue 183794:

public interface MyActionConstants {
  @ActionRegistration(
    displayName="#BUNDLE_KEY"
  )
  @ActionID(
    category="System",
    id="org.openide.actions.FindAction"
  )
  public static final String FIND_ACTION_KEY = "org.openide.actions.FindAction";
 
 
  @ActionRegistration(
    displayName="#BUNDLE_DELETE_KEY"
  )
  @ActionID(
    category="System",
    id="org.openide.actions.DeleteAction"
  )
  public static final String DELETE_ACTION_KEY = "delete";
}

Reference to some action defined by someone else

In scope of Issue 189558. The annotation processor will check that no direct @ActionID annotation is used on element which contains some @ActionReference with id attribute:


@ActionReferences({
  @ActionReference(
    id=@ActionID(
      category="System",
      id="org.openide.actions.FindAction"
    ),
    path="Menu/Edit",
    position=200
  ),
})
package org.my.module;

It is expected there there will be method

static String findSystemFileSystemPath(ActionId id);

to convert action id to real location of the declaring action. This shall allow easy extensibility of the ActionId in future.

Step EaseOfUse: API support Wizard

API support wizard will be rewritten to generate context aware actions according to new style of registration. E.g. alwaysEnabled with @ActionRegistration and context aware with injection. Optionally it might support additional "general" type - e.g. Roll your own style.

Step Apply: Rewrite Selected Actions

OpenAction, SaveAction, etc. shall have a new implementation, registered at the same location in Actions folder which would not require presence of node (see TC's Lookup does not enable SaveAction).

Not necessarily now, but it would be good to get rid of "cookie" marker interfaces one day. Especially for actions like OpenAction, SaveAction, etc. Sample patch available.

Out of Scope

Presenters

No action with own Presenter can be replaced by new system. People will continue to use and register own ContextAwareAction instances.

Presenters seem to go against the lazy loading goal of this effort. The possible alternative for the future is recognize implementation of Presenter.Toolbar interface annotated by @ActionDeclaration and create a delegate for it that shows icon first and when mouse clicked, creates the actual presenter. But this can be left for some future API tuning.


Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo