X

Geertjan's Blog

  • January 23, 2011

org.netbeans.spi.project.SubprojectProvider (Part 1)

Geertjan Wielenga
Product Manager
Project types can be project type containers. Some examples of this can be seen in NetBeans IDE, such as the NetBeans module suite / NetBeans module construction. There's an API that lets you implement the same thing for your own project types.

Here's my first example of this, i.e., subproject support within project support:

Note: This blog entry is not about Ant-based project systems (even though the build.xml files in the screenshot above might give that impression, though they're just there as arbitrary files), but about project systems that simply exist to organize groups of folders and files.

Here's a description of how to create the above:

  1. Start from here:

    http://platform.netbeans.org/tutorials/nbm-projecttype.html

  2. The above describes how to create a single project type. So, in our case, we need two project types, one for the project and another for the subproject. So, create a second package and create a "Project", "ProjectFactory", and "ProjectLogicalView" for your subproject.

  3. Now, we're going to implement this class:

    org.netbeans.spi.project.SubprojectProvider

    For example, the above class could look in a project's "modules" folder for the subprojects:

    public class DemoSubprojectProvider implements SubprojectProvider {
    private final DemoProject project;
    public DemoSubprojectProvider(DemoProject project) {
    this.project = project;
    }
    @Override
    public Set getSubprojects() {
    return loadProjects(project.getProjectDirectory());
    }
    private Set loadProjects(FileObject dir) {
    Set newProjects = new HashSet();
    try {
    FileObject[] subFolders = dir.getChildren(); //subfolders of root folder
    for (FileObject oneSub : subFolders) {
    if (oneSub.getName().equals("modules")) {
    FileObject[] modulesFolder = oneSub.getChildren();
    for (FileObject oneModule : modulesFolder) {
    Project subp = ProjectManager.getDefault().findProject(oneModule);
    if (subp != null && subp instanceof DemoSubProject) {
    newProjects.add((DemoSubProject) subp);
    }
    }
    }
    }
    } catch (IOException e) {
    }
    return Collections.unmodifiableSet(newProjects);
    }
    @Override
    public final void addChangeListener(ChangeListener l) {}
    @Override
    public final void removeChangeListener(ChangeListener l) { }
    }

    At this point, we now have a list of subprojects.

  4. Now go to the "Project" class of the main project and then add the "SubprojectProvider" to the Lookup of the project (you will need it to be there in step 6 below):
    @Override
    public Lookup getLookup() {
    if (lkp == null) {
    lkp = Lookups.fixed(new Object[]{
    ...
    ...
    new DemoSubprojectProvider(this), //Subprojects of this project
    ...
    ...
    });
    }
    return lkp;
    }
  5. Next, we need to make the logical view of our project (i.e., NOT our subproject, unless you need this there too) pluggable, so that a new node can be added to it externally. Go to the "ProjectLogicalView" of the main project and then specify a folder where the nodes of the project will be found, e.g:
    private static final class TextNode extends FilterNode {
    final DemoProject project;
    public TextNode(Node node, DemoProject project) throws DataObjectNotFoundException {
    super(node, NodeFactorySupport.createCompositeChildren(project,
    "Projects/org-demo-project/Nodes"),
    new ProxyLookup(new Lookup[]{Lookups.singleton(project),
    node.getLookup()
    }));
    this.project = project;
    }
    ...
    ...
    ...

    Notice, above, the "NodeFactorySupport.createCompositeChildren" construction.
    Read about the above here: http://eppleton.de/blog/?p=1186

  6. Now, we create an implementation of NodeFactory, which is registered into the folder shown above via @NodeFactory.Registration:
    @NodeFactory.Registration(projectType = "org-demo-project")
    public class DemoSubprojectNode implements NodeFactory {
    static String REGISTERED_NODE_LOCATION = "Projects/org-demo-project/Nodes";
    @Override
    public NodeList createNodes(Project project) {
    DemoSubprojectProvider ds = project.getLookup().lookup(DemoSubprojectProvider.class);
    Set subprojects = ds.getSubprojects();
    return NodeFactorySupport.fixedNodeList(new SubModuleContainerNode(subprojects));
    }
    private static class SubModuleContainerNode extends AbstractNode {
    public SubModuleContainerNode(Set subprojects) {
    super(Children.create(new SubModuleNodeChildFactory(subprojects), true));
    setDisplayName("Modules");
    setIconBaseWithExtension("/org/demo/sub/project/type/arrow.jpg");
    }
    }
    ...
    ...
    ...

    Within this class, we look for the "SubprojectProvider" in the Lookup of the project, which is where we put it in step 4. Once we have that, we can call "getSubProjects" and then construct the Node hierarchy based on the projects returned from the SubprojectProvider.

  7. If you like, you can also create subprojects within the subprojects above, i.e., you'd have subsubprojects. That's also supported, in exactly the same way. (In fact, you could use the approach described in this blog entry to create tree hierarchies that visualize modular structures, with each 'module' in the structure having its own self-contained project behavior. The sky's the limit here.)

The important point is that the project knows its subprojects because subprojects are provided in the lookup of the project. Therefore, all the subprojects of all subproject types of all the currently selected projects could, in principle, behave together as a unit:

@ActionID(category = "Tools",
id = "org.demo.project.type.CompareAllSubprojectsAction")
@ActionRegistration(displayName = "#CTL_CompareAllSubprojectsAction")
@ActionReferences({
@ActionReference(path = "Menu/Tools", position = 0)
})
public final class CompareAllSubprojectsAction implements ActionListener {
private final List<Project> context;
public CompareAllSubprojectsAction(List<Project> context) {
this.context = context;
}
@Override
public void actionPerformed(ActionEvent ev) {
for (Project project : context) {
Collection<? extends SubprojectProvider> spProviders =
project.getLookup().lookupAll(SubprojectProvider.class);
for (SubprojectProvider subprojectProvider : spProviders) {
Set<? extends Project> subprojects = subprojectProvider.getSubprojects();
for (Project subProject : subprojects) {
//Print out the locations of all subprojects
//provided by all the selected projects:
System.out.println(subProject.getProjectDirectory().getPath());
}
}
}
}
}

However, if your project can have, say, hundreds of subprojects, you need to be careful in how you handle the opening and initialization sequences of your project system.

If considered helpful, I could make a complete sample code available to illustrate the above, though the instructions above should be sufficient.

Join the discussion

Comments ( 2 )
  • guest Tuesday, January 13, 2015

    What about the 'SubModuleNodeChildFactory' class used in 'SubModuleContainerNode'?


  • guest Monday, August 17, 2015

    https://forums.netbeans.org/viewtopic.php?p=165691#165691

    Followed Project Type Tutorial below to create the Project Type. The question is how to disable unwanted menu items in the Subproject and the low lever folders and files.

    The actions in the code below are what I want. These actions are available when I open the required sub-project. As show in image below, the select sub-project shows the listed action in the code below.

    Code:

    @Override

    public Action[] getActions(boolean arg0) {

    return new Action[]{

    CommonProjectActions.newFileAction(),

    CommonProjectActions.closeProjectAction(),

    SystemAction.get(PropertiesAction.class),

    SystemAction.get(OpenLocalExplorerAction.class)

    };

    }

    But if I select the same sub-project listed under the main project the action menu is different. How do I go about disabling or modifying these actions.

    I am aware that the main project loads these sub-projects in the CustomerSubProjectProvider

    Code:

    public class CustomerSubprojectProvider implements SubprojectProvider {

    private final CustomerProject _project;

    public CustomerSubprojectProvider(CustomerProject project) {

    this._project = project;

    }

    @Override

    public Set<? extends Project> getSubprojects() {

    return loadProjects(_project.getProjectDirectory());

    }

    private Set loadProjects(FileObject dir) {

    Set newProjects = new HashSet();

    FileObject reportsFolder = dir.getFileObject("texts");

    if (reportsFolder != null) {

    for (FileObject childFolder : reportsFolder.getChildren()) {

    try {

    Project subp = ProjectManager.getDefault().

    findProject(childFolder);

    if (subp != null) {

    if (subp instanceof ReportsSubProject) {

    newProjects.add((ReportsProject) subp);

    } else if (subp instanceof SubProject2) {

    newProjects.add((SubProject2) subp);

    }

    }

    } catch (IOException | IllegalArgumentException ex) {

    Exceptions.printStackTrace(ex);

    }

    }

    }

    return Collections.unmodifiableSet(newProjects);

    }

    @Override

    public void addChangeListener(ChangeListener cl) {

    }

    @Override

    public void removeChangeListener(ChangeListener cl) {

    }

    }

    Also looked in the Layer.xml, not sure about this one. Thank you for any input or advice!!!!


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