NetBeans Editor Component Palette Module Tutorial

This tutorial demonstrates how to create a component palette that provides drag-and-drop code snippets for a new file type. Code snippets serve to speed up coding. The IDE provides a component palette for JSP files, HTML files, and Form files. In this tutorial, you learn how to create a component palette for a different file type—one that is not recognized by the IDE by default. First, you use the New File Type wizard to enable the IDE to recognize the new file type. Then, you implement several classes provided by the NetBeans Palette API. Finally, you register the new component palette in the layer.xml file.

The component palette that you create in this tutorial will be used by the JBoss deployment descriptor file (jboss-web.xml). The jboss-web.xml file is distinguished from all other XML files by its namespace:

<!DOCTYPE jboss-web
    PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-web_3_0.dtd">

The component palette that you create in this tutorial will only be visible when a file with the namespace above is open in the IDE's Source Editor. For these files only, the component palette will include code snippets for three typical elements in a jboss-web.xml file: "Context Root", "Security Domain", and "Resource Reference". As shown below, the tooltip will display the tags that will be inserted at the cursor in the Source Editor when the code snippet is dragged-and-dropped:

Completed module.

If you do not want to create a new component palette, but only want to add a code snippet to an existing component palette, see the NetBeans Code Snippet Module Tutorial.

Contents

Content on this page applies to NetBeans IDE 6.0

For more information on working with modules, see the NetBeans Development Project home on the NetBeans website. If you have questions, visit the NetBeans Developer FAQ or use the feedback link at the top of this page.


Installing the Software

Before you begin, you need to install the following software on your computer:


Installing the Sample

Take the following steps to install the sample:

  1. Unzip the attached file.

  2. 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:

    Completed projects window.

  3. Right-click the project node and choose Install/Reload in Target Platform. The target platform opens and the module is installed.

  4. Verify that the module is correctly installed by opening the Files window (Ctrl-2) and double-clicking the test-jboss-web.xml file that you find there:

    Test file.

    The file opens in the Source Editor and the component palette opens, and displays three code snippets, as in the screenshot shown in the introduction of this tutorial. If the component palette does not open automatically, press Ctrl-Shift-8 to open it manually.

  5. Choose Tools > Palette > JBoss Code Clips from the main menu bar, as shown below:

    New menu item.

    The Palette Manager appears, containing your new code snippets, as shown below:

    New palette manager.

Now that you know what the end result looks like, you will create the module from scratch and learn about each part while creating it.


Setting up the Module Project

Before you start writing the module, you have to make sure you that your project is set up correctly. The IDE provides a wizard that sets up all the basic files needed for a module.

  1. Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Plug-in Modules. Under projects, select Module Project and click Next.
  2. In the Name and Location panel, type Jbosswebxml in Project Name. Change the Project Location to any directory on your computer, such as c:\mymodules. Leave the Standalone Module radiobutton and the Set as Main Project checkbox selected. Click Next.

  3. In the Basic Module Configuration panel, replace yourorghere in Code Name Base with org.netbeans.modules, so that the whole code name base is org.netbeans.modules.jbosswebxml. Leave the location of the localizing bundle and XML layer, so that they will be stored in a package with the name org/netbeans/modules/jbosswebxml. Click Finish.

The IDE creates the Jbosswebxml 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).

Recognizing the New File Type

In order to treat JBoss deployment descriptor files differently to all other types of files, we need to create a NetBeans data object specifically for this type of file, as well as a loader for loading our new data object.

  1. Right-click the project node and choose New > File/Folder and then, in the NetBeans Module Development category, choose File Type. Click Next.
  2. In the File Recognition panel, do the following:

    The File Recognition panel should now look as follows:

    Step 1 of New File wizard.

    Note the following about the fields in the File Recognition panel:

    Click Next.

  3. In the Name and Location panel, type Jbossdd as the Class Name Prefix and browse to any 16x16 pixel image file as the new file type's icon, as shown below.

    Step 2 of New File wizard.

    Note: If you do not have a suitable 16x16 pixel image file, you can right-click on this image, download it, and access it in the wizard above: Datasource.gif

  4. Click Finish.

    The Projects window should now look as follows:

    Final Projects window.

Each of the newly generated files is briefly introduced:


