This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 198731 - Extensible action annotations
Summary: Extensible action annotations
Status: RESOLVED WONTFIX
Alias: None
Product: platform
Classification: Unclassified
Component: Actions (show other bugs)
Version: 7.1
Hardware: PC Windows XP
: P2 normal (vote)
Assignee: Jaroslav Tulach
URL:
Keywords: API
Depends on: 199095
Blocks:
  Show dependency tree
 
Reported: 2011-05-19 12:49 UTC by Geertjan Wielenga
Modified: 2011-06-21 22:51 UTC (History)
1 user (show)

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments
Example, using bug #199095 fix (11.24 KB, application/zip)
2011-06-21 22:51 UTC, Jesse Glick
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Geertjan Wielenga 2011-05-19 12:49:01 UTC
It would be great if action annotations would support non-predefined attributes. For example:

@ActionSupplementals("tooltipTitle=org.demo.Bundle#CTL_SaveActionTitle")

That would create the attribute "tooltipTitle" below and, because there is a hash on the right side, the value would be a bundle value.

That would

<folder name="File">
        <file name="org-openide-actions-SaveAction.instance">
            <attr name="instanceCreate" methodvalue="org.openide.awt.Actions.context"/>
            <attr name="delegate" newvalue="org.openide.actions.SaveAction"/>
            <attr name="selectionType" stringvalue="EXACTLY_ONE"/>
            <attr name="surviveFocusChange" boolvalue="false"/>
            <attr name="displayName" bundlevalue="org/openide/actions/Bundle#Save"/>
            <attr name="noIconInMenu" boolvalue="false"/>
            <attr name="iconBase" stringvalue="org/openide/resources/actions/save.png"/>
            <attr name="type" stringvalue="org.openide.cookies.SaveCookie"/>
            <attr name="tooltipTitle" bundlevalue="org.netbeans.paint.Bundle#CTL_SaveActionTitle"/>
        </file>
</folder>

The above would be very useful for developers providing layer registrations on top of the default items provided by NetBeans, e.g.,:

http://platform.netbeans.org/tutorials/nbm-ribbonbar.html
Comment 1 Jaroslav Tulach 2011-05-24 05:51:33 UTC
Yes, this is not bad idea. Can you donate an API patch?
Comment 2 Jaroslav Tulach 2011-05-30 19:13:46 UTC
I don't have time for this, I need help. Without it, the status is won'tfix.
Comment 3 Geertjan Wielenga 2011-05-31 09:13:04 UTC
I propose this:

@ActionID(category = "Edit",
id = "org.demo.SomeAction")
@ActionRegistration(displayName = "#CTL_SomeAction")
@ActionReferences({
    @ActionReference(path = "Menu/File", position = 1300)
})
@ActionSupplementals({
    @ActionSupplemental(key = "foo1", value = "bar1"),
    @ActionSupplemental(key = "foo2", value = "bar2")
})
@Messages("CTL_SomeAction=Some")
public final class SomeAction implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO implement action body
    }
}
Comment 4 Geertjan Wielenga 2011-05-31 09:14:48 UTC
And this in the layer:

<folder name="Menu">
    <folder name="File">
        <file name="org-demo-SomeAction.shadow">
            <attr name="originalFile" stringvalue="Actions/Edit/org-demo-SomeAction.instance"/>
            <attr intvalue="1300" name="position"/>
            <attr name="foo1" stringvalue="bar1"/>
            <attr name="foo2" stringvalue="bar2"/>
        </file>
    </folder>
</folder>
Comment 5 Jaroslav Tulach 2011-05-31 10:48:49 UTC
I don't think you should generate the attributes to the .shadow file. Rather generate them for the .instance file. Otherwise you can go on and provide a patch.
Comment 6 Geertjan Wielenga 2011-05-31 12:38:53 UTC
Yes, that's exactly what I ended up doing, i.e., not in the shadow file, but in the instance:

http://blogs.oracle.com/geertjan/entry/actionsupplemental
Comment 7 Jesse Glick 2011-05-31 14:25:55 UTC
I do not think this is a good idea. Since no Platform module interprets attributes like these, it is just confusing for there to be an annotation in the Platform which defines them. The generic annotation is also unnecessarily clumsy for the user, and the generic processor cannot perform any useful validation.

Rather, whatever module defines infrastructure for the ribbon bar should also define e.g.

@RibbonInfo(description="#whatever", ...)

plus a processor that writes out the relevant attributes on the *.instance file (identified by @ActionID), after performing any relevant validation (e.g. that tooltipFooterIcon actually exists).
Comment 8 Geertjan Wielenga 2011-05-31 14:51:15 UTC
I don't think it's confusing or clumsy. I think it's helpful to have placeholders for custom items that need to be registered with actions. Otherwise there's a lot of work to be done, which in itself is also confusing. Definitely less simple than using @ActionSupplemental.
Comment 9 Jesse Glick 2011-05-31 21:51:51 UTC
(In reply to comment #8)
> Otherwise there's a lot of work to be done

Not much, and only on the part of the ribbon _infrastructure provider_ (not every ribbon _button provider_), who is already writing specialized code to interpret these attributes at runtime.
Comment 10 bdschubert 2011-06-01 23:21:46 UTC
(In reply to comment #7)

> Rather, whatever module defines infrastructure for the ribbon bar should also
> define e.g.
> @RibbonInfo(description="#whatever", ...)

I'm new to annotations, but fell in love with them in NB7.0. Based on the nb source and Geertjan's blog, I tried to inject my own attributes into an action based on ActionID, but I ran into problems: my annotation processor overwrites the ActionRegistration.  It was a half-hearted attempt and I gave up on it as I had to get back into my problem domain.  But therein lies the problem from my perspective: how can I add content to an action registration without diverting my efforts (too much) from my problem domain.  

Is there a way to append/insert content?

public final class ActionSupplementalsProcessor extends LayerGeneratingProcessor
{
    @Override
    protected boolean handleProcess(Set<? extends TypeElement> annotations,
            RoundEnvironment env) throws LayerGenerationException  {
        for (Element e : env.getElementsAnnotatedWith(ActionID.class)) {
            ActionID aid = e.getAnnotation(ActionID.class);
            if (aid == null) {
                throw new LayerGenerationException("@ActionSupplemental(s) can only be used together with @ActionID annotation", e);
            }
            if (aid.category().startsWith("Actions/")) {
                throw new LayerGenerationException("@ActionID category() cannot contain /", e);
            }
            String id = aid.id().replace('.', '-');
            File f = layer(e).file("Actions/" + aid.category() + "/" + id + ".instance");

            ActionSupplemental asup = e.getAnnotation(ActionSupplemental.class);
            if (asup != null) {
                processSupplemental(e, asup, f);
            }
            ActionSupplementals refs = e.getAnnotation(ActionSupplementals.class);
            if (refs != null) {
                for (ActionSupplemental actionSupplemental : refs.value()) {
                    processSupplemental(e, actionSupplemental, f);
                }
            }
        }
        return true;
    }
Comment 11 bdschubert 2011-06-01 23:25:31 UTC
Additional code missing from my earlier comment.

    private void processSupplemental(Element e, ActionSupplemental sup, File f) throws LayerGenerationException
    {        
        f.stringvalue(sup.key(), sup.value());
        f.write();
    }
Comment 12 Jesse Glick 2011-06-02 00:08:19 UTC
(In reply to comment #10)
> my annotation processor overwrites the ActionRegistration

See bug #199095.
Comment 13 Jesse Glick 2011-06-21 22:51:04 UTC
Created attachment 109034 [details]
Example, using bug #199095 fix