Bug 153442 - Replacement for CookieAction/NodeAction
Replacement for CookieAction/NodeAction
Status: NEW
Product: platform
Classification: Unclassified
Component: -- Other --
6.x
All All
: P2 (vote)
: TBD
Assigned To: _ tboudreau
issues@platform
: API, API_REVIEW
Depends on:
Blocks: 66836
  Show dependency treegraph
 
Reported: 2008-11-19 08:01 UTC by _ tboudreau
Modified: 2009-09-21 17:11 UTC (History)
10 users (show)

See Also:
Issue Type: ENHANCEMENT
:


Attachments
Patch adding org.openide.util.ContextAction and tests (35.04 KB, patch)
2008-11-19 08:03 UTC, _ tboudreau
Details | Diff
New patch (61.44 KB, patch)
2008-11-20 03:15 UTC, _ tboudreau
Details | Diff
And another one (added parameter to have createIndirectAction optionally reject enablement if any Lookup.Providers don't have an object they can use) (64.60 KB, patch)
2008-11-20 04:02 UTC, _ tboudreau
Details | Diff
Convenience replacement for migrating NodeActions with fewer code changes (4.61 KB, text/plain)
2008-11-20 05:55 UTC, _ tboudreau
Details
Demo module - creates an action equivalent to a project sensitive action (sensitive to main project, project in node lookup or project owning selected file) (10.33 KB, application/octet-stream)
2008-11-25 22:25 UTC, _ tboudreau
Details

Note You need to log in before you can comment on or make changes to this bug.
Description _ tboudreau 2008-11-19 08:01:29 UTC
We've been talking about a ContextAwareAction-based replacement for CookieAction/NodeAction for about five years now. 
So, here is one.  The only use-case it doesn't cover is CookieAction with multiple cookie types.

I've written various versions of these classes in a number of projects (mobility.project, imagine, platformx, etc.)
several times over the last few years.  It would be nice to delete them.
Comment 1 _ tboudreau 2008-11-19 08:03:33 UTC
Created attachment 73897 [details]
Patch adding org.openide.util.ContextAction and tests
Comment 2 Jaroslav Tulach 2008-11-19 15:39:23 UTC
I am looking for somebody to help me implement new actions. However it is more work than just having new class to 
subclass. We need declarative registrations, deferred loading, etc. Do you want to work on this task more, or your 
involvement can only be limited?
Comment 3 _ tboudreau 2008-11-19 16:15:31 UTC
Sure, I would be interested in helping (assuming bug-fixing and work on mobility in 7.0 do not conflict.  I would like
to get this class into the API because it will allow me to delete code in a bunch of other places.  It would be pretty
simple from here to deprecate NodeAction, CookieAction and even the entire Actions API (the actions will still work,
there can just be non-SharedClassObject replacements somewhere else).


What are we missing for declarative registration?  File attributes for object type and survive-focus-change policy, or
something more?

For deferred loading, do you mean deferring loading the action, or deferring loading the object it is sensitive to in
the lookup (we did something like that in platformx here -
http://hg.netbeans.org/platformx/file/a84d84581e2c/api.objectloader/src/org/netbeans/platformx/api/objectloader/ - you
would make your action sensitive to ObjectLoader<T> in the lookup, and it could manage its enabled state without loading
the object unless the action was invoked - but you do have to handle the case that the load fails for some reason - but
this code was also partly because T might be a 300Mb serialized object, so we also had to guarantee that the loading was
not on the event thread).
Comment 4 _ tboudreau 2008-11-20 03:15:08 UTC
Created attachment 73933 [details]
New patch
Comment 5 _ tboudreau 2008-11-20 03:54:23 UTC
The attached patch should address all of the use-cases of CookieAction and NodeAction that I can think of or find
reflected in the sources (actually, see the patch I'm about to attach - sorry :-)).

The key method is ContextAction.createIndirectAction(Class<T extends LookupProvider>, ContextAction<?>, boolean) (I'm
starting to think these things might want to be in their own package or o.o.util.actions).  You pass in a type that is a
subclass of Lookup.Provider, such as Node, and an instance of ContextAction which is parameterized on some type that
will be in the Node's lookup.  It takes care of all of the delegation;  if you want to chain things so you just write an
action paramaterized on some object in the project's lookup, you can just call createIndirectAction() with the result of
another call to createIndirectAction(), and the action with the actual logic at the end of the chain.

So, if you want to write an action that is sensitive to ClassPathProviders in the current project, and reject any cases
where a selected node does not contain a project, or where the selected node's project does not contain a
ClassPathProvider, it's two lines of code:

ContextAction<Foo> theRealAction = new MyContextAction(); //the ClassPathProvider sensitive action
Action<Node> theGlobalAction = createIndirectAction(Node.class,
  createIndirectAction(Project.class, theRealAction));

If you do want the action enabled when some nodes don't have a project or some projects don't have a ClassPathProvider,
just use the overload of createIndirectAction that takes a boolean parameter for whether it requires all
Lookup.Providers to provide something usable.

So, Jarda, if you want to make all this declarative, you just need a ContextAction subclass that takes its values from a
file in a layer.xml (or annotation or whatever) and instantiates the real ContextAction on demand, and a spec for
specifying the chaining of types (e.g. Utilities.actionsGlobalContext() -> Node.getLookup() -> Project.getLookup() ->
type).  

And naturally, if you can specify declaratively whether the action needs to run any logic to determine its enabled
state, or if presence/absence, instance count and whether any Lookup.Providers cannot provide an instance of type is
enough, you can avoid loading the action until invocation.

Comment 6 _ tboudreau 2008-11-20 04:02:06 UTC
Created attachment 73934 [details]
And another one (added parameter to have createIndirectAction optionally reject enablement if any Lookup.Providers don't have an object they can use)
Comment 7 _ tboudreau 2008-11-20 05:55:06 UTC
Created attachment 73936 [details]
Convenience replacement for migrating NodeActions with fewer code changes
Comment 8 Jesse Glick 2008-11-20 22:53:00 UTC
This is a pretty major new API. I think we need to review it carefully and make sure it covers realistic use cases (and
no others).


[JG01] A common case that occurs in the project system is that you want enablement to be sensitive to (some
characteristics of) a Project in the selection, or if there is no Project in the selection, the Project which owns the
DataObject in the selection. This lets you press F6 when selecting the project root node, or when selecting any file in
the project. There are a number of rather subtle Action base classes in projectui to handle this and other scenarios. Do
you plan to support anything like this?

No one would use your "indirect" actions in the way you suggest, because file selections do not provide a Project
directly. Even besides that, the suggested use of indirect actions is unlikely because you would not want to look for a
Node in the selection, you would directly look for a Project. (TopComponent's, hence Utilities.actionsGlobalContext,
automatically "splice" the selected Node[]s lookups into the main lookup, as well as including those Node[]s themselves.)


[JG02] Any support for setting the action label according to the selection? This is fairly common.


[JG03] One thing I think is very important is ability to hide an action, not just grey it out, when disabled. Currently
this requires an elaborate trick: a phony Action which is a ContextAwareAction, creating a phony Action which is a
Presenter.Menu (or Presenter.Popup), creating a phony JMenu which is a DynamicMenuContent, which finally creates the
actual JMenu[] you want to display (which you then have to somehow bind to one of the other actions so as get a label
and a event handler). There are a lot of long classes to do this nonsense in our codebase. It would be great to simplify
them with a factory.


[JG04] You again used '.' where you should have used '#' in a Javadoc @see tag to refer to a class member. This will not
work.


[JG05] Don't bother with the HelpCtx.Provider impl.


[JG06] Use Parameters.notNull.


[JG07] NodeContextAction Javadoc says you can override isEnabled, but it is final in ContextAction.


[JG08] checkQuantity has no Javadoc.


[JG09] @param name is wrong in actionPerformed.


[JG10] Node/CookieAction took some care to suspend listening to changes in the global selection while they were not
displayed - i.e. if removeNotify was called, meaning there were no active listeners on them. This would be true for a
menu item (with no visible toolbar button): there is no reason to update its enablement state just because the user is
moving around in the Projects tab, unless and until the user actually posts that menu. Do you plan a similar optimization?


[JG11] hashCode oddly computes the constant 47 * 7, then adds something to it. Anyway I would suggest that equals and
hashCode should _not_ be overridden. I do not see any reason for it.


[JG12] Why does SurviveSelectionChange extend Single? This seems orthogonal. In fact I think it should be a constructor
parameter of ContextAction.

Also, such actions are in fact quite common. For instance, all the project system actions (that I know of) survive focus
change. Otherwise it would be rather irritating to have the Output Window get focus - you would need to switch back to
rerun a project etc.


[JG12] All these classes should be top-level, in a proper package. Probably in a new API module.


[JG13] CookieAction correctly handled the case that a selected node dynamically added or removed a cookie. In practice
this was unusual, mainly applicable to SaveCookie (which I want deprecated anyway - filed elsewhere). Does
LookupProviderAction handle this?


[JG14] It is confusing that LookupProviderAction has enabled(Collection<? extends T>) as well as isEnabled(Collection<?
extends R>) as well as isEnabled()!
Comment 9 _ tboudreau 2008-11-23 03:25:58 UTC
> This is a pretty major new API. I think we need to review it carefully and make sure it covers realistic use cases (and
> no others).

Fair enough.  To clarify how this all works:
The key class is ActionStub.  A ContextAction keeps its enablement state (when it keeps anyway, i.e. only between add/removeNotify), in its ActionStub - 
i.e. just a ContextAwareInstance over Utilities.actionsGlobalContext().  isEnabled(Collection) and actionPerformed(Collection) are callbacks.  So, everything 
lives in one place; the rest is delegation code.  So, replace the stub and replace the behavior - i.e . SurviveFocusChange just keeps the last-known-good 
collection of values.

IndirectAction just wraps another ContextAction, and so its context-aware-instances will be a similar wrapper over a context aware instance from the 
delegate.  An IndirectAction can do anything you can do with a LookupProviderAction;  LookupProviderAction simply is more understandable to Java 
programmers who are used to subclassing and overriding a method. 

> [JG01] A common case that occurs in the project system is that you want enablement to be sensitive to (some
> characteristics of) a Project in the selection, or if there is no Project in the selection, the Project which owns the
> DataObject in the selection. This lets you press F6 when selecting the project root node, or when selecting any file in
> the project. There are a number of rather subtle Action base classes in projectui to handle this and other scenarios. Do
> you plan to support anything like this?

I am working on this.  Basically, rather than have complicated enablement logic in one action, you implement each case as a simple ContextAction.  You 
provide one action for each special-case for enablement and generate an action that merges them with a factory method. So, for the scenario you describe, 
you would write an action that was sensitive to Projects, period.  Make two instances of it.  Then:

ContextAction<Project> caresAboutGlobalLookup = new MyProjectAction();
ContextAction<Project> caresAboutDataObject = ContextAction.indirect (DataObject.class, new MyProjectAction());
ContextAction<?> toDeploy = ContextAction.merge (caresAboutGlobalLookup, caresAboutDataObject);

The enablement logic gets a little complicated for the action that merges these, but I think there is a correct algorithm:  if any merged action is enabled, 
the merging action is enabled.  For return values of getValue(), if any actions are enabled, the first enabled one's value is returned (so the active action 
controls the name).  If all delegates are disabled, then the first non-null value, if any, is returned.

Change firing needs a bit of thought, but should be able to do accurately.

Basically, I think nobody should be writing complex enablement logic for any action except in really unusual circumstances - i.e. something much more 
complicated than testing object presence/absence;  and even in that case it shouldn't ever be something more complicated than, say, running 
FileOwnerQuery over a DataObject.  People will have fewer bugs if complex cases are separated into a couple of simple cases, and the API we provide 
handles merging two or more simple cases appropriately.  And such simpler actions will be more testable and likely to be tested thoroughly.

> No one would use your "indirect" actions in the way you suggest, because file selections do not provide a Project
> directly. 

Just write one action sensitive to Project, and one action sensitive to DataObject, which overrides enabled() and uses FileOwnerQuery to determine the 
return value, and fetches the Project the DataObject in actionPerformed().

(If one were lookup-happy, it would be fairly trivial to provide a Lookup which listens for changes in the selected DataObject and provides a Lookup that (if 
something is listening to it), sticks the result of FileOwnerQuery into it on file changes. That's kind of silly for anyone to do in their own code, but it 
wouldn't necessarily be bad for the Project UI API to provide such a global lookup - the actions people write to handle this case would end up much 
simpler.

> Even besides that, the suggested use of indirect actions is unlikely because you would not want to look for a
> Node in the selection, you would directly look for a Project. (TopComponent's, hence Utilities.actionsGlobalContext,
> automatically "splice" the selected Node[]s lookups into the main lookup, as well as including those Node[]s themselves.)

In that case.  But say you want to write an OpenAction or similar and put it in a toolbar.  Utilities.actionsGlobalContext() will just give you a pile of 
OpenCookies.  If you want the action to be disabled when one node is selected which contains no OpenCookie, you cannot use 
Utilities.actionsGlobalContext() directly;  you actually need the lookup of the Node.  IndirectAction or LookupProviderAction give you this for free if you 
pass true for the boolean constructor argument (i.e. you indicate that all lookups must contain the trigger type for the action to be enabled).

> [JG02] Any support for setting the action label according to the selection? This is fairly common.

Probably need a
protected void contentChanged(Collection<? extends T> content)
method to intercept non-enablement-changing changes in the lookup result and call setValue(NAME, ...) if necessary.

> [JG03] One thing I think is very important is ability to hide an action, not just grey it out, when disabled. Currently
> this requires an elaborate trick: a phony Action which is a ContextAwareAction, creating a phony Action which is a
> Presenter.Menu (or Presenter.Popup), creating a phony JMenu which is a DynamicMenuContent, which finally creates the
> actual JMenu[] you want to display (which you then have to somehow bind to one of the other actions so as get a label
> and a event handler). There are a lot of long classes to do this nonsense in our codebase. It would be great to simplify
> them with a factory.

Plenty of ways to express this in an action putValue ("invisibleWhenDisabled") or a constructor parameter or similar. Or "implements 
Presenter.InvisibleWhenDisabled".  Also would be easy enough to provide a factory that wraps the action in an implementation of DynamicMenuContent 
that does this + getter.  I wasn't planning to address presentation with this API (i.e. just address writing actions, not write a thing that creates UI 
components for them.  Certainly it could be done more cleanly than it is done now.

> [JG07] NodeContextAction Javadoc says you can override isEnabled, but it is final in ContextAction.
Will fix.  Not sure if NodeContextAction is necessary or not;  depends how many NodeActions that are not in our APIs are out there.  It would make it easier 
to migrate;  the question is how many things out there need migrating.

> [JG10] Node/CookieAction took some care to suspend listening to changes in the global selection while they were not
> displayed - i.e. if removeNotify was called, meaning there were no active listeners on them. This would be true for a
> menu item (with no visible toolbar button): there is no reason to update its enablement state just because the user is
> moving around in the Projects tab, unless and until the user actually posts that menu. Do you plan a similar optimization?

It's already there.  See ContextAction.addNotify().  For indirect actions and so forth, this cascades through the delegate actions, and I've got tests that it 
works correctly.

> [JG11] hashCode oddly computes the constant 47 * 7, then adds something to it. 
Auto generated crap.  Already removed.

> [JG12] Why does SurviveSelectionChange extend Single? This seems orthogonal. In fact I think it should be a constructor
> parameter of ContextAction.
Done.

> [JG12] All these classes should be top-level, in a proper package. Probably in a new API module.
I could put something in contrib, or someplace else.  Any preferences?  Guessing this should be o.n.api.actions

> [JG13] CookieAction correctly handled the case that a selected node dynamically added or removed a cookie. In practice
>this was unusual, mainly applicable to SaveCookie (which I want deprecated anyway - filed elsewhere). 

Where and why?

The only alternative that comes to mind is an equivalent which you for changes on.  That forces actions that depend on it to listen on the SaveCookie for 
changes, which means people are more likely to get that wrong than just writing a ContextAction that automatically handles the enablement logic for free.

> Does LookupProviderAction handle this?
Yes.  On addNotify, an action (and any delegates) start listening to the context they're rooted on.  They will fire changes as needed.  If enabled() is not 
overridden, then the presence/absence (and count, if overridden to be something other than > 1) determines enablement.  If enabled() is overridden it can 
do more complex things, although I don't expect this to be commonly necessary.  For any depth of delegation for IndirectAction, starting/stopping 
listening will cascade through the chain of actions.

> [JG14] It is confusing that LookupProviderAction has enabled(Collection<? extends T>) as well as isEnabled(Collection<?
> extends R>) as well as isEnabled()!

Suggestions welcome.
public void actionPerformed (Collection<? extends T>()
has the same erasure as 
public void actionPerformed (Collection<? extends R>()
so the result is uncompilable.  Same for isEnabled().

I hate all of the odd method names in our existing API (isEnabled(), enabled(), actionPerformed(), performAction(), perform()).  The only way I can think to 
handle it is probably uglier, i.e.
/* non public */ class Base<T> extends AbstractAction {
   public void _actionPerformed(Collection<T>) {}
}
and each subclass overrides actionPerformed to delegate to an actionPerformed() with appropriately parameterized().  But then we're exporting a non-
public superclass (or a useless public one), which seems more ugly.  If there's a way I'm not thinking of, let me know.
Comment 10 _ tboudreau 2008-11-23 03:34:33 UTC
BTW, I'm aware that there are cases where it would be better to use lookup items, rather than the result of allInstances().  I'm assuming if this becomes a real 
performance issue, it's easy enough to write a wrapper collection that translates them on the fly.
Comment 11 _ tboudreau 2008-11-23 13:21:30 UTC
when it keeps anyway -> when it keeps any :-)
Comment 12 _ tboudreau 2008-11-25 08:43:32 UTC
Draft version now in Hg with friend module in mobility. spi.actions (I would have put it in o.n.m.spi.actions but
apparently the build is set up to fail if the folder name is not spi.actions if that's the module name - weird).

http://hg.netbeans.org/main/rev/722d860e854a

Merged actions is working.
Comment 13 Jesse Glick 2008-11-25 16:03:27 UTC
For main and contrib modules, the dir name must be derived in a fixed manner from the module's CNB. Unfortunately this
transformation is not fully reversible, due to pressure to keep dir names short. I think you meant to use
org.netbeans.spi.actions as the CNB, not org.netbeans.modules.spi.actions.
Comment 14 Jesse Glick 2008-11-25 16:04:27 UTC
BTW you don't need a test dep on openide.util if you already have a regular module dep on it.
Comment 15 _ tboudreau 2008-11-25 22:25:33 UTC
Created attachment 74147 [details]
Demo module - creates an action equivalent to a project sensitive action (sensitive to main project, project in node lookup or project owning selected file)
Comment 16 Quality Engineering 2008-11-26 05:18:39 UTC
Integrated into 'main-golden', will be available in build *200811260201* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/722d860e854a
User: tboudreau@netbeans.org
Log: #153442 - initial draft of context actions SPI
Comment 17 _ tboudreau 2008-11-26 15:07:35 UTC
> Integrated into 'main-golden', will be available in build *200811260201*

Well, not really - I didn't add it to the module lists in nbbuild yet, since I haven't yet integrated anything that
needs to use it.
Comment 18 _ tboudreau 2008-12-01 14:59:00 UTC
In builds as of 5e5a20ffb742, but requires client modules to be friend or impl dependency.

The foremost open code question at this point is if/whether/how to implement Presenter.* on ActionStub.  In particular,
we ought to think about how to make certain common cases simpler - for example, making an invisible-when-disabled action
currently requires implementing DynamicMenuContent.  It should be as simple as putValue ("invisibleWhenDisabled", true).


Comment 19 _ tboudreau 2009-06-14 19:55:13 UTC
I had an epiphany about this project last night:  With Jarda's work on declarative actions (very little overlap with
this project, really), the need to implement Presenter.* or DynamicMenuContent eliminated.  If you are:
 - Implementing a submenu and need Presenter for that, your action contains no real logic anyway, so you probably are
not using this library, just a regular AbstractAction
 - Implementing a main menu or toolbar item, you will probably use Jarda's declarative stuff
 - Need a custom toolbar presenter, you are probably not really writing an action anyway, just using an action to
piggyback some component into the toolbar
 - Need menu items, you can just write multiple actions

Jarda's declarative stuff fairly handles the case of main menu actions.  However, the things this library addresses are
fairly orthagonal to that (although they could be used for it if you want): 
 - if you want to write actions that appear on a Node's popup menu or in a TopComponent that are context-sensitive and
do not want to write them as inner classes, or allow pluggability of them
 - The drill-through lookups scenario - make an action that is sensitive to Y's in the Lookup of X's in the global
selection context
 - Ease of writing both single-object and multiple-object sensitive actions

With the requirement of handling presenters removed, it appears this API has passed the fast-track-review process as
described at http://openide.netbeans.org/tutorial/review-steps.html 7 months ago.  The code has been in use in Mobility
since December w/ no problems;  and the core of it in Imagine for years before that - it is stable.  Therefore, in 24
hours, I will remove the friend restriction on spi.actions.
Comment 21 Jaroslav Tulach 2009-06-15 09:52:05 UTC
I'd like real review in the new context of work on 
http://wiki.netbeans.org/DeclarativeActionRegistration
I am not convinced the overlap is marginal.
Comment 22 _ tboudreau 2009-06-15 10:31:45 UTC
Disagree.  There are a large number of concerns that http://wiki.netbeans.org/DeclarativeActionRegistration does not
cover and that spi.actions already does.  The most important is API usability.  Complicated annotations are fine for
generated code, but for people who want to just write straightforward Java code, we need to offer them a way to do that.  

The work on spi.actions is complete and solves the usability problem;  you are offering a proposal that does solve
performance problem but does not address API usability, and which is a proposal, not a finished piece of work that has
already been in production code for a release.  spi.actions is stable and well tested code.

Declarative actions are good for static menu items and toolbar items;  if you are writing an Action that should be
sensitive to some object in the lookup of a Node, TopComponent, etc., it should not require magic annotations or
lookups, just straightforward Java code.  If I have both a TopComponent and a Node that should contain the same action,
I should not have to write two inner action classes to solve that problem;  and the amount of code required to properly
implement ContextAwareAction is too heavyweight (as evidenced by the fact that the apisupport module still gives you a
CookieAction).

If you want to block an existing, tested, working library from becoming generally available to platform users, please
specify precisely the areas in which you think there really is overlap, why overlap is a problem, and what is gained by
not making such functionality available to platform users now rather than later.
Comment 23 _ tboudreau 2009-06-15 10:38:48 UTC
Huh?

http://deadlock.netbeans.org/hudson/job/trunk/6011/testReport/org.netbeans.modules.ide.ergonomics/AvailableJ2EEServerCheck/testGetAllJ2eeServersErgo/

This is some kind of glassfish-related failure and has nothing to do with anybody's actions APIs.  ??
Comment 24 Jaroslav Tulach 2009-06-17 16:47:19 UTC
Tim, I'd be more than happy to let you provide better infrastructure for Actions in 6.8 timeframe. However the current 
state of spi.actions is not good enough. It does not solve any of performance problems we are facing, it does not 
include updates to apisupport wizards and thus it is not ready for integration.

If you believe you can update your API to all requirements that will be gathered during initial inception review, and 
you have permission to implement them in 6.8 release, then start the review process.

I'd be more than happy to give up on (already approved) issue 166658, in case I gain confidence that your work will 
meet performance requirements (or any other brought up during the inception review) and that you will finish the work 
for 6.8. Until that confidence is gained I'll have to proceed with my work on issue 166658 however.

Comment 25 Jesse Glick 2009-06-17 17:07:37 UTC
This API targets usability - ease of creating advanced action implementations. It is not an attempt to implement lazy
class loading, which is valuable for some but not all action implementations: typically those placed in prominent parts
of the global UI, which must be registered declaratively (for example in the style suggested in
DeclarativeActionRegistration), and probably cannot afford to have nontrivial display or enablement semantics.

In this sense the issues do not overlap, though one could certainly imagine in the future the lazy registrations for
context-sensitive actions being extended to work well with the Java API proposed here. For example, the lazy
registration could specify a static icon and display name, and use a limited and simplified version of enablement logic
("enable iff >=1 node with o.n.api.db.DatabaseTable is selected"); if and when first selected, the real delegate would
be loaded and be able to use the richer API to control all aspects of display and enablement henceforth.

Probably all that needs to be done in issue #166658 for this to work is to ensure that proxy actions delegate all
methods (not just actionPerformed) to the real action after it is loaded - which Actions.alwaysEnabled already does.
(One minor complication with context-sensitive actions is that the real action might consider the context passed the
_first_ time it is selected in a VM session to be inappropriate, due to a more refined enablement check. This could be
handled by the real action directly - beep, or show an error dialog - or the proxy could do this automatically.)

All this said, API_REVIEW seems more appropriate for a fairly fundamental API, especially one which is not trivial.
There should be broader usage in widely divergent modules by different authors, and development-time support.
Comment 26 Quality Engineering 2009-07-01 17:13:34 UTC
Integrated into 'main-golden', will be available in build *200907011400* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main-golden/rev/63118025ed38
User: Jaroslav Tulach <jtulach@netbeans.org>
Log: Sort of addresses #153442 - adding migration guide for users of old actions to switch to new Actions.* factories
Comment 27 _ tboudreau 2009-07-16 08:06:19 UTC
> Sort of addresses #153442

"Sort of" being the point.  There is a need for a clear, simple API for writing context-aware actions using plain Java code.  

Anybody writing multiple Nodes or TopComponents that may contain an object of a type actions should be registered against needs a way to write that action 
once, simply and clearly in plain Java code.  

In that case, there is no startup overhead, because we are not talking about global action registration;  and in fact, it is more processing work (i.e. slower) to 
resolve a declaratively registered action to populate a popup menu than to run a simple constructor for say, new Single<Foo>() - for this use case, declarative 
actions are both more confusing to code and accomplish the opposite of their goal, which is to improve performance.
Comment 28 Jaroslav Tulach 2009-07-16 12:48:33 UTC
I think I agree. Just keep in mind these things:

1. Actions as you describe them prevent users to assign them some shortcut. I consider this a major flaw. Every 
application I use (e.g. KDE apps) define pool of actions and allows me to assign to shortcut to every action provided 
by the application.

2. There is an API for non-declarative creation of Actions.context and Actions.callback (this is the "sort of"). 
Through I am sure there can be extensions and improvements to functionality provided by this API

3. Actions as you describe them have huge troubles with multi selection. This may not be problem all the time (e.g. 
Single<T> is fine), but often is and without some global identity it is unavoidable.
Comment 29 _ tboudreau 2009-07-16 19:50:42 UTC
> 3. Actions as you describe them have huge troubles with multi selection. 
> This may not be problem all the time (e.g. Single<T> is fine), but often is and without some global identity it is unavoidable.

Could you explain what you mean by this?  ContextAction<T> with a collection should work fine, without any need for it to be a singleton - if the system is 
handling ContextAwareAction correctly, everything should work.  So I think you must mean something else, but I don't know what.

Re shortcuts, primarily we're talking about actions that operate on some object but which are local to a Node or TopComponent.  Presumably if someone 
wants a keybinding local to their TC, they can do it in the normal way with InputMap/ActionMap for a TC, and create it on the fly for their Nodes (we don't 
really solve the problem of Node-specific keybindings for explorer views today anyway - although it could be useful to solve it), and all should be well.
Comment 30 Jesse Glick 2009-09-21 17:11:49 UTC
I think jtulach's #3 referred to the fact that when NodeOp creates a context menu from a multiselection, it displays
only those Action's which are present on all nodes (using equals() but it is recommended for them to be ==). This is not
an issue with globally registered actions, since you create a single global Action instance which different nodes refer
to, so merged context menus work as you might expect.


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