Creating the Component Palette and Code Snippets


Specifying the Module's Dependencies

You will need to subclass several classes that belong to NetBeans APIs. Each has to be declared as a Module dependency. Use the Project Properties dialog box for this purpose.

  1. In the Projects window, right-click the Jbosswebxml project node and choose Properties. In the Project Properties dialog box, click Libraries.
  2. For each of the following APIs, click "Add...", select the name from the Module list, and then click OK to confirm it:

    All source files.

  3. Click OK to exit the Project Properties dialog box.

  4. In the Projects window, expand the Important Files node, double-click the Project Metadata node, and note the long list of APIs that you selected have been declared as module dependencies.

Creating the Component Palette

Component Palettes are defined by the NetBeans Palette API. Implementing the NetBeans Palette API for this tutorial means implementing the following NetBeans Palette API classes:

File
Description
PaletteFactory Creates a new instance of the Component Palette. To do so, it invokes the createPalette method which creates a new palette from the JBOSSPalette folder, which is defined in the layer.xml file.
PaletteController Provides access to data in the Component Palette.
PaletteActions (to be done)

In addition, you will also create the following supporting classes:

File
Description
JbossddPaletteUtilities Handles insertion of the dropped code snippet.
JbossddPaletteCustomizerAction Provides the menu item that opens the Palette Manager.
JbossddPaletteDropDefault (to be done)

