Here's a capability for opening something: package no.imr.viewer; public interface OpenCapability { public void open(); }
In my DataObject, I assign the OpenCapability to the dynamic Lookup of the DataObject, take note of the code in bold below: public class IMRCategoryDataObject extends MultiDataObject { InstanceContent content = new InstanceContent(); public IMRCategoryDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { super(pf, loader); CookieSet cookies = getCookieSet(); cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies)); } @Override protected Node createNodeDelegate() { final DataNode node = new DataNode(this, Children.LEAF, new ProxyLookup(getLookup(), new AbstractLookup(content))); content.add(new OpenCapability() { @Override public void open() { StatusDisplayer.getDefault().setStatusText("Open category " + node.getName()); } }); return node; } @Override public Lookup getLookup() { return getCookieSet().getLookup(); } }
(Alternatively, use "getCookieSet.assign(OpenCapability.class", oc), if you want to use cookies, which is simply an outdated pattern replaced by Lookup.)
I then use the New Action wizard to create a new "OpenCategoryAction" action that is sensitive to "OpenCapability" objects (which is added to the dynamic content of the DataObject, as shown above). Here's the layer entry generated:
It is registered for the MIME type of the node, which I created via the New File Type dialog, so will appear in the right-click contextual menu item of my node, having the same appearance for each node, but with behavior that depends on the particular node in question (thanks to the dynamicity of the Lookup used in the definition of the DataObject, as shown above). Here's how the ActionListener is registered (by the wizard automatically) in the layer: ... ... ...
And here's how the ActionListener is defined, simply getting the capability injected into it, from where the method is called, which behaves context sensitively, i.e., as defined in the DataObject above:
public final class OpenCategoryAction implements ActionListener { private final OpenCapability context; public OpenCategoryAction(OpenCapability context) { this.context = context; } public void actionPerformed(ActionEvent ev) {; } }
That's how a capability can be added to a Node, letting me dynamically compose the capabilities of my Node as and when needed. Currently, the "OpenCapability" is set in the definition of the DataObject, but since the InstanceContent is in the Lookup of the DataObject, we can dynamically add/remove the OpenCapability to/from the Node's Lookup as needed.

Read More about [Creating Context-Sensitive Capabilities for File-Based Nodes...