blob: 0f628d8d104e4d6a320eb4f3ec3eefdf2b7e2da4 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- -*- xhtml -*- -->
<title>NetBeans System Properties Module Tutorial for NetBeans Platform 7.1</title>
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/>
<meta name="AUDIENCE" content="NBUSER"/>
<meta name="TYPE" content="ARTICLE"/>
<meta name="EXPIRES" content="N"/>
<meta name="developer" content="gwielenga@netbeans.org"/>
<meta name="indexed" content="y"/>
<meta name="description"
content="A short guide to using the Nodes API."/>
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans System Properties Module Tutorial</h1>
<p>This tutorial is intended to demonstrate aspects of the <a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/package-summary.html">NetBeans Nodes API</a>.</p>
<p><strong class="notes">Note: </strong>This document uses NetBeans Platform 7.1 and
NetBeans IDE 7.1. If you
are using an earlier version, see <a href="71/nbm-nodesapi.html">the previous version
of this document</a>.</p>
<p><b>Contents</b></p>
<p><img src="../../images/articles/71/netbeans-stamp.png" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.1" title="Content on this page applies to NetBeans IDE 7.1"/></p>
<ul class="toc">
<!--<li><a href="#gettingtoknowthesample">Getting to Know the Sample</a>
<ul>
<li><a href="#introducing-sample">Introducing the Sample</a></li>
<li><a href="#introducing-sources">Introducing the Sources</a></li>
</ul></li>-->
<li><a href="#settingupthemoduleproject">Setting Up the Module Project</a>
<ul>
<li><a href="#creatingthemoduleproject">Creating the Module Project</a></li>
<li><a href="#specifying">Specifying the Module's Dependencies</a></li>
</ul></li>
<li><a href="#creatingandgettingtoknowthemainfiles">Creating the Main Files</a>
<ul>
<li><a href="#AllPropsNode"><tt>AllPropsNode.java</tt></a></li>
<li><a href="#PropertiesNotifier"><tt>PropertiesNotifier.java</tt></a></li>
<li><a href="#AllPropsChildFactory"><tt>AllPropsChildFactory.java</tt></a></li>
<li><a href="#OnePropNode"><tt>OnePropNode.java</tt></a></li>
<li><a href="#RefreshPropsAction"><tt>RefreshPropsAction.java</tt></a></li>
</ul></li>
<li><a href="#finetuning">Setting Up the Supporting Files</a></li>
<li><a href="#building">Building and Installing the Module</a></li>
</ul>
<p><b>To follow this tutorial, you need the software and resources listed in the following
table.</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">Software or Resource</th>
<th class="tblheader" scope="col">Version Required</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">version 7.1 or above</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td>
<td class="tbltd1">version 6 or above</td>
</tr>
</tbody>
</table>
<!-- ===================================================================================== -->
<!--<h2><a name="gettingtoknowthesample"></a>Getting to Know the Sample</h2>
<p>This tutorial will help you understand the following:</p>
<ul>
<li>how to add a hierarchy of nodes to the Services window</li>
<li>how to rename and delete nodes</li>
<li>how to to display subnodes</li>
<li>how to change the list of nodes</li>
<li>how to create a property sheet</li>
<li>how to assign properties</li>
<li>how to perform other common tasks</li></ul>
<p>Working through this sample is
important because the <a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/package-summary.html">NetBeans Nodes API</a> is one of the more ubiquitous APIs and reflects
information that exists independently of the nodes. You interact
through an interface to this live information.</p>
<p>Before you start writing the module, you have to make sure you have all of the necessary software.
In addition, you might want to play with the sample before building it yourself. Getting to know the
sample lets you know what you are in for during the rest of this tutorial!</p>
<div class="indent">
<h3 class="tutorial"><a name="installing-sample"></a>Installing the Sample</h3>
<p>In this section, we download and install the sample, so that we can become
acquainted with it before recreating it ourselves.</p>
<ol>
<li>Unzip the <a href="../samples/zips/SystemProperties.zip">attached file</a>.</li>
<li><p>In the IDE, choose File > Open Project and browse to the folder that contains the unzipped file.
Open the module project. It should look as follows:</p>
<p><img style="border:1px solid black" src="../../images/tutorials/sysprops/nbm-sysprops-70.png" alt="All source files."/></p></li>
<li>Right-click the project node and choose Install/Reload in Target Platform. The target
platform opens and the module is installed.</li>
</ol>
<h3 class="tutorial"><a name="introducing-sample"></a>Introducing the Sample</h3>
<p>In this section, we look at the functionality provided by the module, from
the perspective of an end user. Once we know in detail what the module will provide,
we will be in a better position to build it from scratch.</p>
<ol>
<li><p>Open the Services window (Ctrl-5) and notice that you have a new node with a large number (~50) of subnodes,
each labeled according to a key for specific Java properties:</p>
<p><img style="border:1px solid black" src="../../images/tutorials/sysprops/nbm-sysprops-70-2.png" alt="System Properties module."/></p>
<p> Contextual menu items appear when you
right-click the main node and subnodes. For the subnodes, the Java
runtime's generalized properties are used to configure basic mechanisms such as
search paths for fonts, location of the Java Virtual Machine (JVM), default locale,
and so on. These are typically string keys, similar to <tt>name.name.name</tt>, and an
associated string value. Values can be queried or set from within the Java language.
In addition, they can be used to set default values at JVM start up. When the System Properties node is
extended, it will display one node for each of the keys, each labeled according to the
key. For example:</p>
<ul>
<li>The value <tt>java.vm.vendor</tt> may display the value Sun Microsystems, Inc.,
for example.</li>
<li>The value <tt>path.separator</tt> may display a semicolon, depending on your
configuration.</li>
<li>The value <tt>user.language</tt> may display the value English, depending on your
configuration.</li></ul>
</li>
<li>Play with the sample:
<ul>
<li><b>Show the property sheet.</b> Right-click a subnode and choose Properties.
The property sheet is displayed with a single Properties tab,
containing two properties. The first property is labeled Name and
it gives the same name as the node (key), while the other is labeled Value and
displays the string value of the property.</li>
<li><b>Rename a property.</b> Click on a subnode and then click it again. This lets you rename a key in
place, while keeping the same value. Alternatively, click
on the Name property in the property sheet, or right-click a subnode and choose Rename.</li>
<li><b>Change a value.</b> Click on the Value tab in the property sheet and type in a new value. This
resets the system property to the entered value.</li>
<li><b>Add a property.</b> Right click the main System Properties node and choose Add System Property. This lets you add a new system property
to the list.</li>
<li><b>Delete a property.</b> Select a property and press Delete. This removes the property.</li>
<li><b>Refresh the list.</b> Right click the main System Properties node and choose Refresh. This prompts the module
to recheck the values, refreshing the list as needed.</li>
</ul></li>
</ol>
<h3 class="tutorial"><a name="introducing-sources"></a>Introducing the Sources</h3>
<p>The System Properties sample consists of main files and supporting files.</p>
<ul>
<li><p><b>Main Files.</b> The module's main
files are its Java classes.</p>
<p>The Java classes are introduced in alphabetical order below:</p>
<table width="76%" border="1">
<tbody><tr>
<td>
<div><b>File</b></div>
</td>
<td>
<div><b>Description</b></div>
</td>
</tr>
<tr>
<td valign="top"><tt>AllPropsChildFactory.java</tt></td>
<td>
This children object is responsible for keeping track of the list of nodes underneath
the System Properties node. When first asked for the list, it retrieves all system
properties and asks the node implementation to keep track of all the system
property names. The interface for doing this is a class called Children.
</td>
</tr>
<tr>
<td valign="top"><tt>AllPropsNode.java</tt></td>
<td>
This node specifies what children will be under it, and asks to use
AllPropsChildFactory to keep track of the list of child nodes. The module takes care
of things such as its context menu.
</td>
</tr>
<tr>
<td valign="top"><tt>OnePropNode.java</tt></td>
<td>
This is the node representing a single property, and is an AbstractNode. Its
constructor requires that the user supply the key, in the form of a string. For every
system property name, OnePropNode is used to display it. When the user expands
the system properties node, it builds a list of keys, then creates a corresponding
number of OnePropNodes. Each OnePropNode displays a single key, and does not
directly interact with its parent node -- its knowledge is limited to a single system
property and how to deal with it, as well as notifying the PropertiesNotifier
if there are any changes.This design makes it easier to reuse such nodes, including
placing them in other contexts.
</td>
</tr>
<tr>
<td valign="top"><tt>PropertiesNotifier.java</tt></td>
<td>
Manages routing events whenever there are changes, including adding, deleting,
or renaming a property, or when a property value has changed.
</td>
</tr>
<tr>
<td valign="top"><tt>RefreshPropsAction.java</tt></td>
<td>
This action appears in the pop-up menu under System Properties with the label Refresh.
It forces a refresh to occur, updating the display of information based
on the current state of system properties.
</td>
</tr>
</tbody>
</table>
</li>
<li><p><b>Supporting Files.</b> The module's supporting files are
in the <tt>org.myorg.systemproperties</tt> package and
in the Important Files node.</p>
<p>The supporting files in the <tt>org.myorg.systemproperties</tt> package are introduced in alphabetical order below:</p>
<table width="76%" border="1">
<tbody><tr>
<td>
<div><b>File</b></div>
</td>
<td>
<div><b>Description</b></div>
</td>
</tr>
<tr>
<td valign="top"><tt>allPropsIcon.gif</tt></td>
<td>Icon for the System Properties node.</td>
</tr>
<tr>
<td valign="top"><tt>Bundle.properties</tt></td>
<td>
This is a standard Java properties file, which uses the syntax <tt>Key=Value</tt>. Keys are
code names for things that appear in the source code, with values designating those
things which will be displayed to the user.
This file is useful for localization. For example, by creating a properties file such
as <tt>Bundle_ja.properties</tt>, and filling all the values with Japanese, this module will automatically display
everything in Japanese, if the
user is running the IDE in Japanese mode.
</td>
</tr>
<tr>
<td valign="top"><tt>onePropIcon.gif</tt></td>
<td>
Icon for subnodes.
</td>
</tr>
</tbody>
</table>
<p>The files in the Important Files node are
introduced in the order in which they appear in the Projects window:</p>
<table width="76%" border="1">
<tbody><tr>
<td>
<div><b>File</b></div>
</td>
<td>
<div><b>Description</b></div>
</td>
</tr>
<tr>
<td valign="top">Module Manifest</td>
<td>Declares project as module.</td>
</tr>
<tr>
<td valign="top">Build Script</td>
<td>
Contains Ant targets for building the project.
</td>
</tr>
<tr>
<td valign="top">Project Metadata</td>
<td>
Contains project metadata, such as dependencies, for project.
</td>
</tr>
<tr>
<td valign="top">Project Properties</td>
<td>Contains project properties.</td>
</tr>
<tr>
<td valign="top">NetBeans Platform Config</td>
<td>Contains platform properties.</td>
</tr>
<tr>
<td valign="top">Per-user NetBeans Platform Config</td>
<td>Contains user-specific properties.</td>
</tr>
</tbody>
</table>
</li>
</ul>
</div>
-->
<!-- ===================================================================================== -->
<h2><a name="settingupthemoduleproject"></a>Setting Up the Module Project</h2>
<p>Before you start writing the module, set up its structure
and dependencies correctly. NetBeans IDE provides a wizard and dialogs
that set up all the basic files
and configurations needed for a module.</p>
<div class="indent">
<h3 class="tutorial"><a name="creatingthemoduleproject"></a>Creating the Module Project</h3>
<p>In this section, we use the New Module wizard to create the source structure needed by all
module projects.</p>
<ol>
<li>Choose File &gt; New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules.
Under Projects, select Module. Click Next.</li>
<li>In the Name and Location panel, type <tt>SystemProperties</tt>
in the Project Name field.
Change the Project Location to any directory on your computer. Click Next.</li>
<li>In the Basic Module Configuration panel, type <tt>org.myorg.systemproperties</tt>
in Code Name Base. Click Finish.</li>
</ol>
<p> The IDE creates the <tt>System Properties</tt>
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).</p>
<h3 class="tutorial"><a name="specifying"></a>Specifying the Module's Dependencies</h3>
<p>Later, you will need to subclass several classes that belong to NetBeans APIs.
Each NetBeans API, provided by a module, has to be declared as a module dependency.
Use the Project Properties dialog box for this purpose, as explained below.</p>
<ol>
<li><p>In the Projects window, right-click the <tt>System Properties</tt> project and choose Properties.
In the Project Properties dialog box, click Libraries and then click Add... Start typing 'CallableSystemAction',
which is one of the NetBeans API classes you will need later. As you type, notice that the filter narrows,
displaying only those modules that can provide the class that you are typing.</p></li>
<li>For each of the following APIs, click "Add..." in the Libraries panel,
select the name from the Module list, and then click OK to confirm it:
<ul>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-actions/overview-summary.html">Actions API</a></tt></li>
<li><tt><a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-core-ide/overview-summary.html">Core IDE</a></tt></li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-dialogs/overview-summary.html">Dialogs API</a></tt></li>
<li><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/overview-summary.html">Lookup API</a></tt></li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/overview-summary.html">Nodes API</a></tt></li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-util/overview-summary.html">Utilities API</a></tt></li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-windows/overview-summary.html">Window System API</a></tt></li>
</ul>
<p>Click OK to exit the Project Properties dialog box.</p></li>
<li>In the Projects window, expand the Important Files node
and double-click "Project Metadata". The <tt>project.xml</tt>
file opens. Note that the modules you selected have been
declared as dependencies.</li>
</ol>
</div>
<!-- ===================================================================================== -->
<h2><a name="creatingandgettingtoknowthemainfiles"></a>Creating the Main Files</h2>
<p>The functionality of a NetBeans module is provided by its Java classes. In this section,
you will create and examine each of them:</p>
<ul>
<li><tt>AllPropsNode.java</tt></li>
<li><tt>PropertiesNotifier.java</tt></li>
<li><tt>AllPropsChildFactory.java</tt></li>
<li><tt>OnePropNode.java</tt></li>
<li><tt>RefreshPropsAction.java</tt></li>
</ul>
<div class="indent">
<h3 class="tutorial"><a name="AllPropsNode"></a>AllPropsNode.java</h3>
<p>This Java class specifies the definition of the main node. The definition includes
a display name, as well as a definition of the children that will be under the main node. The definition
of the children is provided by the
<tt>AllPropsChildFactory</tt> class, which keeps track of the list of child nodes. The class also takes care
of things such as the main node's context menu.</p>
<p>Via the <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-core-ide/org/netbeans/api/core/ide/ServicesTabNodeRegistration.html">@ServicesTabNodeRegistration</a> annotation, the node
is registered as a new node within the Services window.</p>
<p>Do the following:</p>
<ol>
<li><b>Create the file.</b> Right-click the <tt>org.myorg.systemproperties</tt> node and choose New &gt; Other. Under
Categories, choose Java Classes. Under File Types, choose Java Class.
Click Next and type <tt>AllPropsNode</tt> in Class Name. Click Finish. The new Java class opens in the Source Editor.
Replace the default code with the following:
<pre class="examplecode">import java.io.IOException;
import javax.swing.Action;
import org.netbeans.api.core.ide.ServicesTabNodeRegistration;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.actions.NewAction;
import org.openide.actions.OpenLocalExplorerAction;
import org.openide.actions.PropertiesAction;
import org.openide.actions.ToolsAction;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.SystemAction;
import org.openide.util.datatransfer.NewType;
@ServicesTabNodeRegistration(name = "AllPropsNode",
displayName = "#LBL_AllPropsNode",
shortDescription = "#HINT_AllPropsNode",
iconResource = "org/myorg/systemproperties/allPropsIcon.gif",
position = 2021)
@Messages({
"LBL_AllPropsNode=System Properties",
"HINT_AllPropsNode=Shows all currently set system properties."
})
public class AllPropsNode extends AbstractNode {
public AllPropsNode() {
super(Children.create(new AllPropsChildFactory(), false));
setDisplayName(Bundle.LBL_AllPropsNode());
setShortDescription(Bundle.HINT_AllPropsNode());
setIconBaseWithExtension("org/myorg/systemproperties/allPropsIcon.gif");
}
@Override
public Action[] getActions(boolean context) {
Action[] result = new Action[]{
new RefreshPropsAction(),
null,
SystemAction.get(OpenLocalExplorerAction.class),
null,
SystemAction.get(NewAction.class),
null,
SystemAction.get(ToolsAction.class),
SystemAction.get(PropertiesAction.class),};
return result;
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
public Node cloneNode() {
return new AllPropsNode();
}
@Messages({
"LBL_NewProp=System Property",
"LBL_NewProp_dialog=Create New Property",
"MSG_NewProp_dialog_key=New property name:",
"MSG_NewProp_dialog_value=New property value:"})
@Override
public NewType[] getNewTypes() {
return new NewType[]{
new NewType() {
@Override
public String getName() {
return Bundle.LBL_NewProp();
}
@Override
public void create() throws IOException {
NotifyDescriptor.InputLine msg = new NotifyDescriptor.InputLine(Bundle.LBL_NewProp_dialog(), Bundle.MSG_NewProp_dialog_key());
DialogDisplayer.getDefault().notify(msg);
String key = msg.getInputText();
if ("".equals(key)) {
return;
}
msg = new NotifyDescriptor.InputLine(Bundle.MSG_NewProp_dialog_value(), Bundle.MSG_NewProp_dialog_key());
DialogDisplayer.getDefault().notify(msg);
String value = msg.getInputText();
System.setProperty(key, value);
PropertiesNotifier.changed();
}
}
};
}
}</pre>
</li>
<li><b>Understand the file.</b> Here is an explanation of the class:
<ul>
<li><b><tt>public class AllPropsNode extends <a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html">AbstractNode</a></tt>.</b> <tt>AbstractNode</tt> is a generic Node subclass. <tt><a href="http://www.netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html">Node</a></tt> is the abstract
class, <tt>AbstractNode</tt> is the common implementation that can be customized.</li>
<li><b>Constructor:</b>
<ul>
<li><b><tt>public AllPropsNode</tt>.</b> In creating this node, it first calls super -- the <a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#AbstractNode(org.openide.nodes.Children)">constructor
for the super class (AbstractNode)</a>. This creates the infrastructure for
AbstractNode, and shows that it is mandatory to supply a child object for its
use. This object represents the list of children of the node, creating a separate class
for clarity: AllPropsChildFactory.
<ul>
<li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#setIconBaseWithExtension%28java.lang.String%29">setIconBaseWithExtension</a></tt>.</b> Designates the location for the associated icon.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#setDisplayName(java.lang.String)">setDisplayName</a></tt>.</b> Sets the name the user sees. This defaults to the internal name,
but it is better to set it to something localized.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#setShortDescription(java.lang.String)">setShortDescription</a></tt>.</b> Sets the associated tool tip. This is the override to specify
what goes into the node context menu. </li>
</ul></li></ul></li>
<li><b>Methods:</b>
<ul>
<!-- 1 -->
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#getActions(boolean)">getActions</a></tt>.</b> The following is a list of actions to be
displayed in the menu, with separators between the menu items. The following
methods are used:
<ul>
<li><tt>RefreshPropsAction</tt> is an action defined in another source file</li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-actions/org/openide/actions/NewAction.html">NewAction</a></tt> enables the creation of a new subnode or key-value pair</li>
<li><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-actions/org/openide/actions/OpenLocalExplorerAction.html">OpenLocalExplorerAction</a></tt> permits the user to make a new Explorer window
showing only system properties</li>
</ul>
<p>Both <tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-actions/org/openide/actions/ToolsAction.html">ToolsAction</a></tt> and <tt><a href="http://www.netbeans.org/download/dev/javadoc/org-openide-actions/org/openide/actions/PropertiesAction.html">PropertiesAction</a></tt> are standard actions that most nodes
should have.</p></li>
<!-- 2 -->
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#getHelpCtx()">getHelpCtx</a></tt>.</b> Supplies an IDE key for the context help. When building context help
for this Module, this is how you would associate a specific node with a
specific help string.</li>
<!-- 3 -->
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#cloneNode()">cloneNode</a></tt>.</b> Creates a new copy of the node that enables other parts of the IDE to
display a separate copy of the System Properties list, other than the Services window.
This is more efficient than the fallback implementation, which is to delegate to the
original.</li>
<!-- 3 -->
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#getNewTypes()">getNewTypes</a></tt>.</b> Returns a list of <tt><a href="http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/datatransfer/NewType.html">NewType</a></tt> objects. When
there is a <tt>NewAction</tt> in the context menu, the action displays menu items
corresponding to each of the <tt>NewTypes</tt> in the node. The action provides the actual
GUI, such as showing a submenu. You specify abstract definitions
and make the new objects. In this example, only one <tt>NewType</tt> is returned,
since there is only one type of thing that can reasonably be created (a new system
property); however, more than one <tt>NewType</tt> could be returned, and they would be
displayed in a submenu.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/datatransfer/NewType.html#create()">create</a></tt>.</b> Creates the new object. In this example, there
will be dialog boxes for the key-in values.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-dialogs/org/openide/NotifyDescriptor.InputLine.html">NotifyDescriptor.InputLine</a></tt>.</b> The description of a small dialog with a single
text entry field pop up, a title for the dialog, and a message.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-dialogs/org/openide/DialogDisplayer.html#notify(org.openide.NotifyDescriptor)">DialogDisplayer.getDefault().notify(desc)</a></tt>.</b> Displays all this in a pop-up dialog.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-dialogs/org/openide/NotifyDescriptor.InputLine.html#getInputText()">getInputText</a></tt>.</b> Retrieves the user input for the key.
<p>The same is done for the value, again using <tt>DialogDisplayer.getDefault</tt> and
<tt>getInputText</tt>.</p>
<p>Next, <tt>System.setProperty</tt>, from the Java API, is called to set the system
property.</p>
<p>Finally, another class, <tt>PropertiesNotifier.changed</tt> (created next), is called to indicate
to other classes and Module components that something about the current set of
system properties has changed and updates are required. For example, there may
be a new property, or an existing value may have changed.</p></li>
</ul></li>
</ul></li>
</ol>
<h3 class="tutorial"><a name="PropertiesNotifier"></a>PropertiesNotifier.java</h3>
<p>This Java class manages routing events whenever there are changes, including adding, deleting,
or renaming a property, or when a property value has changed. You could also see it as a helper routine, very similar
to a JavaBeans component
that has an event set attached to it. However, it is not strictly a JavaBeans component -- there
are no instances of this class -- but its static methods are used like JavaBeans
instance methods. Use is made of the <a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/ChangeSupport.html">ChangeSupport</a>
class from the NetBeans APIs, which is an equivalent of <tt>PropertyChangeSupport</tt> for <tt>ChangeListeners</tt>.</p>
<ol>
<li><b>Create the file.</b> Right-click the <tt>org.myorg.systemproperties</tt> node, choose New &gt; Java Class,
and type <tt>PropertiesNotifier</tt> in Class Name. Click Finish. The new Java class opens in the Source Editor.
Replace the default code with the following:
<pre class="examplecode">import javax.swing.event.ChangeListener;
import org.openide.util.ChangeSupport;
public class PropertiesNotifier {
private static final ChangeSupport cs = new ChangeSupport(PropertiesNotifier.class);
public static void addChangeListener(ChangeListener listener) {
cs.addChangeListener(listener);
}
public static void removeChangeListener(ChangeListener listener) {
cs.removeChangeListener(listener);
}
public static void changed() {
cs.fireChange();
}
}</pre>
</li>
<li><b>Understand the file.</b> The methods defined for this class are as follows:
<ul>
<li><b><tt><a name="PropertiesNotifierchanged" id="PropertiesNotifierchanged"></a>changed</tt>.</b> Fires an event to those processes that are listening. Every component that
displays information based on a system property must listen for these events and
update their displays as needed.</li>
<li><b><tt>addChangeListener</tt></b> and <b><tt>removeChangeListener</tt>.</b> Lets components register
themselves as listeners for these events. Processes which have displayed state can
add a <tt>ChangeListener</tt> to this class. To ensure proper updates, processes that
affect the state call <tt>changed</tt>.</li>
</ul></li>
</ol>
<h3 class="tutorial"><a name="AllPropsChildFactory"></a>AllPropsChildFactory.java</h3>
<p>This Java class is responsible for keeping track of the list of nodes underneath
the main node. When first asked for the list, the class retrieves all system
properties and asks the node implementation to keep track of all the system
property names. The abstract class doing this is
called <tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Children.html">Children</a></tt>.</p>
<p>In this example, a popular children implementation called <tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/ChildFactory.Detachable.html">ChildFactory</a></tt> is used.
By subclassing <tt>ChildFactory</tt>, you need not explicitly keep track of
the nodes -- this implementation does that. Instead, you keep track
of a set of keys, which are lighter weight objects. Each key typically represents one
node. You must tell the implementation how to create a
node for each key. You can decide for yourself what type of keys to
use.</p>
<p>In this example, the keys are names of system properties.</p>
<ol>
<li><b>Create the file.</b> Right-click the <tt>org.myorg.systemproperties</tt> node, choose New &gt; Java Class,
and type <tt>AllPropsChildFactory</tt> in Class Name. Click Finish. The new Java class opens in the Source Editor.
Replace the default code with the following code:
<pre class="examplecode">import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
public class AllPropsChildFactory extends ChildFactory.Detachable&lt;String&gt; {
private ChangeListener listener;
@Override
protected void addNotify() {
PropertiesNotifier.addChangeListener(listener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent ev) {
refresh(true);
}
});
}
@Override
protected void removeNotify() {
if (listener != null) {
PropertiesNotifier.removeChangeListener(listener);
listener = null;
}
}
@Override
protected Node createNodeForKey(String key) {
return new OnePropNode(key);
}
@Override
protected boolean createKeys(List&lt;String&gt; toPopulate) {
List&lt;String&gt; keys = new ArrayList&lt;String&gt;();
for (Object prop : System.getProperties().keySet()) {
keys.add((String) prop);
}
Collections.sort(keys);
toPopulate.addAll(keys);
return true;
}
}</pre>
</li>
<li><b>Understand the file.</b> The important methods that should be defined when implementing
<tt>ChildFactory</tt> include:
<ul><li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/ChildFactory.Detachable.html#addNotify%28%29">addNotify</a></tt>.</b> Called the first time that a list of
nodes is needed by the platform. An example of this is when the System Properties node
is expanded. When <tt>addNotify</tt> is called, it calls the helper method <tt>refreshList</tt>
to determine the keys, then it registers itself with the <tt>PropertiesNotifier</tt>,
requesting notification of any system property changes. If there is such a change,
the list will be refreshed.</li>
<li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/ChildFactory.Detachable.html#removeNotify%28%29">removeNotify</a></tt>.</b> Called when the user collapses a System Properties node and
starts working on something else. The platform will notice that the list of nodes is
no longer needed, and it will free up the memory that is no longer being used.
Note that momentarily collapsing the node will not trigger this call. When
<tt>removeNotify</tt> is called, it removes the listener, as it is no longer interested in
receiving notifications. In addition, <tt>refresh</tt> is called. This method
is defined by <tt>ChildFactory</tt> for use by the subclasses.</li>
<li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/ChildFactory.html#createKeys%28java.util.List%29">createKeys</a></tt>.</b> The <tt>System.getProperties</tt> call retrieves all of the properties
currently defined in the system. This call goes through all of the property names,
keeping and sorting this list. When <tt>true</tt> is returned,
every item found in the <tt>toPopulate</tt> list is
automatically passed to <tt>createNodeForKey</tt>, where the
subnodes are created, one per system property, sorted by property name.</li>
<li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/ChildFactory.html#createNodeForKey%28T%29">createNodeForKey</a></tt>.</b> Called by the implementation whenever it needs to construct a
child node. It is passed the key for which it is making a node. It returns either none,
one, or more nodes corresponding to what should be displayed for the key. In this
example, a new instance of one property node is being created, and the system
property name is passed into its constructor.</li></ul></li>
</ol>
<h3 class="tutorial"><a name="OnePropNode"></a>OnePropNode.java</h3>
<p>This Java class provides the <tt>AbstractNode</tt> implementation for a single property.
Its constructor requires a string key. This class displays a single system property name. When the user expands
the system properties node, it builds a list of keys, then creates a corresponding
number of <tt>OnePropNodes</tt>. Each <tt>OnePropNode</tt> displays a single key, and does not
directly interact with its parent node -- its knowledge is limited to a single system
property and how to deal with it, as well as notifying the <tt>PropertiesNotifier</tt>
if there are any changes. This design makes it easier to reuse such nodes, including
placing them in other contexts.</p>
<ol>
<li><b>Create the file.</b> Right-click the <tt>org.myorg.systemproperties</tt> node, choose New &gt; Java Class,
and type <tt>OnePropNode</tt> in Class Name. Click Finish. The new Java class opens in the Source Editor.
Replace the default code with the following:
<pre class="examplecode">import java.io.IOException;
import java.util.Properties;
import javax.swing.Action;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.actions.DeleteAction;
import org.openide.actions.PropertiesAction;
import org.openide.actions.RenameAction;
import org.openide.actions.ToolsAction;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.SystemAction;
public class OnePropNode extends AbstractNode {
private String key;
private ChangeListener listener;
@Messages("HINT_OnePropNode=Represents one system property.")
public OnePropNode(String key) {
super(Children.LEAF);
this.key = key;
setIconBaseWithExtension("org/myorg/systemproperties/onePropIcon.gif");
super.setName(key);
setShortDescription(Bundle.HINT_OnePropNode());
}
@Override
public Action[] getActions(boolean context) {
Action[] result = new Action[]{
SystemAction.get(DeleteAction.class),
SystemAction.get(RenameAction.class),
null,
SystemAction.get(ToolsAction.class),
SystemAction.get(PropertiesAction.class),};
return result;
}
@Override
public Action getPreferredAction() {
return SystemAction.get(PropertiesAction.class);
}
@Override
public Node cloneNode() {
return new OnePropNode(key);
}
@Messages({"PROP_value=Value","HINT_value=Value of this system property."})
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set props = sheet.get(Sheet.PROPERTIES);
if (props == null) {
props = Sheet.createPropertiesSet();
sheet.put(props);
}
props.put(new PropertySupport.Name(this));
class ValueProp extends PropertySupport.ReadWrite {
public ValueProp() {
super("value", String.class, Bundle.PROP_value(), Bundle.HINT_value());
}
@Override
public Object getValue() {
return System.getProperty(key);
}
@Override
public void setValue(Object nue) {
System.setProperty(key, (String) nue);
PropertiesNotifier.changed();
}
}
props.put(new ValueProp());
PropertiesNotifier.addChangeListener(listener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent ev) {
firePropertyChange("value", null, null);
}
});
return sheet;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (listener != null) {
PropertiesNotifier.removeChangeListener(listener);
}
}
@Override
public boolean canRename() {
return true;
}
@Override
public void setName(String nue) {
Properties p = System.getProperties();
String value = p.getProperty(key);
p.remove(key);
if (value != null) {
p.setProperty(nue, value);
}
System.setProperties(p);
PropertiesNotifier.changed();
}
@Override
public boolean canDestroy() {
return true;
}
@Override
public void destroy() throws IOException {
Properties p = System.getProperties();
p.remove(key);
System.setProperties(p);
PropertiesNotifier.changed();
}
}</pre>
</li>
<li><b>Understand the file.</b> Here is an explanation of the class:
<ol>
<li><b><tt>public class OnePropNode extends AbstractNode</tt>.</b> <tt>AbstractNode</tt> is a generic Node subclass. <tt>Node</tt> is the abstract
class, <tt>AbstractNode</tt> is the common implementation that can be customized.</li>
<li><b>Constructor:</b>
<ul>
<li><b><tt>super(<a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Children.html#LEAF">Children.LEAF</a>)</tt>.</b> Tells the node�s hierarchy that this
is a leaf node that will not need to be expanded and will not have any children. It
then stores the key and sets the icon.</li>
<li><b><tt>super.<a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#setName(java.lang.String)">setName(key)</a></tt>.</b> Sets the name of the key. The inherited version is used,
to set the node name (it does not attempt to rename the actual property).</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#setShortDescription(java.lang.String)">setShortDescription</a></tt>.</b> Sets the associated tool tip. This is the override to specify
what goes into the node context menu. </li>
</ul></li>
<li><b>Methods:</b>
<ul>
<li><b><tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#getPreferredAction%28%29">getPreferredAction</a></tt>.</b> Sets what is run by default if the node is double clicked or
similar user actions are performed. In this example, the default action is to pop up
the property sheet. </li>
<li><b><tt>createSheet</tt>.</b> Configures the look of
the property sheet. This creates the list of tabs in the property sheet, along with the
list of properties. <tt>createSheet</tt> is not called until there is a need to display the list
of properties.
<ul>
<li><b><tt>super.<a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#createSheet()">createSheet</a></tt>.</b> Ensures there is a sheet to start with.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Sheet.html#get(java.lang.String)">sheet.get (Sheet.PROPERTIES)</a></tt>.</b> Checks to see if there is a tab named
<tt>Properties</tt>. If not, <tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Sheet.html#createPropertiesSet()">Sheet.createPropertiesSet</a></tt> makes one. Note that <tt><a href="http://www.netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Sheet.html">Sheet</a></tt>
refers to the entire set of properties for the node, and <tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Sheet.Set.html">Sheet.Set</a></tt> is one tab in the
property sheet.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/PropertySupport.Name.html">PropertySupport.Name</a></tt>.</b> Creates a <tt>Name</tt> property that reflects
the name of the node. The code is already synchronizing the node name with the
system property name.</li>
<li><b><tt>ValueProp</tt>.</b> Is an inner class, a custom property that is created for this example.
<tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/PropertySupport.ReadWrite.html">PropertySupport.ReadWrite</a></tt> is the base class for entering and viewing values.
The super call provides a code name for the property as well as a display name and
a tool tip for the user.
<ul>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.Property.html#getValue()">getValue</a></tt>.</b> Looks up the system property.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.Property.html#setValue(java.lang.Object)">setValue</a></tt>.</b> Sets a new value for the
system property and notifies other processes that the value has changed.</li>
</ul>
<p>The property is added to the property sheet, along with a <tt>ChangeListener</tt>, which
listens for changes in system properties, which may mean that this specific property
has changed. If true, then the <tt>firePropertyChange</tt> node fires a change to say that
one of the properties in its property sheet is no longer valid, and checks and updates
should be made accordingly. Note that the name of the property is value, which
matches the internal name assigned when creating <tt>ValueProp</tt>.</p></li>
</ul></li>
<li><b><tt>finalize</tt>.</b> Called when the class is destroyed -- whenever this node
is destroyed, the <tt>ChangeListener</tt> is removed.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#canRename()">canRename</a></tt>.</b> Returns <tt>true</tt>, allowing the node to be renamed.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#setName(java.lang.String)">setName</a></tt>.</b> Called when the node is renamed, such as from the rename action, an inplace
rename from the Explorer, or from the Name property in the property sheet.
This action retrieves all system properties and associated values, removes the key,
adds a new property with a new name and value, and sets the system properties.
This action also notifies all concerned that it has changed, though it does not directly
rename itself (see <tt>AllPropsChildFactory</tt> next).</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/AbstractNode.html#canDestroy()">canDestroy</a></tt>.</b> Gives permission to delete this node.</li>
<li><b><tt><a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#destroy()">destroy</a>.</tt></b> Retrieves system properties, removes its key, sets
properties back, and notifies all concerned of changes. Note that this <tt>destroy</tt>
method does not remove the node -- it only removes the system property and
notifies interested parties that this property is gone. The node is actually removed
later, by <tt>AllPropsChildFactory</tt>. <tt>AllPropsChildFactory</tt> realizes this property no longer
exists, and creates a new set of keys that no longer includes this property. Then the
<tt>ChildFactory</tt> implementation automatically removes that node. This is done to
reflect the actual state of the system.</li>
</ul>
</li></ol></ol>
<h3 class="tutorial"><a name="RefreshPropsAction"></a>RefreshPropsAction.java</h3>
<p>This Java class provides the "Refresh" action that appears in the pop-up menu under the "System Properties" main node.
It forces a refresh to occur, updating the display of information based
on the current state of system properties. It is a plain <tt>AbstractAction</tt> and is
always enabled, yet is not sensitive to what is selected. In principle, it could also be placed
as a button in a toolbar.</p>
<div class="indent">
<ol>
<li><b>Create the file.</b> Right-click the <tt>org.myorg.systemproperties</tt> node, choose New &gt; Java Class,
and type <tt>RefreshPropsAction</tt> in Class Name. Click Finish. The new Java class opens in the Source Editor.
Replace the default code with the following:
<pre class="examplecode">import org.openide.util.NbBundle.Messages;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
public class RefreshPropsAction extends AbstractAction {
@Messages("LBL_RefreshProps=Refresh")
public RefreshPropsAction() {
super(Bundle.LBL_RefreshProps());
}
@Override
public void actionPerformed(ActionEvent e) {
PropertiesNotifier.changed();
}
}</pre>
</li>
<li><b>Understand the file.</b> When the action is invoked, the standard JDK <tt>actionPerformed</tt> method is called:
<ul><li><b><tt>actionPerformed</tt>.</b> Calls <tt><a href="#PropertiesNotifierchanged">PropertiesNotifier.changed</a></tt> to indicate to other classes
and Module components that something about the current set of system properties has changed and
updates are required. For example, a new property may have been added or an existing value may have been changed.</li></ul>
</li>
</ol>
</div>
</li>
</ol>
</div>
<!-- ===================================================================================== -->
<h2><a name="finetuning"></a>Setting Up the Supporting Files</h2>
<p>Once you have coded the main files, you must include the icons you'd like to use. For the icons used to display the nodes, you can use any 16x16 pixel icons you want, so long as they are
named <tt>allPropsIcon.gif</tt> and <tt>onePropIcon.gif</tt>, which
is what they are named in the code above.</p>
<p>Alternatively, use the icons below:</p>
<p><img style="border:1px solid black" src="../../images/tutorials/sysprops/allPropsIcon.gif" alt="System Properties module."/></p></li>
<p><img style="border:1px solid black" src="../../images/tutorials/sysprops/onePropIcon.gif" alt="System Properties module."/></p></li>
<p>Note that the <tt>setIconBaseWithExtension</tt> statements in the constructors of <tt>AllPropsNode.java</tt> and <tt>OnePropNode.java</tt>
set the location of the icons.</p>
<!-- ======================================================================================= -->
<h2><a name="building"></a>Building and Installing the Module</h2>
<p>Now that you have completed your module, it is time to try it out.
The IDE uses an Ant build script to build and install your module. The build script was created for you
when you created the module project.</p>
<div class="indent">
<ol>
<li><p>In the Projects window, right-click the <tt>System Properties</tt> project and choose Install/Reload
in Target Platform.</p>
<p>The module is built and installed in the target IDE or Platform. The target IDE or Platform opens so that you
can try out your new Module. The default target IDE or Platform is the
installation used by the current instance of the development IDE. Note that when you run your Module, you will be using
a temporary test user directory, not the development IDE's user directory. </p></li>
<li><p>In the IDE's Services window (Ctrl-5), you should see the new node, together with its many subnodes:</p>
<p><img style="border:1px solid black" src="../../images/tutorials/sysprops/nbm-sysprops-70-2.png" alt="System Properties module."/></p></li>
</ol>
</div>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback:%20System%20Properties%20Module%207.1%20Tutorial">Send Us Your Feedback</a></div>
<!-- ======================================================================================== -->
<h2><a name="nextsteps"></a>Next Steps</h2>
<p>For more information about creating and developing NetBeans Module, see the following resources: </p>
<ul>
<li><a href="https://netbeans.org/kb/trails/platform.html">Other Related Tutorials</a></li>
<li><a href="https://netbeans.org/download/dev/javadoc/">NetBeans API Javadoc</a></li>
</ul>
</body>
</html>