To implement the NetBeans Palette API, take the following steps:

  1. Right-click the org.netbeans.modules.jbosswebxml project node and choose New > Java Class. Create the first of the following files, type org.netbeans.modules.jbosswebxml.palette in the package drop-down list, click Finish, and then repeat the process to create the others:

    • JbossddPaletteActions
    • JbossddPaletteCustomizerAction
    • JbossddPaletteDropDefault
    • JbossddPaletteFactory
    • JbossddPaletteUtilities

    You should now have the following files (highlighted in the screenshot below):

    Main palette files.

  2. Replace the default content of the JbossddPaletteFactory.java file with the following:

    package org.netbeans.modules.jbosswebxml.palette;
    
    import java.io.IOException;
    import org.netbeans.spi.palette.PaletteController;
    import org.netbeans.spi.palette.PaletteFactory;
    
    public class JbossddPaletteFactory {
        
        public static final String JBOSS_PALETTE_FOLDER = "JBOSSPalette";
        
        private static PaletteController palette = null;
        
        public static PaletteController getPalette() throws IOException {
            if (palette == null)
                palette = PaletteFactory.createPalette(JBOSS_PALETTE_FOLDER, new JbossddPaletteActions()); 
            return palette;
        }
    }

  3. Replace the default content of the JbossddPaletteActions.java file with the following:

    package org.netbeans.modules.jbosswebxml.palette;
    
    import java.awt.event.ActionEvent;
    import java.io.IOException;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.text.JTextComponent;
    import org.netbeans.editor.Utilities;
    import org.netbeans.spi.palette.PaletteActions;
    import org.netbeans.spi.palette.PaletteController;
    import org.openide.DialogDisplayer;
    import org.openide.NotifyDescriptor;
    import org.openide.text.ActiveEditorDrop;
    import org.openide.util.Lookup;
    import org.openide.util.NbBundle;
    
    public class JbossddPaletteActions extends PaletteActions {
        
        /** Creates a new instance of JbossddPaletteActions */
        public JbossddPaletteActions() {
        }
    
        public Action[] getImportActions() {
            return new Action[0]; //TODO implement this
        }
    
        public Action[] getCustomCategoryActions(Lookup category) {
            return new Action[0]; //TODO implement this
        }
    
        public Action[] getCustomItemActions(Lookup item) {
            return new Action[0]; //TODO implement this
        }
    
        public Action[] getCustomPaletteActions() {
            return new Action[0]; //TODO implement this
        }
    
        public Action getPreferredAction( Lookup item ) {
            return new MFPaletteInsertAction(item);
        }
        
        private static class MFPaletteInsertAction extends AbstractAction {
            
            private Lookup item;
            
            MFPaletteInsertAction(Lookup item) {
                this.item = item;
            }
                    
            public void actionPerformed(ActionEvent e) {
          
                ActiveEditorDrop drop = (ActiveEditorDrop) item.lookup(ActiveEditorDrop.class);
                
                JTextComponent target = Utilities.getFocusedComponent();
                if (target == null) {
                    String msg = NbBundle.getMessage(JbossddPaletteActions.class, "MSG_ErrorNoFocusedDocument");
                    DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE));
                    return;
                }
                
                try {
                    drop.handleTransfer(target);
                }
                finally {
                    Utilities.requestFocus(target);
                }
                
                try {
                    PaletteController pc = JbossddPaletteFactory.getPalette();
                    pc.clearSelection();
                }
                catch (IOException ioe) {
                } //should not occur
    
            }
        }
        
    }

  4. Replace the default content of the JbossddPaletteUtilities.java file with the following:

    package org.netbeans.modules.jbosswebxml.palette;
    
    import java.awt.Component;
    import java.awt.Container;
    import java.util.StringTokenizer;
    import javax.swing.JTree;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Caret;
    import javax.swing.text.Document;
    import javax.swing.text.JTextComponent;
    import org.netbeans.api.project.FileOwnerQuery;
    import org.netbeans.api.project.Project;
    import org.netbeans.api.project.ProjectUtils;
    import org.netbeans.api.project.SourceGroup;
    import org.netbeans.api.project.Sources;
    import org.netbeans.editor.BaseDocument;
    import org.netbeans.editor.Formatter;
    import org.openide.filesystems.FileObject;
    
    public class JbossddPaletteUtilities {
        
        /** Creates a new instance of JbossddPaletteUtilities */
        public JbossddPaletteUtilities() {
        }
        
        public static void insert(String s, JTextComponent target)
        throws BadLocationException {
            insert(s, target, true);
        }
        
        public static void insert(String s, JTextComponent target, boolean reformat)
        throws BadLocationException {
            
            if (s == null)
                s = "";
            
            Document doc = target.getDocument();
            if (doc == null)
                return;
            
            if (doc instanceof BaseDocument)
                ((BaseDocument)doc).atomicLock();
            
            int start = insert(s, target, doc);
            
            if (reformat && start >= 0 && doc instanceof BaseDocument) {  // format the inserted text
                int end = start + s.length();
                Formatter f = ((BaseDocument)doc).getFormatter();
                f.reformat((BaseDocument)doc, start, end);
            }
            
            if (doc instanceof BaseDocument)
                ((BaseDocument)doc).atomicUnlock();
            
        }
        
        private static int insert(String s, JTextComponent target, Document doc)
        throws BadLocationException {
            
            int start = -1;
            try {
                //at first, find selected text range
                Caret caret = target.getCaret();
                int p0 = Math.min(caret.getDot(), caret.getMark());
                int p1 = Math.max(caret.getDot(), caret.getMark());
                doc.remove(p0, p1 - p0);
                
                //replace selected text by the inserted one
                start = caret.getDot();
                doc.insertString(start, s, null);
            } catch (BadLocationException ble) {}
            
            return start;
        }
    }

  5. Replace the default content of the JbossddPaletteCustomizerAction.java file with the following:

    package org.netbeans.modules.jbosswebxml.palette;
    
    import java.io.IOException;
    import org.openide.ErrorManager;
    import org.openide.util.HelpCtx;
    import org.openide.util.NbBundle;
    import org.openide.util.actions.CallableSystemAction;
    
    public class JbossddPaletteCustomizerAction extends CallableSystemAction {
    
        private static String name;
        
        public JbossddPaletteCustomizerAction () {
            putValue("noIconInMenu", Boolean.TRUE); // NOI18N
        }
    
        protected boolean asynchronous() {
            return false;
        }
    
        /** Human presentable name of the action. This should be
         * presented as an item in a menu.
         * @return the name of the action
         */
        public String getName() {
            if (name == null)
                name = NbBundle.getBundle(JbossddPaletteCustomizerAction.class).getString("ACT_OpenJbossddCustomizer"); // NOI18N
            
            return name;
        }
    
        /** Help context where to find more about the action.
         * @return the help context for this action
         */
        public HelpCtx getHelpCtx() {
            return null;
        }
    
        /** This method is called by one of the "invokers" as a result of
         * some user's action that should lead to actual "performing" of the action.
         */
        public void performAction() {
            try {
                JbossddPaletteFactory.getPalette().showCustomizer();
            }
            catch (IOException ioe) {
                ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ioe);
            }
        }
    
    }

  6. Replace the default content of the JbossddPaletteDropDefault.java file with the following:

    package org.netbeans.modules.jbosswebxml.palette;
    
    import javax.swing.text.BadLocationException;
    import javax.swing.text.JTextComponent;
    import org.openide.text.ActiveEditorDrop;
    
    public class JbossddPaletteDropDefault implements ActiveEditorDrop {
        
        String body;
    
        public JbossddPaletteDropDefault(String body) {
            this.body = body;
        }
    
        public boolean handleTransfer(JTextComponent targetComponent) {
    
            if (targetComponent == null)
                return false;
    
            try {
                JbossddPaletteUtilities.insert(body, (JTextComponent)targetComponent);
            }
            catch (BadLocationException ble) {
                return false;
            }
            
            return true;
        }
    
    }

  7. Right-click the org.netbeans.modules.jbosswebxml.palette node, choose New > File/Folder, and select Properties File from the Other category. Click Next. Name the file Bundle and then click Finish. Add the following keys to the new Bundle.properties file:

    MSG_ErrorNoFocusedDocument=No document selected. Please select a document to insert the item into.
    ACT_OpenJbossddCustomizer=&JBoss Code Clips

