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:
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.
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.
Before you begin, you need to install the following software on your computer:
Take the following steps to install the sample:
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.
The Palette Manager appears, containing your new code snippets, as shown below:
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.
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.
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).
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.
The File Recognition panel should now look as follows:
Note the following about the fields in the File Recognition panel:
Note that manifests in JAR files are "MANIFEST.MF" and can be case-sensitive (at least on Unix). For this reason, you specify two MIME types in this tutorial—.mf and .MF.
Click Next.
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:
The Projects window should now look as follows:
Each of the newly generated files is briefly introduced:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE MIME-resolver PUBLIC "-//NetBeans//DTD MIME Resolver 1.0//EN" "http://www.netbeans.org/dtds/mime-resolver-1_0.dtd"> <MIME-resolver> <file> <ext name="xml"/> <resolver mime="text/x-jboss+xml"> <xml-rule> <doctype public-id="-//JBoss//DTD Web Application 2.3//EN"/> </xml-rule> </resolver> </file> </MIME-resolver>
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.
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:
You should now have the following files (highlighted in the screenshot below):
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; } }
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 } } }
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; } }
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); } } }
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; } }
MSG_ErrorNoFocusedDocument=No document selected. Please select a document to insert the item into. ACT_OpenJbossddCustomizer=&JBoss Code Clips
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):
In this section, we register the menu item and code snippets in the layer.xml file and in the Bundle.properties 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>
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.
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.
ACT_OpenJbossddCustomizer=&HTML/JSP Code Clips
<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>
<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.
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.
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.
The NBM file is created and you can view it in the Files window (Ctrl-2):
For more information about creating and developing NetBeans modules, see the following resources:
Version
|
Date
|
Changes
|
Open Issues
|
1 | 29 November 2005 | Initial version |
|
2 | 30 November 2005 |
|
All other issues from above must still be done. |
3 | 8 December 2005 |
All other issues from above must still be done.
|
|
4 | 22 August 2006 |
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. |