X

Geertjan's Blog

  • May 6, 2009

@NodeFactory.Registration

Geertjan Wielenga
Product Manager
Following on from yesterday's blog entry, about the @AntBasedProjectRegistration annotation, let's now add a logical view for our project type, making use of the new @NodeFactory.Registration annotation in the process. That's a new 6.7 annotation that you use to add nodes to a logical view. Your default logical view does not need to have any nodes itself, other than the root node.

Here's a simple LogicalViewProvider:

import java.awt.Image;
import javax.swing.Action;
import org.netbeans.spi.project.ui.LogicalViewProvider;
import org.netbeans.spi.project.ui.support.CommonProjectActions;
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
class DemoProjectLogicalView implements LogicalViewProvider {
private final DemoProject project;
public DemoProjectLogicalView(DemoProject project) {
this.project = project;
}
@Override
public Node createLogicalView() {
return new RootNode(project);
}
private static final class RootNode extends AbstractNode {
public static final String DEMO_PROJECT_ICON_PATH =
"org/netbeans/demo/project/icon.png";public static final String REGISTERED_NODE_LOCATION =
"Projects/org-netbeans-demo-project-DemoProject/Nodes";

final DemoProject project;
public RootNode(DemoProject project) {super(NodeFactorySupport.createCompositeChildren
(project, REGISTERED_NODE_LOCATION));

this.project = project;
setIconBaseWithExtension(DEMO_PROJECT_ICON_PATH);
}
@Override
public Action[] getActions(boolean arg0) {
Action[] nodeActions = new Action[7];
nodeActions[0] = CommonProjectActions.newFileAction();
nodeActions[1] = CommonProjectActions.copyProjectAction();
nodeActions[2] = CommonProjectActions.deleteProjectAction();
nodeActions[5] = CommonProjectActions.setAsMainProjectAction();
nodeActions[6] = CommonProjectActions.closeProjectAction();
return nodeActions;
}
@Override
public Image getIcon(int type) {
return ImageUtilities.loadImage(DEMO_PROJECT_ICON_PATH);
}
@Override
public Image getOpenedIcon(int type) {
return getIcon(type);
}
@Override
public String getDisplayName() {
return project.getProjectDirectory().getName();
}
}
@Override
public Node findPath(Node root, Object target) {
//leave unimplemented for now
return null;
}
}

Note: After defining the above, you need to add the DemoProjectLogicalView to the Lookup of the DemoProject. I.e., in the DemoProject's Lookup, instantiate the DemoProjectLogicalView and pass in "this", which is the DemoProject:

@Override
public Lookup getLookup() {
return Lookups.fixed(new Object[]{
new Info(),
new DemoProjectLogicalView(this)
});
}

What's interesting in the DemoProjectLogicalView, defined above, are the lines highlighted in bold. They specify that the children of the root node must be retrieved from the "Projects/org-netbeans-demo-project-DemoProject/Nodes" folder. In the past, i.e., prior to 6.7, that could only be a reference to a folder in the layer.xml file, as described in the section "Registering the New Extensions" in the NetBeans Project Type Extension Module Tutorial.

Now, however, you simply need an annotation (highlighted in bold in the code below)! Forget the whole layer.xml file, some more. Here's a simplistic NodeFactory, that registers itself as an extension to the DemoProject type, using the type that was defined in the @AntBasedProjectRegistration shown yesterday:

import org.netbeans.api.project.Project;
import org.netbeans.spi.project.ui.support.NodeFactory;
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
import org.netbeans.spi.project.ui.support.NodeList;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
@NodeFactory.Registration(projectType="org-netbeans-demo-project-DemoProject", position=200)
public class DemoNodeFactory implements NodeFactory {
public DemoNodeFactory() {
}
@Override
public NodeList createNodes(Project arg0) {
AbstractNode nd = new AbstractNode(Children.LEAF);
nd.setDisplayName("Hello World!");
return NodeFactorySupport.fixedNodeList(nd);
}
}

For a more realistic example of a NodeFactory, see the NetBeans Project Type Extension Module Tutorial. In the above rather trivial example, you'd end up with a logical view like this:

...while the Files window continues to show the file structure on disk:

Also, thanks to the @NodeFactory.Registration annotation, ANYONE can extend my project type. So, let's say the application (whatever it is, insurance application, oil flow application, etc) is now out in the field and being used by customers. One of them thinks, "Hmmm. Would be cool if the project type had a few additional nodes in the logical view." All they'd need to do would be to create a new module, define a NodeFactory, register it to the project type via the @NodeFactory.Registration annotation, build the NBM and deliver it to other customers... without needing to have access to the sources of the DemoProject! The only required info is the type of the project and then you can use that to register the NodeFactory via the annotation. In other words, this particular annotation enables a project type to be pluggable. Pretty cool.

Now, let's add the DemoProject to the root node's Lookup:

public RootNode(DemoProject project) {
super(NodeFactorySupport.createCompositeChildren
(project, REGISTERED_NODE_LOCATION), Lookups.singleton(project));
this.project = project;
setIconBaseWithExtension(DEMO_PROJECT_ICON_PATH);
}

That results in some of the items being enabled when you right-click the project in the logical view:

Plus, the highlighted items do exactly what you'd expect.

Join the discussion

Comments ( 6 )
  • Rich Unger Wednesday, May 6, 2009

    Well, anybody could extend your project type before, as well, by providing their own layer.xml file.


  • Geertjan Thursday, May 7, 2009

    Right, except that now it's done via annotations.


  • Geertjan Thursday, May 7, 2009

    (Although the layer.xml approach is still supported.)


  • venic Tuesday, July 7, 2009

    Hello! Can you help me.

    I have my class FactoryNodesInstance implements NodeFactory, and I have a special structure List<MyModels>, so I want to update project tree with my nodes. I have register FactoryNodesInstance in layer.xml. The first time it works and I can see nodes, but when some changes were made in List<MyModels>, I don't know how to call in class FactoryNodesInstance method node to refresh nodes?

    Thank you.


  • Farouk Alhassan Monday, July 26, 2010

    Hi Gj

    These tutorials are very helpful for creating new projecttypes. One thing am struggling to do is to add the source folder and my own jars to the classpath when the project is opened but I'm not getting anywhere. I have been looking in the java.j2seproject module for ideas especially J2SEProject.java

    One very typicall use of this is to create project types for your company that will have project dependencies and everything added to the classpath.

    Regards


  • Robert Bracko Thursday, June 16, 2016

    Great article, Geertjan! By your help, I succeeded to solve a task that looked quite complicated: "plugging" logical view of existing project type (J2SEProject) into the custom-design project (made by the company I work for).

    Thank you!


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.