Creating the Code Snippets

Each code snippet requires the following files:

After you have created or added the above files to the NetBeans module, you declare them in a resource declaration XML file, which you register in the NetBeans System Filesystem by using the layer.xml file.

For details on all of the items above, work through the NetBeans Code Snippet Module Tutorial.

As an example, when you create three code snippets called ContextRoot.java, ResourceReference.java, and SecurityDomain.java, the result might be as follows (the highlighted packages below contain the code snippets and their supporting resources):

Main palette files.


Registering the Module

In this section, we register the menu item and code snippets in the layer.xml file and in the Bundle.properties file.

  1. Add the following tags to the layer.xml file:

    <folder name="Menu">
      <folder name="Tools">
        <folder name="PaletteManager">
            <file name="org-netbeans-modules-jbosswebxml-palette-JbossddPaletteCustomizerAction.instance"/>
        </folder>
      </folder>
    </folder>
    
    <folder name="JBOSSPalette">
       <folder name="JBoss">  
          <attr name="SystemFileSystem.localizingBundle" 
                 stringvalue="org.netbeans.modules.jbosswebxml.palette.Bundle"/>
          <file name="ContextRoot.xml" 
    	     url="nbresloc:/org/netbeans/modules/jbosswebxml/palette/items/resources/ContextRoot.xml"/>
          <file name="SecurityDomain.xml" 
    	     url="nbresloc:/org/netbeans/modules/jbosswebxml/palette/items/resources/SecurityDomain.xml"/>
          <file name="ResourceReference.xml" 
    	     url="nbresloc:/org/netbeans/modules/jbosswebxml/palette/items/resources/ResourceReference.xml"/>
       </folder>
    </folder>

  2. Add the following to the Bundle.properties file that is in the same package as the layer.xml file:

    JBOSSPalette/JBoss=JBoss Deployment Descriptor
    
    JBOSSPalette/JBoss/ContextRoot.xml=Context Root
    JBOSSPalette/JBoss/ResourceReference.xml=Resource Reference
    JBOSSPalette/JBoss/SecurityDomain.xml=Security Domain

    The key-value pairs listed above localize the items registered in the layer.xml file.

Reusing Code Snippets from Other Palettes

