blob: af8582b8488a5c8a5b01cd630258099cf5f61bae [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= NetBeans Project Type Module Tutorial
:jbake-type: platform_tutorial
:jbake-tags: tutorials
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: NetBeans Project Type Module Tutorial - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, NetBeans Project Type Module Tutorial
This tutorial demonstrates how to create a new project type in an Ant-based NetBeans Platform application.
Rather than creating a new project type, you might want to extend an existing project type instead, in which case refer to the link:https://netbeans.apache.org/tutorials/nbm-projectextension.html[NetBeans Project Type Extension Module Tutorial]. For Maven-based NetBeans Platform applications, see link:http://netbeans.dzone.com/how-create-maven-nb-project-type[How to Create a Custom Project Type in a Mavenized NetBeans Platform Application]. If the projects for which you're creating a project type (whether on Ant or Maven based NetBeans Platform applications) need to use Ant as their build tool, you should use the link:https://netbeans.apache.org/tutorials/nbm-projecttypeant.html[NetBeans Ant-Based Project Type Module Tutorial] instead.
NOTE: This document uses NetBeans Platform 7.1 and NetBeans IDE 7.1. If you are using an earlier version, see link:../70/nbm-projecttype.html[the previous version of this document].
You will also make use of these two icons, which you can right-click here and download:
image::images/icon1.png[]
image::images/projecttypes_icon2.png[]
== Introduction to Project Types
A _project type_ is a NetBeans Platform term for a grouping of folders and files that is treated as a single unit. Treating related folders and files as a single unit makes working with them easier for the end user. One way in which a project type simplifies life for the user is that you are able to fill the Projects window only with those folders and files that the end user is most likely to work.
For example, the Java project type in NetBeans IDE helps the end user to work with the folders and files belonging to a single Java application. As you can see below, the folders and files the end user most needs to work with are shown in the Projects window:
image::images/projecttypes_70pics_sample-3.png[]
In this tutorial, we will create a project type that will look as follows in the Projects window and Files window:
image::images/projecttypes_70pics_nb-sample-proj6.png[]
Our project type will be defined by the existence of a subfolder named "texts". If a folder contains a subfolder with that name, the NetBeans Platform will recognize it as a project type. The user will be able to open the project into a NetBeans Platform application and the content of the "texts" folder will be displayed in the Projects window. The user will also be able to create new projects, via the New Projects window (Ctrl-Shift-N), which is where we will register two sample projects.
The following are the main NetBeans API classes we will be implementing in this tutorial:
|===
|Class |Description
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ProjectFactory.html[org.netbeans.spi.project.ProjectFactory]`` |Determines when a folder or file is a valid project and then creates the implemention of ``org.netbeans.api.project.Project`` .
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/Project.html[org.netbeans.api.project.Project]`` |Represents the project.
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/LogicalViewProvider.html[org.netbeans.spi.project.ui.LogicalViewProvider]`` |Provides the logical view for the project.
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/ProjectInformation.html[org.netbeans.api.project.ProjectInformation]`` |Provides supplemental information for the project.
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ActionProvider.html[org.netbeans.spi.project.ActionProvider]`` |Provides one or more actions for the project.
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/CopyOperationImplementation.html[org.netbeans.spi.project.CopyOperationImplementation]`` |Provides the Copy operation for the project.
| `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/DeleteOperationImplementation.html[org.netbeans.spi.project.DeleteOperationImplementation]`` |Provides the Delete operation for the project.
|===
At the end of this tutorial, your module source structure will be as follows:
image::images/projecttypes_71pics_result.png[]
== Creating the Module Project
We begin by working through the New Module Project wizard. At the end of it, we will have a basic source structure, with some default files, that every NetBeans module requires.
[start=1]
1. Choose File > New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules. Under Projects, select Module. Click Next.
[start=2]
1. In the Name and Location panel, type ``DemoProjectType`` in the Project Name field. Change the Project Location to any directory on your computer. Click Next.
[start=3]
1. In the Basic Module Configuration panel, type ``org.netbeans.demo.project`` in Code Name Base. Click Finish.
The IDE creates the ``DemoProjectType`` project. The project contains all of your sources and project metadata, such as the project's Ant build script. The project opens in the IDE. You can view its logical structure in the Projects window (Ctrl-1) and its file structure in the Files window (Ctrl-2).
== Setting Dependencies
We will need to make use of several NetBeans APIs. In this step, we select the modules that provide the NetBeans APIs that we will need.
[start=1]
1. Right-click the project node and choose Properties. The Project Properties dialog box opens.
[start=2]
1.
In the Libraries panel, add dependencies on the following modules:
* Datasystems API
* Dialogs API
* File System API
* Lookup API
* Nodes API
* Project API
* Project UI API
* UI Utilities API
* Utilities API
== Creating the Project Factory
We start by implementing the `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ProjectFactory.html[org.netbeans.spi.project.ProjectFactory]`` class.
[start=1]
1. Create a Java class named ``DemoProjectFactory`` .
[start=2]
1. Change the default code to the following:
[source,java]
----
@org.openide.util.lookup.ServiceProvider(service=ProjectFactory.class)
public class DemoProjectFactory implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ProjectFactory.html[ProjectFactory] {
public static final String PROJECT_DIR = "texts";
*//Specifies when a project is a project, i.e.,
//if the project directory "texts" is present:*
@Override
public boolean isProject(FileObject projectDirectory) {
return projectDirectory.getFileObject(PROJECT_DIR) != null;
}
*//Specifies when the project will be opened, i.e.,
//if the project exists:*
@Override
public Project loadProject(FileObject dir, ProjectState state) throws IOException {
return isProject(dir) ? new DemoProject(dir, state) : null;
}
@Override
public void saveProject(final Project project) throws IOException, ClassCastException {
FileObject projectRoot = project.getProjectDirectory();
if (projectRoot.getFileObject(PROJECT_DIR) == null) {
throw new IOException("Project dir " + projectRoot.getPath() +
" deleted," +
" cannot save project");
}
*//Force creation of the texts dir if it was deleted:*
((DemoProject) project).getTextFolder(true);
}
}
----
The @ServiceProvider annotation used in the class signature above will cause a META-INF/services file to be created when the module is compiled. Within that folder, a file named after the fully qualified name of the interface will be found, containing the fully qualified name of the implementing class. That is the standard JDK mechanism, since JDK 6, for registering implementations of interfaces. That is how project types are registered in the NetBeans Plaform.
== Creating the Project
Next, we implement the `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/Project.html[org.netbeans.api.project.Project]`` class.
[start=1]
1. Create a Java class named ``DemoProject`` .
[start=2]
1. Change the default code to the following:
[source,java]
----
class DemoProject implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/Project.html[Project] {
private final FileObject projectDir;
private final ProjectState state;
private Lookup lkp;
public DemoProject(FileObject projectDir, ProjectState state) {
this.projectDir = projectDir;
this.state = state;
}
@Override
public FileObject getProjectDirectory() {
return projectDir;
}
FileObject getTextFolder(boolean create) {
FileObject result =
projectDir.getFileObject(DemoProjectFactory.PROJECT_DIR);
if (result == null && create) {
try {
result = projectDir.createFolder(DemoProjectFactory.PROJECT_DIR);
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
return result;
}
*//The project type's capabilities are registered in the project's lookup:*
@Override
public Lookup getLookup() {
if (lkp == null) {
lkp = Lookups.fixed(new Object[]{
state, *//allow outside code to mark the project as needing saving*
new ActionProviderImpl(), *//Provides standard actions like Build and Clean*
new DemoDeleteOperation(),
new DemoCopyOperation(this),
new Info(), *//Project information implementation*
new DemoProjectLogicalView(this), *//Logical view of project implementation*
});
}
return lkp;
}
private final class ActionProviderImpl implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ActionProvider.html[ActionProvider] {
private String[] supported = new String[]{
ActionProvider.COMMAND_DELETE,
ActionProvider.COMMAND_COPY,
};
@Override
public String[] getSupportedActions() {
return supported;
}
@Override
public void invokeAction(String string, Lookup lookup) throws IllegalArgumentException {
if (string.equalsIgnoreCase(ActionProvider.COMMAND_DELETE)) {
DefaultProjectOperations.performDefaultDeleteOperation(DemoProject.this);
}
if (string.equalsIgnoreCase(ActionProvider.COMMAND_COPY)) {
DefaultProjectOperations.performDefaultCopyOperation(DemoProject.this);
}
}
@Override
public boolean isActionEnabled(String command, Lookup lookup) throws IllegalArgumentException {
if ((command.equals(ActionProvider.COMMAND_DELETE))) {
return true;
} else if ((command.equals(ActionProvider.COMMAND_COPY))) {
return true;
} else {
throw new IllegalArgumentException(command);
}
}
}
private final class DemoDeleteOperation implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/DeleteOperationImplementation.html[DeleteOperationImplementation] {
public void notifyDeleting() throws IOException {
}
public void notifyDeleted() throws IOException {
}
public List<FileObject> getMetadataFiles() {
List<FileObject> dataFiles = new ArrayList<FileObject>();
return dataFiles;
}
public List<FileObject> getDataFiles() {
List<FileObject> dataFiles = new ArrayList<FileObject>();
return dataFiles;
}
}
private final class DemoCopyOperation implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/CopyOperationImplementation.html[CopyOperationImplementation] {
private final DemoProject project;
private final FileObject projectDir;
public DemoCopyOperation(DemoProject project) {
this.project = project;
this.projectDir = project.getProjectDirectory();
}
public List<FileObject> getMetadataFiles() {
return Collections.EMPTY_LIST;
}
public List<FileObject> getDataFiles() {
return Collections.EMPTY_LIST;
}
public void notifyCopying() throws IOException {
}
public void notifyCopied(Project arg0, File arg1, String arg2) throws IOException {
}
}
private final class Info implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/ProjectInformation.html[ProjectInformation] {
@Override
public Icon getIcon() {
return new ImageIcon(ImageUtilities.loadImage(
"org/netbeans/demo/project/icon2.png"));
}
@Override
public String getName() {
return getProjectDirectory().getName();
}
@Override
public String getDisplayName() {
return getName();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener pcl) {
//do nothing, won't change
}
@Override
public void removePropertyChangeListener(PropertyChangeListener pcl) {
//do nothing, won't change
}
@Override
public Project getProject() {
return DemoProject.this;
}
}
}
----
== Creating the Logical View Provider
Finally, we implement the `` link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/LogicalViewProvider.html[org.netbeans.spi.project.ui.LogicalViewProvider]`` class.
[start=1]
1. Create a Java class named ``DemoProjectLogicalView`` .
[start=2]
1. Change the default code to the following:
[source,java]
----
class DemoProjectLogicalView implements link:http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/LogicalViewProvider.html[LogicalViewProvider] {
private final DemoProject project;
public DemoProjectLogicalView(DemoProject project) {
this.project = project;
}
@Override
public org.openide.nodes.Node createLogicalView() {
try {
*//Get the Text directory, creating if deleted*
FileObject text = project.getTextFolder(true);
*//Get the DataObject that represents it*
DataFolder textDataObject =
DataFolder.findFolder(text);
*//Get its default node-we'll wrap our node around it to change the
//display name, icon, etc*
Node realTextFolderNode = textDataObject.getNodeDelegate();
*//This FilterNode will be our project node*
return new TextNode(realTextFolderNode, project);
} catch (DataObjectNotFoundException donfe) {
Exceptions.printStackTrace(donfe);
*//Fallback-the directory couldn't be created -
//read-only filesystem or something evil happened*
return new AbstractNode(Children.LEAF);
}
}
*/** This is the node you actually see in the project tab for the project */*
private static final class TextNode extends link:http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/FilterNode.html[FilterNode] {
final DemoProject project;
public TextNode(Node node, DemoProject project) throws DataObjectNotFoundException {
super(node, new FilterNode.Children(node),
*//The projects system wants the project in the Node's lookup.
//NewAction and friends want the original Node's lookup.
//Make a merge of both*
new ProxyLookup(new Lookup[]{Lookups.singleton(project),
node.getLookup()
}));
this.project = project;
}
@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("org/netbeans/demo/project/icon1.png");
}
@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;
}
}
----
== Registering the Project Type as Project Sample
In this section, we create some project samples that make use of our project type. We also register these project samples in the New Project window of our application.
[start=1]
1. Create some folders and files on disk. The folders and files should meet the requirements for projects of the type that you have defined in your module. For example, in the case of this tutorial, make sure that you have a folder that has a subfolder named "texts".
[start=2]
1. Run the module that you created in this tutorial. A new instance of your NetBeans IDE starts up. Now your new project type is installed in NetBeans IDE.
[start=3]
1. Open the sample projects you created in the previous step, which you're now able to do since you have installed a module providing your module type.
[start=4]
1. Also open the module itself into the new instance of NetBeans IDE. Create a new subpackage, named "sample", as shown below, then right-click it and choose New | Project Template:
image::images/projecttypes_70pics_nb-sample-proj1.png[]
[start=5]
1. Use the New Project Template wizard to register your first sample project:
image::images/projecttypes_70pics_nb-sample-proj7.png[]
Click Next. Specify the name of the template, the display text, and the package where the template should be registered.
[start=6]
1. Once you have completed the wizard, use it again to register your second sample project.
[start=7]
1. Check that the new instance of NetBeans IDE shows you the following in the Projects window:
image::images/projecttypes_71pics_result.png[]
You have now used the New Project Template wizard to register some project samples into the central registry of your application.
Also notice that you have some ZIP files containing your sample projects, created by the Project Template wizard, together with several classes from the NetBeans Wizard API. For further information, refer to the link:https://netbeans.apache.org/tutorials/nbm-projectsamples.html[NetBeans Project Sample Module Tutorial].
== Installing the Module
Finally, we install the module and make use of the result.
[start=1]
1. Right-click the module project and choose "Run". The application for which the module is being created starts up and the module installs into it.
[start=2]
1. Choose File | Open Project and browse to a folder that has a subfolder named "texts".
[start=3]
1. Open the project and you should see the Projects window displaying your project. The content of the "texts" folder should be shown in the Projects window:
image::images/projecttypes_70pics_nb-sample-proj6.png[]
[start=4]
1. Alternatively, you can use the New Project wizard (Ctrl-Shift-N) to create your projects using the templates you created:
image::images/projecttypes_70pics_nb-sample-proj5.png[]
link:http://netbeans.apache.org/community/mailing-lists.html[Send Us Your Feedback]
== Next Steps
For more information about creating and developing NetBeans modules, see the following resources:
* link:https://netbeans.apache.org/kb/docs/platform.html[Other Related Tutorials]
* link:http://bits.netbeans.org/dev/javadoc/index.html[NetBeans API Javadoc]