| <!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 Platform Porting Tutorial for NetBeans Platform 7.0</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 porting a Swing application to the NetBeans PLatform."/> |
| <!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. --> |
| <!-- Use is subject to license terms.--> |
| </head> |
| <body> |
| <h1>NetBeans Platform Porting Tutorial</h1> |
| |
| <p>This tutorial demonstrates how to port a simple Swing application to the <a href="https://platform.netbeans.org/screenshots.html">NetBeans Platform</a>. |
| Though the scenario below is simple, the basic concepts of "porting" an application to the NetBeans |
| Platform will become clear. In the end, some general principles will be identified, based on the |
| steps taken in the tutorial. Hopefully, they will be useful to you when porting your own |
| Swing applications to the NetBeans Platform.</p> |
| |
| <p><b>Contents</b></p> |
| |
| <p><img src="../../images/articles/70/netbeans-stamp.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.0" title="Content on this page applies to NetBeans IDE 7.0"/></p> |
| <ul class="toc"> |
| <li><a href="#intro">Introduction to Porting</a></li> |
| <li><a href="#getting">Getting the Anagram Game</a></li> |
| <li><a href="#compliance">Levels of Compliance</a> |
| <ul> |
| <li><a href="#creating">Creating the NetBeans Platform Application</a></li> |
| <li><a href="#porting0">Porting Level 0: Launchable</a></li> |
| <li><a href="#porting1">Porting Level 1: Integration</a></li> |
| <li><a href="#porting3">Porting Level 2: Use Case Support</a></li> |
| <li><a href="#porting4">Porting Level 3: Aligned</a></li> |
| </ul></li> |
| <li><a href="#tips">Porting Tips & Tricks</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">NetBeans IDE</td> |
| <td class="tbltd1">version 7.0 or above</td> |
| </tr> |
| <tr> |
| <td class="tbltd1">Java Developer Kit (JDK)</td> |
| <td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">version 6</a></td> |
| </tr> |
| <!-- <tr> |
| <td class="tbltd1"><a href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=2753">Download the Sample</a></td> |
| <td class="tbltd1"></td> |
| </tr>--> |
| </tbody> |
| </table> |
| |
| <h2 class="tutorial"><a name="intro"></a>Introduction to Porting</h2> |
| |
| <p>Before beginning this procedure, it makes sense to ask why one would want to do so in the |
| first place. A typical Swing application consists of a domain-specific layer on top of a |
| general framework. The general framework normally provides features dealing with an |
| application's infrastructure, such |
| as an application's menu bar, windowing system (also known as "docking framework"), and |
| lifecycle management. Typically this framework is very generic and is (or could be) |
| reused by many applications within the |
| same organization.</p> <p>The NetBeans Platform exists specifically to cater to these infrastructural |
| concerns. You do not need to create these on your own for your own Swing applications. You |
| can simply move the useful domain-specific parts of your application to the |
| NetBeans Platform and then, |
| from that point onwards, the NetBeans Platform will be the new underlying 'plumbing' layer |
| of your application. You can then focus on the more interesting parts of your application, |
| specifically, the domain-specific parts. This will speed up your development process and give you |
| a consistent basis for all your applications.</p> |
| |
| <p>In this tutorial, we will begin with the Anagram Game, which is a standard Swing application |
| sample that is distributed with NetBeans IDE. We will, step by step, move it to the NetBeans |
| Platform and gradually see the advantages of doing so.</p> |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="getting"></a>Getting the Anagram Game</h2> |
| |
| <p>We begin by getting the Anagram Game, which is one of the IDE's standard Java samples, |
| from the New Project wizard. Then we run it and analyze its parts.</p> |
| |
| <ol> |
| <li><p>Choose File > New Project (Ctrl-Shift-N). Under Categories, select Samples > Java. Under Projects, |
| select Anagram Game. Click Next and Finish.</p> |
| |
| <p>You should now see the Anagram Game application outlined in the Projects window, as shown here:</p> |
| |
| <p><img alt="" style="border: 1px solid black" src="../../images/tutorials/porting/70/ag0.png"/></p> |
| |
| <p>The application contains the following classes:</p> |
| |
| <ul> |
| <li><b><tt>WordLibrary.java</tt></b>. Provides an abstract class, with |
| abstract methods like <code>getWord(int idx)</code>, <code>getScrambledWord(int idx)</code>, |
| and <code>isCorrect(int idx, String userGuess)</code>.</li> |
| <li><b><tt>StaticWordLibrary.java</tt></b>. Extends <code>WordLibrary.java</code>, |
| providing a list of scrambled words, as well |
| as their unscrambled equivalents, together with the getters and setters for accessing them |
| and for evaluating them.</li> |
| <li><b><tt>Anagrams.java</tt></b>. Provides the main user interface of the application, |
| principally consisting of a <code>JFrame</code> with |
| a <tt>JPanel</tt> containing labels and text fields. Also included |
| is a menu bar containing a File menu, with the menu items 'About' and 'Exit'.</li> |
| <li><b><tt>About.java</tt></b>. Provides the About box, accessed from the File menu.</li> |
| </ul></li> |
| |
| <li><p>Run the application and you should see the following:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/ag1.png"/></p></li> |
| |
| <li><p>When you specify the correctly unscrambled word, you will see this:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/ag2.png"/></p></li> |
| |
| </ol> |
| |
| <p>Before porting this application to the NetBeans Platform, we need to think |
| about <i>the stages in which we want to port our application</i>. In other |
| words, you do not need to port everything at once. And there are different |
| levels to which you can integrate your application, from a mostly superfical |
| level to a level that aligns your application completely with the paradigms |
| and purposes of the NetBeans Platform. The next section will show the levels |
| of compliance your application can have with the NetBeans Platform.</p> |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="compliance"></a>Levels of Compliance</h2> |
| |
| <p>Converting an application to be fit for a framework such as the NetBeans Platform can be done |
| on various levels. The integration can be shallow and use just a few integration points or |
| it can be deeper, tightly following the paradigms of the NetBeans Platform.</p> |
| |
| <p>The stages can be described as follows:</p> |
| |
| <h4 id="section-LevelsOfCompliance-Level0Launchable">Level 0: Launchable</h4> |
| |
| <p>One or more of the following can be done to make your application launchable with as |
| few changes as possible:</p> |
| |
| <ul> |
| <li>Enhance your manifest with NetBeans key/value pairs so that your JAR is recognized as an |
| OSGi bundle or as a |
| NetBeans module.</li> |
| <li>Set dependencies between modules. In the manifest, with instances of plain Class-Path you can set dependencies |
| between modules.</li> |
| <li>Register a menu item in the declarative |
| layer file (<tt>layer.xml</tt>) of your module, to |
| invoke your original application. This file can be automatically created and populated when the module is compiled, |
| via annotations, as you will do later.</li> |
| </ul> |
| |
| <p>In this tutorial, we will do all of the above. We will enhance the manifest, which the module |
| project wizard will do for us. We will create a menu item that will |
| invoke our application. To do so, we will move our application's classes into a module source |
| structure. Then we will create a new Java <code>ActionListener</code> for opening the <code>JFrame</code> |
| of the application. We will annotate the <code>ActionListener</code> to register it in |
| the application's registry as a new menu item. From that action, we will invoke our application.</p> |
| |
| <h4 id="section-LevelsOfCompliance-Level1Integrated">Level 1: Integrated</h4> |
| |
| <p>Here are some pointers for integrating the application more |
| tightly with the NetBeans Platform:</p> |
| |
| <ul> |
| <li>Integrate visually to get the benefits of the NetBeans Window System, which is |
| its docking framework.</li> |
| <li>Use NetBeans Window System API and the Dialog APIs, primarily the <tt>TopComponent</tt> class and the |
| <tt>DialogDisplayer</tt> class.</li> |
| <li>Change initialization code of your application, use the <tt>ModuleInstall</tt> |
| class or declarative registrations, through the layer file or the META-INF/services folder.</li> |
| </ul> |
| |
| <p>In this tutorial, we will move the relevant parts of the <tt>JPanel</tt> |
| from the <tt>JFrame</tt> to a new <tt>TopComponent</tt>. The <tt>TopComponent</tt> class |
| creates a window on the NetBeans Platform, which in our case will show our <tt>JPanel</tt>.</p> |
| |
| <h4 id="section-LevelsOfCompliance-Level2UseCaseSupport">Level 2: Use Case Support</h4> |
| |
| <p>This level of compliance with the NetBeans Platform is concerned with one or more of the following activities:</p> |
| |
| <ul> |
| <li>Bind your application to other modules by inspecting existing functionality and trying to use it.</li> |
| <li>Simplify the workflow to fit into the NetBeans Platform paradigms.</li> |
| <li>Listen to the global selection to discover what other modules are doing and update your state accordingly.</li> |
| </ul> |
| |
| <p>In this tutorial, we will listen for the existence of <tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-text/org/openide/cookies/EditorCookie.html">EditorCookie</a>s</tt>. |
| A cookie is a <i>capability</i>. |
| With a Java interface, your object's capabilities are fixed at compile time, while NetBeans Platform cookies |
| allow your object to behave dynamically because your object can expose capabilities, or |
| not, based on its state. An <tt>EditorCookie</tt> |
| defines an editor, with interfaces for common activities such as opening a document, closing the editor, |
| background loading of files, document saving, and modification notifications.</p> <p>We will listen for the |
| existence of such a cookie and then we will pass the content of the editor to the <tt>TopComponent</tt>, in the |
| form of words. By doing this, we are doing what the first item above outlines, i.e., inspecting existing |
| functionality and reusing it within the context of our ported application. This is a modest level of integration. |
| However, it pays off because it shows how you can reuse functionality provided by the NetBeans Platform or |
| by any other application created on top of the NetBeans Platform, such as NetBeans IDE..</p> |
| |
| <h4 id="section-LevelsOfCompliance-Level3Aligned">Level 3: Aligned</h4> |
| |
| <p>In this final stage of your porting activity, you are concerned with the following thoughts, first and foremost:</p> |
| |
| <ul> |
| <li>Become a good citizen of the NetBeans Platform, by exposing your own state to other modules so that they know what you are doing.</li> |
| <li>Eliminate duplicated functionality, by |
| reusing the Navigator, Favorites window, Task List, Progress API, etc., instead of creating or maintaining your own.</li> |
| <li>Cooperate with other modules and adapt your application to the NetBeans Platform way of doing things.</li> |
| </ul> |
| |
| <p>Towards the end of this tutorial, we will adopt this level of compliance by letting our <tt>TopComponent</tt> |
| expose a <tt>SaveCookie</tt> when changes are made to the "Guessed Word" text field. By doing this, we will |
| enable the Save menu item under the Tools menu. This kind of integration brings the full benefits of the |
| NetBeans Platform, however it also requires some effort to attain.</p> |
| |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="creating"></a>Creating the NetBeans Platform Application</h2> |
| |
| <p>First, let's create the basis of our application. We use a wizard to do so. This |
| is the typical first practical step of creating a new application |
| on top of the NetBeans Platform application.</p> |
| |
| <ol> |
| <li><p>Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. |
| Under Projects, |
| select NetBeans Platform Application, as shown below:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp0.png"/></p> |
| |
| <p>Click Next.</p></li> |
| <li><p>Name the application <code>AnagramApplication</code>, as shown below:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp01.png"/></p> |
| <p>Click Finish</p> |
| <p>You now have a NetBeans Platform application. You can run it and |
| you will see an empty main window, with a menu bar and a tool bar:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp02.png"/></p> |
| |
| |
| <p>Look |
| under some of the menus, click a few toolbar buttons, and explore the |
| basis of your new application. For example, open the Properties window |
| and the Output window, from the Window menu, and you have the starting |
| point of a complex application: |
| </p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agp03.png"/></p> |
| |
| |
| <p>Next, we create a first custom module. We will name it |
| <code>AnagramCore</code> because, in the end, it will |
| contain the essential parts of the application. |
| Using subsequent tutorials on the <a href="https://netbeans.org/kb/trails/platform.html">NetBeans Platform Learning Trail</a>, we |
| will be able to add more features to the application, none of which |
| will be manadatory parts, since the user will be able to plug |
| them into the application. The core module, however, that is, <code>AnagramCore</code>, |
| will be a required module in every distribution of the application.</p></li> |
| |
| <li><p>Right-click the application's "Modules" node and choose "Add New...", as shown below:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp04.png"/></p> |
| |
| <p>Click Next.</p></li> |
| |
| <li><p>Type <tt>AnagramGameCore</tt> in Project Name and choose somewhere |
| to store the module, as shown below:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp2.png"/></p> |
| |
| <p>Click Next.</p></li> |
| |
| <li><p>Type a unique name in the Code Name Base field, |
| which provides the unique identifier for your module. |
| It could be anything, but here it is <tt>com.toy.anagrams.core</tt> |
| because it is convenient to reproduce the package structure |
| of the original application, which is "com.toy.anagrams.*".</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agp3.png"/></p> |
| <p><b>Note:</b> Do not click the two checkboxes you see above since |
| you will not need these.</p> |
| <p>Click Finish.</p> |
| |
| <p>Below the original Anagram Game sample, you should now see the source structure of your |
| new module, as shown here:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agp4.png"/></p></li> |
| |
| </ol> |
| |
| <p>Above, we can see that we now have the original application, |
| together with the module to which it |
| will be ported. In the next sections, we will begin porting the application to |
| the module, using the porting levels described earlier.</p> |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="porting0"></a>Porting Level 0: Launchable</h2> |
| |
| |
| <p>At this stage, we simply want to be able to launch our application. |
| To do that we will create a menu item that invokes the application. |
| We begin |
| by copying the application's sources into the module source structure.</p> |
| |
| <ol> |
| <li><p>Copy the two packages from the Anagram Game into the module. Below, |
| the new packages and classes in the module are highlighted:</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport0.png"/></p></li> |
| |
| <li><p>In the <code>com.toy.anagrams.core</code> package, create a new Java |
| class named <code>OpenAnagramGameAction</code>, implementing |
| the standard JDK <code>ActionListener</code> as follows:</p> |
| |
| <pre class="examplecode">import com.toy.anagrams.ui.Anagrams; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| |
| public class OpenAnagramGameAction implements ActionListener { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| new Anagrams().setVisible(true); |
| } |
| |
| }</pre> |
| |
| <p>When the user |
| invokes the <code>OpenAnagramGameAction</code>, the |
| <code>JFrame</code> from the Anagram Game will open.</p> |
| |
| </li> |
| |
| <li><p>Next, we need to register the new <code>OpenAnagramGameAction</code> in |
| the NetBeans central registry, which is also known as the "System FileSystem". |
| We will do this via annotations that will generate entries in the central registry. |
| To use these annotations, the AnagramGameCore module needs to have a library dependency |
| on the module that provides the annotations.</p> |
| <p>Right-click on the module's "Libraries" node and choose "Add Module Dependency", |
| as shown below:</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport0a.png"/></p> |
| <p>Start typing "ActionRegistration" and you will see that the filter narrows |
| to show the library dependency that provides the <code>ActionRegistration</code> |
| class:</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport0b.png"/></p> |
| |
| <li><p>Now you can annotate your <code>Action</code> class as follows:</p> |
| |
| <pre class="examplecode"><a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html">@ActionID</a>(id="com.toy.anagrams.core.OpenAnagramGameAction",category="Window") |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionRegistration.html">@ActionRegistration</a>(displayName = "#CTL_OpenAnagramGameAction") |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReferences.html">@ActionReferences</a>({ |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html">@ActionReference</a>(path = "Menu/Window", position = 10) |
| }) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/NbBundle.Messages.html">@Messages</a>("CTL_OpenAnagramGameAction=Open Anagram Game") |
| public class OpenAnagramGameAction implements ActionListener { |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| new Anagrams().setVisible(true); |
| } |
| |
| }</pre> |
| |
| <li><p>In the Projects window, |
| right-click the AnagramApplication project node |
| and choose Run. The application starts up, installing |
| all the modules provided by the application, which |
| includes our custom module.</p></li> |
| |
| <li><p>Under the Window menu, |
| you should find the menu item "Open Anagram Game".</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport0c.png"/></p> |
| <p>Click "Open Anagram Game" and your application appears, as before.</p></li> |
| |
| </ol> |
| |
| <p>The application is displayed, but note that it is not |
| well integrated with the NetBeans Platform. For example, |
| it is not modal and it is impossible to close the <code>JFrame</code>, |
| unless you close the application. The latter is because |
| the application now manages the lifecycle of the <code>JFrame</code>. |
| In the next section, |
| we will integrate |
| the Anagram Game more tightly with the NetBeans Platform.</p> |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="porting1"></a>Porting Level 1: Integration</h2> |
| |
| <p>In this section, we integrate the application |
| more tightly by creating a new window, so that we have a user |
| interface, that is, |
| a window, to which we can move those contents of |
| the <tt>JFrame</tt> that are useful to our new application.</p> |
| |
| <ol> |
| <li><p>Right-click the <code>com.toy.anagrams.core</code> package in the Projects window and then |
| choose New > Other. Under Categories, select Module Development. Under File Types, |
| select Window, as shown below:</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport4.png"/></p> |
| <p>Click Next.</p> |
| </li> |
| |
| <li><p>Choose the position where you would |
| like the window to appear. For purposes of this |
| tutorial choose "editor", which will place the |
| Anagram Game in the main part of the application:</p> |
| <p><img alt="" src="../../images/tutorials/porting/70/agport5.png"/></p> |
| <p>Optionally, specify whether the window should |
| open automatically when the application starts up.</p> |
| <p>Click Next.</p> |
| </li> |
| |
| |
| <li><p>Type <tt>Anagram</tt> in Class Name |
| Prefix and select <tt>com.toy.anagrams.core</tt> |
| in Package, as shown here:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agport6.png"/></p> |
| |
| <p>Above, notice that the IDE shows the files it |
| will create and modify.</p></li> |
| |
| <li><p>Click Finish. Now you have a new Java class named "AnagramGameTopComponent.java". |
| Double-click it and the Matisse GUI Builder opens. You can use the GUI Builder to |
| design your windows: |
| <p><img alt="" style="border: 1px solid" src="../../images/tutorials/porting/70/agport8.png"/></p></li> |
| |
| <li><p>Open the <tt>Anagrams</tt> class in |
| the <code>com.toy.anagrams.ui</code> package. Click within the |
| Anagrams in the GUI Builder until you see an orange line around |
| the <code>JPanel</code>, as shown below:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agport8a.png"/></p></li> |
| |
| <li><p>When you see the orange line around the <code>JPanel</code>, as shown above, |
| right-click it and choose "Copy". |
| Then paste the <code>JPanel</code> into the <code>AnagramTopComponent</code> and you should see |
| the old user interface in your new <code>AnagramTopComponent</code> class:</p> |
| |
| <p><img alt="" style="border: 1px solid" src="../../images/tutorials/porting/70/agport9.png"/></p></li> |
| |
| <li><p>You have now ported the user interface of the Anagram Game. |
| A few variables need still to be moved from the <code>Anagrams</code> |
| class to the new <code>AnagramTopComponent</code> class. Declare these |
| two, which are in the <code>Anagrams</code> class, at the top of your |
| new <code>AnagramTopComponent</code> class.</p> |
| |
| <pre class="examplecode">private int wordIdx = 0; |
| private WordLibrary wordLibrary;</pre> |
| |
| <p>Next, look in the constructor of the <code>Anagrams</code> class. The first line |
| in the constructor is as follows:</p> |
| |
| <pre class="examplecode">wordLibrary = WordLibrary.getDefault();</pre> |
| |
| <p>Copy that statement. Paste it into the <code>TopComponent</code> class, making it |
| the new first statement in the constructor of the <code>TopComponent</code> class.</p> |
| |
| <p>Check that the first part of your <code>TopComponent</code> class is now as follows:</p> |
| |
| <pre class="examplecode">... |
| ... |
| ... |
| import com.toy.anagrams.lib.WordLibrary; |
| import org.openide.util.NbBundle; |
| import org.openide.windows.TopComponent; |
| import org.netbeans.api.settings.ConvertAsProperties; |
| import org.openide.awt.ActionID; |
| import org.openide.awt.ActionReference; |
| |
| /** |
| * Top component which displays something. |
| */ |
| <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-settings/org/netbeans/api/settings/ConvertAsProperties.html">@ConvertAsProperties</a>(dtd = "-//com.toy.anagrams.core//Anagram//EN", |
| autostore = false) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Description.html">@TopComponent.Description</a>(preferredID = "AnagramTopComponent", |
| //iconBase="SET/PATH/TO/ICON/HERE", |
| persistenceType = TopComponent.PERSISTENCE_ALWAYS) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Registration.html">@TopComponent.Registration</a>(mode = "editor", openAtStartup = true) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html">@ActionID</a>(category = "Window", id = "com.toy.anagrams.core.AnagramTopComponent") |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html">@ActionReference</a>(path = "Menu/Window" /*, position = 333 */) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.OpenActionRegistration.html">@TopComponent.OpenActionRegistration</a>(displayName = "#CTL_AnagramAction", |
| preferredID = "AnagramTopComponent") |
| public final class AnagramTopComponent extends TopComponent { |
| |
| private int wordIdx = 0; |
| private WordLibrary wordLibrary; |
| |
| public AnagramTopComponent() { |
| wordLibrary = WordLibrary.getDefault(); |
| initComponents(); |
| setName(NbBundle.getMessage(AnagramTopComponent.class, "CTL_AnagramTopComponent")); |
| setToolTipText(NbBundle.getMessage(AnagramTopComponent.class, "HINT_AnagramTopComponent")); |
| } |
| ... |
| ... |
| ...</pre> |
| |
| </li> |
| |
| |
| <li><p>Run the application again. When the |
| application starts up, you should now see the Anagram Game window, which |
| you defined in this section. You will also find |
| a new menu item that opens the window, under the |
| Window menu. Also notice that the game works as before. You |
| need to click the "New Word" button once, to have the module |
| call up a new word, and then you can use it as |
| before:</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/agport10.png"/></p></li> |
| |
| </ol> |
| |
| <p>As a final step in this section, you can simply delete |
| the <code>com.toy.anagrams.ui</code> package. That package |
| contains the two UI classes from the original Anagram Game. |
| You do not need either of these two classes anymore. Simply |
| delete the package that contains them, since you have ported everything of interest |
| to the NetBeans Platform. Then also delete the <code>OpenAnagramGameAction</code> class, |
| since this class is not needed because the <code>AnagramTopComponent</code> |
| provides its own <code>Action</code> for opening the window.</p> |
| |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="porting3"></a>Porting Level 2: Use Case Support</h2> |
| |
| <p>In this section, we are concerned with listening to the global selection and making use |
| of data we find there. The global selection is the registry for |
| global singletons and instances of objects which have been registered in the system by modules. |
| Here we query the lookup for <tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-text/org/openide/cookies/EditorCookie.html">EditorCookie</a></tt>s and make use of the <tt>EditorCookie</tt>'s |
| document to fill the string array that defines the scrambled words displayed in |
| the <tt>TopComponent</tt>. </p> |
| |
| |
| <p>A cookie is a capability. With a Java interface, your object's capabilities are |
| fixed at compile time, while NetBeans Platform cookies allow your object to behave dynamically |
| because your object can expose capabilities, or not, based on its state. An <code>EditorCookie</code> |
| defines an editor, with interfaces for common activities such as opening a document, closing the |
| editor, background loading of files, document saving, and modification notifications. We will |
| listen for the existence of such a cookie and then we will pass the content of the editor |
| to the TopComponent, in the form of words. By doing this, we are inspecting existing functionality |
| and reusing it within the context of our ported application. This is a modest level of |
| integration. However, it pays off because you are reusing functionality provided by the NetBeans Platform.</p> |
| |
| <ol> |
| <li>We begin by tweaking the <tt>StaticWordLibrary</tt> class. We do this so that |
| we can set its list of words externally. The sample provides a hardcoded list, |
| but we want to be able to set that list ourselves, via an external action. Therefore, |
| add this method to <tt>StaticWordLibrary</tt>: |
| |
| <pre class="examplecode">public static void setScrambledWordList(String[] inScrambledWordList) { |
| SCRAMBLED_WORD_LIST = inScrambledWordList; |
| }</pre> |
| |
| <p>Importantly, change the class signature of <tt>StaticWordLibrary</tt> |
| to <code>public class</code> and remove the <code>final</code> |
| from the signature of <code>SCRAMBLED_WORD_LIST</code></p> |
| |
| <p>Next, we will create an action that will obtain the content of a Manifest file, |
| break the content down into words, and fill the <tt>SCRAMBLED_WORD_LIST</tt> string array |
| with these words.</p> |
| |
| </li> |
| |
| <li>As you learned to do in the previous section, set library |
| dependencies on the Text API and the Nodes API.</li> |
| |
| <li>Create a Java class |
| named <code>SetScrambledAnagramsAction</code>, in the <code>com.toy.anagrams.core</code> package, |
| and define it as follows: |
| |
| <pre class="examplecode"><a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html">@ActionID</a>(id="com.toy.anagrams.core.SetScrambledAnagramsAction",category="Window") |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionRegistration.html">@ActionRegistration</a>(displayName = "#CTL_SetScrambledAnagramsAction") |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReferences.html">@ActionReferences</a>({ |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html">@ActionReference</a>(path = "Editors/text/x-manifest/Popup", position = 10) |
| }) |
| <a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/NbBundle.Messages.html">@Messages</a>("CTL_SetScrambledAnagramsAction=Set Scrambled Words") |
| public final class SetScrambledAnagramsAction implements ActionListener { |
| |
| private final EditorCookie context; |
| |
| public SetScrambledAnagramsAction(EditorCookie context) { |
| this.context = context; |
| } |
| |
| @Override |
| public void actionPerformed(ActionEvent ev) { |
| try { |
| //Get the EditorCookie's document: |
| StyledDocument doc = context.getDocument(); |
| //Get the complete textual content: |
| String all = doc.getText(0, doc.getLength()); |
| //Make words from the content: |
| String[] tokens = all.split(" "); |
| //Pass the words to the WordLibrary class: |
| StaticWordLibrary.setScrambledWordList(tokens); |
| //Open the TopComponent: |
| TopComponent win = WindowManager.getDefault().findTopComponent("AnagramTopComponent"); |
| win.open(); |
| win.requestActive(); |
| } catch (BadLocationException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| |
| }</pre></li> |
| |
| |
| <li><p>As discussed above, when we run the application we want to be able to right-click |
| within a Manifest file, choose a menu item, and invoke our Action. Right now, |
| however, the NetBeans Platform is unable to distinguish Manifest files |
| from any other file. Therefore, we need to enable Manifest support in our |
| application.</p> |
| <p>For demonstration purposes, we will enable ALL the modules in the NetBeans Platform, |
| as well as those provided by NetBeans IDE. As a result, when we run the |
| application, a new instance of NetBeans IDE will start up, together with |
| our custom module.</p> |
| <p>To achieve the above, expand the Important Files node in the application, then open |
| the NetBeans Platform Config file, which on disk |
| is named <code>platform.properties</code>. Notice that many modules have |
| been disabled. You can enable them via the Project Properties dialog |
| of the NetBeans Platform application. Since we are simply going to enable |
| ALL of them, we need only change the content of the <code>platform.properties</code> |
| file to the following:</p> |
| |
| <pre class="examplecode">cluster.path=\ |
| ${nbplatform.active.dir}/apisupport:\ |
| ${nbplatform.active.dir}/harness:\ |
| ${nbplatform.active.dir}/ide:\ |
| ${nbplatform.active.dir}/java:\ |
| ${nbplatform.active.dir}/nb:\ |
| ${nbplatform.active.dir}/platform:\ |
| ${nbplatform.active.dir}/profiler:\ |
| ${nbplatform.active.dir}/websvccommon |
| disabled.modules= |
| nbplatform.active=default</pre> |
| |
| <p>In the next step, when we run the application, all the groups |
| of modules (called "clusters") will be enabled, nothing will |
| be excluded, and you will see NetBeans IDE started up.</p></li> |
| |
| <li><p>Build the application. Then, after you have done so, |
| run the application. Go to the Window menu and choose |
| Favorites. In the Favorites window, browse to a Manifest |
| file. Open the file. Inside the file, i.e., in the Manifest |
| Editor, right-click, and invoke the Set Scrambled Words |
| action via the menu item.</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/ageditorcookie3.png"/></p> |
| |
| <p>The <code>AnagramTopComponent</code> is displayed and, |
| when you click the Next Word button, you will see that |
| the scrambled words all come from the selected Manifest file.</p> |
| |
| <p><img alt="" src="../../images/tutorials/porting/70/ageditorcookie4.png"/></p> |
| |
| |
| </li> |
| |
| </ol> |
| |
| <p>The result of this exercise is that you now see the content of the Manifest |
| file in the Scrambled Word text field. |
| Of course, these words are not really scrambled and you cannot |
| really unscramble them. However, your module is |
| making use of the content of a file that is supported by a |
| different set of modules altogether, that is, the Manifest support |
| modules, as well as related editor modules.</p> |
| |
| |
| <p>Optionally, before continuing, you can now remove all the groups of |
| modules (known as "clusters") provided by NetBeans IDE, which |
| may not be relevant for your own application. To do so, right-click |
| the <code>AnagramApplication</code> node in the Projects window, |
| choose Properties, go to the Libraries tab, and uncheck all the |
| checkboxes, except for <code>harness</code> and <code>platform</code>. |
| Run the application again and you will see that all the project-related |
| and editor-related features of the application have now been removed.</p> |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="porting4"></a>Porting Level 3: Aligned</h2> |
| |
| |
| <p>In this section, we are concerned with becoming a "good citizen" of the |
| NetBeans Platform. We are going to expose the state of the TopComponent to |
| the other modules, so that we can cooperate with them.</p> |
| <p>As an example of this, we |
| will modify the TopComponent to offer a <tt>SaveCookie</tt>, which gives |
| the user a way to store the text typed in the text field. By offering the |
| <tt>SaveCookie</tt> when changes are made in the text field, the Save button |
| and the Save menu item under the File menu will become enabled. That is because |
| the NetBeans Platform provides a context-sensitive Action called <code>SaveAction</code>. |
| The <code>SaveAction</code> becomes enabled whenever the capability of being saved, |
| in other words, the <code>SaveCookie</code>, is available. In this case, |
| we will make the <code>SaveCookie</code> available whenever the user types |
| something in the <code>guessedWord</code> text field. Then the <code>SaveAction</code> |
| will automatically become enabled.</p> |
| <p>When the user |
| selects the enabled button or menu item, a dialog will be displayed and the button |
| and menu item will become disabled, until the next time that a change is made |
| to the text field.</p> |
| <ol> |
| |
| <li>Begin by setting a library dependency on the Dialogs API, which you learned |
| to do in the previous sections. |
| </li> |
| |
| <li>Next, we define an implementation of the <code>SaveCookie</code>, somewhere |
| within the <code>AnagramTopComponent</code> class: |
| |
| <pre class="examplecode">private class SaveCookieImpl implements SaveCookie { |
| |
| @Override |
| public void save() throws IOException { |
| |
| Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save \"" |
| + guessedWord.getText() + "\"?", NotifyDescriptor.OK_CANCEL_OPTION, |
| NotifyDescriptor.QUESTION_MESSAGE); |
| |
| Object result = DialogDisplayer.getDefault().notify(msg); |
| |
| //When user clicks "Yes", indicating they really want to save, |
| //we need to disable the Save button and Save menu item, |
| //so that it will only be usable when the next change is made |
| //to the text field: |
| if (NotifyDescriptor.YES_OPTION.equals(result)) { |
| fire(false); |
| //Implement your save functionality here. |
| } |
| |
| } |
| |
| }</pre> |
| |
| <p>We have not defined the <code>fire</code> method yet, so the related |
| statement above will be underlined in red until we do so.</p> |
| </li> |
| |
| <li><p>In the constructor, call the as-yet-undefined <code>fire</code> method, |
| passing in true this time, whenever a change is detected in the |
| <code>guessedWord</code> text field:</p> |
| |
| <pre class="examplecode">guessedWord.getDocument().addDocumentListener(new DocumentListener() { |
| |
| @Override |
| public void insertUpdate(DocumentEvent arg0) { |
| fire(true); |
| } |
| |
| public void removeUpdate(DocumentEvent arg0) { |
| fire(true); |
| } |
| |
| public void changedUpdate(DocumentEvent arg0) { |
| fire(true); |
| } |
| |
| });</pre></li> |
| |
| <li><p>Now we declare an <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/InstanceContent.html">InstanceContent</a></code> at the top of the class. The |
| <code>InstanceContent</code> class is a very powerful class in the NetBeans Platform, |
| enabling you to update the Lookup on the fly, at runtime. We also declare |
| the implementation of our <code>SaveCookie</code>:</p> |
| |
| |
| <pre class="examplecode">InstanceContent ic; |
| SaveCookieImpl impl;</pre></li> |
| |
| <li><p>Next, at the end of the constructor, we instantiate the <code>SaveCookie</code> and the <code>InstanceContent</code>, |
| while adding the <code>InstanceContent</code> to the <code>Lookup</code> |
| of the <code>AnagramTopComponent</code>:</p> |
| |
| <pre class="examplecode">impl = new SaveCookieImpl(); |
| |
| ic = new InstanceContent(); |
| |
| associateLookup(new AbstractLookup(ic));</pre></li> |
| |
| <li><p>Now we can add the <code>fire</code> method, which |
| dynamically adds and removes the <code>SaveCookie</code> |
| from the <code>InstanceContent</code>:</p> |
| |
| <pre class="examplecode">public void fire(boolean modified) { |
| if (modified) { |
| //If the text is modified, |
| //we add the SaveCookie implementation |
| //to the InstanceContent, which |
| //is in the Lookup of the TopComponent: |
| ic.add(impl); |
| } else { |
| //Otherwise, we remove the SaveCookie |
| //from the InstanceContent: |
| ic.remove(impl); |
| } |
| }</pre></li> |
| |
| <li><p>Run the application again. Make a change in the "Guessed Word" text field and |
| notice that the Save menu item is enabled:</p> |
| <p><img alt="" style="border: 1px solid black" src="../../images/tutorials/porting/70/ageditorcookie2.png"/></p> |
| |
| |
| <p>Click the menu item, |
| click the "OK" button in the |
| dialog...</p> |
| |
| <p><img alt="" style="border: 1px solid black" src="../../images/tutorials/porting/70/ageditorcookie5.png"/></p> |
| |
| <p>...and notice that the Save menu item is disabled afterwards.</p> |
| |
| </li> |
| |
| </ol> |
| |
| <p>Congratulations! Now that your application is making use of existing NetBeans Platform functionality, |
| you have taken |
| one further step |
| in successfully aligning it with the NetBeans Platform. Other modules can be now be |
| plugged into the NetBeans Platform to take advantage of, or even extend, features added by your |
| application. Hence, not only can your application benefit from what the NetBeans Platform provides, |
| but you can create features that other modules can use as well.</p> |
| <!-- |
| <p>You need to continue finding ways |
| to further align your original application with the functionality offered by the NetBeans Platform, |
| in order to make it even more of a good "good citizen" and useful member of the community of modules |
| within the application.</p> |
| |
| <p>For example, you can write a new node, with child nodes for each |
| word defined in the class:</p> |
| |
| <pre class="examplecode">public class WordListNode extends AbstractNode { |
| |
| private int index; |
| private final WordLibrary wordLibrary; |
| |
| public WordListNode() { |
| this(WordLibrary.getDefault()); |
| } |
| |
| private WordListNode(WordLibrary w) { |
| super(new WordListChildren(w)); |
| wordLibrary = w; |
| } |
| |
| WordListNode(int index, WordLibrary w) { |
| super(Children.LEAF); |
| this.index = index; |
| this.wordLibrary = w; |
| |
| setName("Index: " + index); |
| setDisplayName(wordLibrary.getWord(index)); |
| } |
| |
| @Override |
| public String getHtmlDisplayName() { |
| return "<b>" + wordLibrary.getWord(index) + "</b> (<i>" + wordLibrary.getScrambledWord(index) + "</i>)"; |
| } |
| |
| private static class WordListChildren extends Children.Keys<Integer> { |
| private final WordLibrary wordLibrary; |
| |
| public WordListChildren(WordLibrary wordLibrary) { |
| this.wordLibrary = wordLibrary; |
| } |
| |
| @Override |
| protected void addNotify() { |
| List<Integer> arr = new ArrayList<Integer>(); |
| for (int i = 0; i < wordLibrary.getSize(); i++) { |
| arr.add(i); |
| } |
| setKeys(arr); |
| } |
| |
| @Override |
| protected void removeNotify() { |
| setKeys(Collections.<Integer>emptyList()); |
| } |
| |
| @Override |
| protected Node[] createNodes(Integer index) { |
| WordListNode node = new WordListNode(index, wordLibrary); |
| return new Node[] { node }; |
| } |
| } |
| |
| }</pre> |
| |
| <p>In return, the lifecycle of the original application is now handled by the NetBeans Platform |
| and you can leverage as much of the existing modules' functionality as is reasonable for your module. In fact, |
| your original application is now no longer an application, but an integral part of a larger application.</p> |
| --> |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="tips"></a>Porting Tips & Tricks</h2> |
| |
| <p>There are several next steps one can take at this point, aside from further aligning |
| the application with the NetBeans Platform, as outlined above:</p> |
| |
| <ul> |
| <li><b>Attain a thorough understanding of what the NetBeans Platform provides.</b> |
| As you port your application, you will learn more and more about the |
| various features that the NetBeans Platform makes available. A central |
| problem is that the NetBeans Platform is quite large and attaining a thorough |
| overview of all that it offers can be a lengthy process. A quick shortcut |
| is to download and print out the <a href="http://refcardz.dzone.com/refcardz/netbeans-platform-70">NetBeans Platform 7.0 Refcard</a>, |
| which is a free DZone document that highlights all the NetBeans Platform |
| benefits, features, APIs, and many tips and tricks in an easy |
| to digest format.</li> |
| |
| <li><b>Become aware of the differences between standard Swing applications |
| and the NetBeans Platform.</b> For the most part, the standard Swing approach to |
| creating a user interface will continue to work for your NetBeans Platform |
| application. However, the NetBeans Platform approach is better, easier, or both |
| in some cases. One example is that of the NetBeans Dialogs API. The standard Swing approach, |
| via, for example, the <tt>JOptionsPane</tt>, works OK, but using the NetBeans Dialogs API is easier, |
| because it automatically centers your dialog in the application and allows you to dismiss it |
| with the ESC key. Using the Dialogs API also lets you plug in a different DialogDisplayer, |
| which can make it easier to customize or test your application. |
| |
| <p>Below is a list of the principle differences between the typical Swing approach |
| and that of the NetBeans Platform:</p> |
| |
| <ul> |
| <li>Loading of images</li> |
| <li>Loading of resource bundles and localized string</li> |
| <li>Assigning of mnemonics to labels and buttons</li> |
| <li>Showing dialogs</li> |
| </ul> |
| <p>For details on all of the above items, read |
| this FAQ: <a href="http://wiki.netbeans.org/wiki/view/DevFaqNbIdeosyncracies">Common calls that should be done slightly differently in NetBeans than standard Swing apps (loading images, localized strings, showing dialogs)</a>.</p> |
| |
| <p>In addition, note that, since the NetBeans Platform now handles the lifecycle of your module, since it is |
| now part of the whole application, you can no longer use <tt>System.exit</tt>. Instead, you need to use <tt>LifecycleManager</tt>. To |
| run code on start up, which should only be done when absolutely necessary, you need to use the NetBeans <tt>ModuleInstall</tt> class and, |
| specifically, its <tt>restored</tt> method. A useful reference in this context is <a href="http://www.ociweb.com/jnb/jnbOct2005.html#porting">Porting a Java Swing Application to the NetBeans Platform</a>, |
| by Tom Wheeler, in <a href="http://www.ociweb.com/jnb/jnbOct2005.html#porting">Building A Complete NetBeans Platform Application</a>.</p></li> |
| |
| <li><p><b>Create a module project for each distinct part of your application.</b> The |
| NetBeans Platform provides a modular architecture out of the box. Break your |
| application into one or more modules. Doing so requires some analysis of your |
| original application and an assessment of which parts could best fit within |
| a new module and how to communicate between them. Since the example in this |
| tutorial was simple, we only needed one module. A next step might be to put the |
| <tt>WordLibrary</tt> class in a separate module and expose it as a public API. |
| The <tt>StaticWordLibrary</tt> would be put into another module, providing |
| an implementation of the <tt>WordLibrary</tt> API. Doing so would let other modules |
| provide user interfaces on top of the API provided by the first module, without |
| depending in any way on the implementations.</p> |
| |
| <p>As shown above, you need to put the modules in a module suite. Then set |
| a dependency in the plugin module on the API module, using the Libraries |
| panel in the plugin module's Project Properties dialog box. The size of each module, i.e., |
| when one should create a new module or continue developing within an existing one, is a |
| question of debate. Smaller is better, in general.</p></li> |
| |
| <li><b>Always keep reevaluating what you really need to port.</b> Look at the NetBeans Platform and |
| decide where there is overlap with your own application. Where there is overlap, |
| such as the menu bar and About box, decide what you want to do. Typically, you |
| want to leverage as much as possible from the NetBeans Platform. Therefore, you |
| would port as little as possible from your own application, while keeping as much |
| of it as is useful to you.</li> |
| |
| <li><b>Move distinct parts of your user interface to one or more TopComponents.</b> On the NetBeans |
| Platform, the <tt>TopComponent</tt> class provides the top level Swing container. In effect, |
| it is a window. Move the user interface from your original application to one or |
| more of these windows and discard your original <tt>JFrame</tt>s.</li> |
| |
| <li><b>Copy the Java classes that do not provide user interface elements.</b> We simply |
| copied the original <tt>WordLibrary.java</tt> class. You can do the same with the model |
| of your own Swing applications. You might need to tweak some code to smoothen the transition |
| between the old Swing application and the new NetBeans Platform application, but (as |
| in the case shown in this tutorial) this might not even be necessary.</li> |
| |
| <li><b>Learn from others.</b> Aside from joining the dev@platform.netbeans.org mailing list, |
| also read the following two crucial articles: |
| <ul> |
| <li><a href="http://netbeans.dzone.com/10-tips-4-porting-2-netbeans">Top 10 Tips for Porting to the NetBeans Platform</a></li> |
| <li><a href="http://java.dzone.com/news/how-to-split-into-modules">How to Split an Application into Modules?</a></li> |
| </ul> |
| </li> |
| |
| <li><b>Watch the Top 10 NetBeans APIs Screencast.</b> The <a href="https://platform.netbeans.org/tutorials/nbm-10-top-apis.html">screencast series</a> |
| gives a good overview of the NetBeans Platform, with many useful code snippets and coding patterns.</li> |
| |
| </ul> |
| |
| |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20NetBeans%20Platform%20Porting%20Tutorial">Send Us Your Feedback</a></div> |
| <br style="clear:both;" /> |
| <!-- ======================================================================================== --> |
| |
| <h2><a name="nextsteps"></a>Next Steps</h2> |
| |
| <p>For more information about creating and developing NetBeans modules, 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> |