Optionally, you can merge your collection of code snippets with the code snippets provided by other modules. For example, if you want to merge your code snippets so that the jboss-web.xml file has access to the code snippets provided by the JSP module and the HTML module, take the steps below.

  1. In org.netbeans.modules.jbosswebxml.palette, change the value for the ACT_OpenJbossddCustomizer so that it matches the names of the same action provided by the HTML module and the JSP module:
    ACT_OpenJbossddCustomizer=&HTML/JSP Code Clips
  2. Hide the other two actions (the action in the HTML module and the JSP module that displays the JSP/HTML Code Clips Palette Manager) by adding the two lines in bold below to the layer.xml file:
    <folder name="Menu">
        <folder name="Tools">
    
            <folder name="PaletteManager">
    	    <file name="org-netbeans-modules-jbosswebxml-palette-JbossddPaletteCustomizerAction.instance"/>
    	    <file name="org-netbeans-modules-html-palette-HTMLPaletteCustomizerAction.instance_hidden"/>
    	    <file name="org-netbeans-modules-web-core-palette-JSPPaletteCustomizerAction.instance_hidden"/>
    	</folder>
    
        </folder>
    </folder>
  3. Finally, declare the resource declaration XML files of the HTML module and the JSP module as shadow files:
    <folder name="JBOSSPalette">
        <folder name="JBoss">
            <attr name="SystemFileSystem.localizingBundle" 
           stringvalue="org.netbeans.modules.jbosswebxml.Bundle"/>
            <file name="ContextRoot.xml" 
                   url="ContextRoot.xml"/>
            <file name="ResourceReference.xml" 
                   url="ResourceReference.xml"/>
            <file name="SecurityDomain.xml" 
                   url="SecurityDomain.xml"/>
        </folder>
        <folder name="HTML.shadow">
            <attr name="SystemFileSystem.localizingBundle" 
           stringvalue="org.netbeans.modules.web.core.palette.Bundle"/>
            <attr name="originalFile" 
           stringvalue="HTMLPalette/HTML"/>
        </folder>
        <folder name="HTML_Forms.shadow">
            <attr name="SystemFileSystem.localizingBundle" 
           stringvalue="org.netbeans.modules.web.core.palette.Bundle"/>
            <attr name="originalFile" 
           stringvalue="HTMLPalette/HTML_Forms"/>
        </folder>
        <folder name="JSP.shadow">
            <attr name="SystemFileSystem.localizingBundle" 
           stringvalue="org.netbeans.modules.web.core.palette.Bundle"/>
            <attr name="originalFile" 
           stringvalue="JSPPalette/JSP"/>
        </folder>
        <folder name="Database.shadow">
            <attr name="SystemFileSystem.localizingBundle" 
           stringvalue="org.netbeans.modules.web.core.palette.Bundle"/>
            <attr name="originalFile" 
           stringvalue="JSPPalette/Database"/>
        </folder>
    </folder>

Now, when you build and install the module, as described in the next section, the Component Palette for jboss-web.xml files will contain all the code snippets provided by the JSP module as well as all the code snippets provided by the HTML module.

Building and Installing the Module

The IDE uses an Ant build script to build and install your module. The build script is created for you when you create the module project.

Installing and Using the Module

  1. In the Projects window, right-click the Jbosswebxml project and choose Install/Reload in Target Platform.

    The module is built and installed in the target platform. The target platform opens so that you can try out your new module. The default target platform is the installation used by the current instance of the development IDE.

  2. Verify that the module is correctly installed by using it as shown in Installing the Sample.

Creating a Shareable Module Binary

  1. In the Projects window, right-click the the project node and choose Create NBM.

    The NBM file is created and you can view it in the Files window (Ctrl-2):

    Shareable NBM.

  2. Make the module available to others via, for example, e-mail.

  3. The recipient can install the module by using the Plugin Manager. Choose Tools > Plugins from the main menu.


Send Us Your Feedback

Next Steps

For more information about creating and developing NetBeans modules, see the following resources:


Versioning

Version
Date
Changes
Open Issues
1 29 November 2005 Initial version
  • Needs to be reviewed! Use at your own risk!
  • Need to add explanation for adding own dialog box for predefining values.
  • Explanatory text for the use of the NetBeans APIs to be added.
  • Check for copy-paste errors.
  • Need to add more Javadoc links, for NetBeans API classes in the code.
2 30 November 2005
  • Changed the ZIP file because of problems (see Issue 69571).
  • Removed hyphens and changed screenshots.
  • All other issues from above must still be done.
    3 8 December 2005
  • Fixed reopened issue Issue 69571
  • All other issues from above must still be done.
    4 22 August 2006
  • Minor tweaks.
  • All other issues from above must still be done.
    Discovered that the editor support file is missing in the tutorial, but not in the module that you can download from the top of the file.
    Discovered that there are several gaps and tbds in this tutorial.
    5 30 May 2007 Began updating this tutorial for 6.0.