Tuesday July 31, 2007
Extend an Existing NetBeans Project Type for Grails?
As mentioned in Adding Nodes to an Existing Project Type's Logical View, in 6.0 NetBeans module authors are encouraged to—as far as reasonably possible—extend existing project types, rather than creating new ones. The thinking is that in doing so, there will be more consistency between them. The more new ones that are created, the more potential conflicts there might end up being. Plus, there's no need to reinvent the wheel—you can 'simply' slot your own nodes into the nodes of an existing project type, checking the lookup to determine whether the nodes should be created or not.
So, when thinking about creating a new project type, the first thing you should think is: "Wouldn't it make more sense to extend one that already exists? After all, in 6.0 I can extend an existing project type's logical view, customizer, and lookup... so what is so special about my project type that I can't use one of the existing project types as a starting point?" In this light, have a look at this screenshot, which shows one of the samples that comes with the Grails distribution, which I opened in the IDE, via the 'Open Project' dialog box:
There is still a very long way to go (such as the fact that I can't deploy the application yet), and possibly the approach I've taken is completely wrong (though I've consulted a bit with Milos Kleint, who created the APIs for extending project types), but here one can see the benefits (and potential pitfalls) of extending an existing project type. For example, a benefit is that the server support for my Grails application is baked right into the original web application project type. A pitfall is that that same project type lets the user add support for web frameworks... I doubt that one would want the user to be able to add JSF or Struts or something similar to a Grails application. And, even if one would want that, one would probably want to do so in a different way to standard web applications. (Maybe I should be extending the freeform web application project type instead, which doesn't support web frameworks. Alternatively, maybe I should simply hide the customizer panel for adding web frameworks.) So... as one can see, just from these random ruminations, there is a lot one can gain and a lot one can lose from the ability to extend an existing project type. But Grails is probably an extreme example. Adding web service support or JAXB support to an existing project type is more likely to be the typical use case in this regard. Still, let's see how far I get! And, at the same time, Martin Adamek is taking the new-project-type approach to creating Grails support (in the few seconds per day that he has some free time), so maybe he'll end up with a better end result. It will be interesting to compare the pros and cons in this real life situation.
Jul 31 2007, 09:45:57 AM PDT Permalink
Nirvana is a Paperless Office
As Roumen reported, in NetBeans Is Moving!, NetBeans is moving. Or, in fact, has already moved, to the other end of town. In doing so, I have begun implementing my long cherished dream... a paperless office. Not only because you can't lose papers that you don't have, not only because cleanliness is next to godliness, not only because Al Gore would be proud of me, not only because of the ethical soundness of the principle, but because a workspace that is permanently denuded of chaos has a pleasing and soothing effect, conveying (at least the intention of) order and and simplicity:
Before I forget them, here are my personal tips and tricks for establishing and maintaining a paperless office:
- Don't move your stuff. When you move to a different office (as we have just done), grab the opportunity to not move all your stuff with you. Destroy it all. (Or cheat and take it home, creating a paperless office at the cost of a chaotic home environment.)
- Don't connect to the printer. Once you are in your fresh, new, pristine office: Don't set up a connection to the printer! If you can't print, you won't generate paper, will you?
- Avoid getting access to your drawers and cupboards. Notice the cupboards above my desk in the picture above? They were locked when I got here. I'm supposed to go somewhere and pick up the keys for these cupboards. My advice: Don't go and pick up those keys! If you can't put stuff in your cupboards, because they are locked, then useless stuff that you should just throw away (and that you probably won't have much of anyway since you can't print) won't collect in your cupboards or drawers (or other similar junk-collecting containers).
- Be ever watchful. Whenever you find yourself with something in your hand, ask yourself if you need it to be there and whether you can avoid taking it to your desk. If you can't avoid taking it to your desk (from which it will become increasingly difficult to remove, because the longer it is there, the greater the significance it will attain), put it in your bag, instead of on your desk. Okay, that's cheating, but you're more likely to empty your bag than clean your desk. Or maybe that's just me. (When placed on a solid surface, the weight of an object increases in proportion to the length of time it isn't moved. Based on empirical research, I have found this axiom to be unfailingly true.)
- Blog about your intention to maintain a paperless office. Once you've blogged about it, you can't go back. You've reached the point of no return. Paperless is what you said and paperless is what it will have to be for ever after. Chances are that you will get mocked by colleagues as soon as paper starts collecting on your desk, which is further incentive for maintaining paperlessness.
So, this is me, in my new home, i.e., the little corner of the Sun world that is my new paperless office:
And guess who the twists and turns of fate have conjured up as my new neighbor...

Jul 30 2007, 07:45:28 AM PDT Permalink
Checking HTML Links Across Helpsets
In Link Checking and Iterating Through Folders, I described how one would need to iterate through folders to determine whether HTML links resolve correctly or not. However, that was only within helpsets. The NetBeans Platform also supports linking between helpsets (or across helpsets). Maybe you have one topic that references a topic that is defined in a different module within the same application. These links have their own syntax. For example, a standard HTML link goes like this:
<a href="../debug/breakpoint_about.html">About Debugging Java Applications</a>
Here, a link checker would need to go one folder up, then look for a folder called 'debug' and then look for a topic called 'breakpint_about.html'. That's relatively simple and the approach is described in the topic referenced above. However, a cross helpset HTML link is more complex:
<a href="nbdocs://org.netbeans.modules.usersguide/org/netbeans/modules/usersguide/xml/xml_validate.html">Validating an XML Document</a>
Somehow, from that, one would need to extract the top level folder of the module in which the xml_validate.html topic is found, as well as the folder "xml", if possible, and then the topic itself. That's quite a bit more work, especially since one would want the code to be generic, so that it could be used from all modules to any other module. That means that one cannot assume that the top level folder, to which one must traverse, somehow, is at a fixed location. Instead, one needs to go up a level, look for the name of the folder, match it with something from the link above, and then, if the match fails, continue going up indefinitely. Then, once the top level folder (e.g., 'usersguide' or 'j2ee') has been found, one needs to look within that folder for the right subfolder containing the help topic.
I'm pretty sure that my solution doesn't work yet in all cases (need to try out more cases for at least a week before publishing the module), but it definitely works in some, as can be seen from this screenshot, which shows two different annotations, one for a broken cross-helpset link and one for a broken intra-helpset link, together with information in the form of hyperlinks in the Output window:
I made the annotations different (i.e., slightly different color and a different icon) so that one can see in one glance whether the problematic link is cross-helpset or intra-helpset. Notice that the Output window shows that there are two cross-helpset links that have been resolved correctly, while one cross-helpset link has been found to be problematic (pink annotation in the editor). In addition, there is one intra-helpset link that is problematic (red annotation in the editor).
Update. I updated the HTML Link Checker on the Plugin Portal, with the functionality described above, because thus far it works as expected. Go here to get it, currently at version 2.0.
Jul 29 2007, 03:09:35 AM PDT Permalink
Indentation Code Correction for Page 350
Page 350 of Rich Client Programming: Plugging into the NetBeans Platform, is problematic because it uses the BaseDocument class, which will be deprecated soon, as mentioned in Errata for "Rich Client Programming: Plugging into the NetBeans Platform". The code concerns the indentation of code that is dropped from the Palette into an editor. Today I received from Jarda a code snippet that replaces and corrects the problematic code. So, on page 350, the complete JavaSourceFilePaletteUtilities class should be as follows, although note that I have included some commented-out code lines, which are some of the original lines in the code that are now obsolete:
public class JavaSourceFilePaletteUtilities {
public static void insert(final String s, final JTextComponent target) throws BadLocationException {
// Not needed, because string will never be null:
// if (s == null) {
// s = "";
// }
//Use StyledDocument, because needed
//by NbDocument.runAtomicAsUser, later:
final StyledDocument doc = (StyledDocument) target.getDocument();
if (doc == null) {
return;
}
// No more BaseDocument:
// if (doc instanceof BaseDocument) {
// ((BaseDocument) doc).atomicLock();
// }
// int start = insert(s, target, doc);
// if (reformat && start >= 0 && doc instanceof BaseDocument) {
// int end = start + s.length();
// Formatter f = ((BaseDocument) doc).getFormatter();
// f.reformat((BaseDocument) doc, start, end);
// }
class InsertFormatedText implements Runnable {
public void run() {
try {
insertFormated(s, target, doc);
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
}
InsertFormatedText insert = new InsertFormatedText();
// No more BaseDocument check:
// if (doc instanceof BaseDocument) {
// ((BaseDocument) doc).atomicUnlock();
// }
//This starts the run() in the Runnable above:
NbDocument.runAtomicAsUser(doc, insert);
}
private static int insertFormated(String s, JTextComponent target, Document doc) throws BadLocationException {
int start = -1;
try {
//Find the location of the cusor in the editor,
//and if it is a selection, remove it,
//to be replaced by the dropped item:
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);
start = caret.getDot();
//Insert the string in the document,
//using the indentation engine
//to create the correct indentation:
IndentEngine engine = IndentEngine.find(doc);
StringWriter textWriter = new StringWriter();
Writer indentWriter = engine.createWriter(doc, start, textWriter);
indentWriter.write(s);
indentWriter.close();
doc.insertString(start, textWriter.toString(), null);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} catch (BadLocationException ble) {
Exceptions.printStackTrace(ble);
}
return start;
}
}
If you want to see the above code within the context of a sample, go here in the Plugin Portal, where I've updated the Java Source File Palette Sample to use the code above.
Jul 27 2007, 07:41:35 AM PDT Permalink
Resource Bundle Searcher in the IDE
One of the cool facilities you get when you install the testing tools in the IDE is a resource bundle searcher. Just type in some text in the window, and then a search is done through all the installed modules, and the results are listed at the bottom of the window. When you click on the results, you can choose the most appropriate line of code, which is then copied to the clipboard, from where you can paste it in your own code. Handy, right? Here's the whole tool in action:

This is part of the testing tools support in the IDE, enabled by a series of buttons in the toolbar. Just click one of the buttons, and you get the window above. I will discuss the other tools in coming blog entries.
Jul 26 2007, 02:47:34 PM PDT Permalink
Tim's Selection Tutorials Ported to 6.0
Tim's highly acclaimed 4-part selection tutorial series for the NetBeans Platform has been updated to 6.0:
- NetBeans Selection Management Tutorial I—Using a TopComponent's Lookup
- NetBeans Selection Management Tutorial II—Using Nodes
- NetBeans Nodes API Tutorial
- NetBeans Property Editor Tutorial
You should follow these tutorials sequentially; they build on top of each other. They are now correct for 6.0, at least, for Milestone 10. They will of course be tried out again several times by the time the final release of 6.0 comes out. At that time, they will be linked to from the http://platform.netbeans.org/tutorials page.
A few small but annoying bugs in the series have been fixed, mainly thanks to Roxie Rochat. Thanks Roxie! I found some of your e-mails where you reported several issues that had not been resolved, mainly in the third part of the series. They're all fixed now, though I need to check your e-mails again several times to make sure. Something Bennie Vaessen reported has also been fixed.
However, a few small things must still be worked on (e.g., how to correctly replace the usage of ErrorManager) and the final solution of the 4 tutorials (as well as individual parts) will be made available to the Plugin Portal, and then referenced at the top of the tutorials, by the time 6.0 comes out. (Update: The completed sample can already be downloaded from the Plugin Portal. Click here to jump to the correct page, where you can simply click the Download button.)
Purely as an exercise or refresher, I highly recommend that every NetBeans Platform developer should go through all 4 parts of this series at least once a month. Irrespective of your level of skill and knowledge. I'm willing to bet that you'll learn something new each time. From Lookup to Nodes to Property Editors, this series is really extremely broad in its coverage and helpful in its explanations. Big bonus in the 6.0 version—I added lots of new screenshots, to soothe the weary eye and sustain you on your journey!
In other news. A concise and persuasive testimonial about the NetBeans Mobility Pack, by someone who is actually using it: Why I use NetBeans Mobility Pack to write Cotopia.
Jul 25 2007, 11:57:59 PM PDT Permalink
Communicating with the User (and Yourself)
If you use System.out and the JOptionPane to communicate with your users, or with yourself during debugging or testing, you might be interested in knowing about similar (better!) facilities that the NetBeans APIs make available:
- Class StatusDisplayer. Provided by the UI Utilities API, this class lets you write to the IDE's status bar. I've found this a very simple way of sending messages to myself, because it just requires you to type a single simple (yet powerful) line:
StatusDisplayer.getDefault().setStatusText("hello world");And that's all, nothing more than that, apart from declaring the dependency on UI Utilities API. And now you'll get your message displayed in the IDE's status bar:

- Class OutputWriter. Provided by the I/O APIs, this class is a subclass of PrintWriter, letting you write to the Output window. You can create a new tab there and then write to it:
OutputWriter writer; InputOutput io = IOProvider.getDefault().getIO("Hello Output", false); writer = io.getOut(); writer.println("hello world");The above would give you a "Hello Output" tab with the content of the println statement:

(Maybe it's just me, but I think that NetBeans IDE 6, thus far, on JDK 6 under Ubuntu Linux doesn't always look very nice by default. The screenshot above is a case in point. Probably I need to experiment with look and feels or something.)
However, what is interesting about this approach is that you can add a NetBeans API OutputListener to the println, which will result in hyperlinks being created in the Output window. (I've blogged about the OutputListener quite a lot in this blog, so just do a search if you want more info.) So, here's something slightly more complex than the example above:
OutputWriter writer; InputOutput io = IOProvider.getDefault().getIO("Hello World Output", false); writer = io.getOut(); writer.println("Click me: "); writer.println(dObj.getPrimaryFile().getPath(), new HelloOutputListener(dObj));So, the only difference between the above snippet and the previous snippet is that I now have the path to my file, retrieved from the data object, and an OutputListener. That's a whole separate class, which in this case receives the data object and, when the link in the Output window is clicked, the related file opens:
class HelloOutputListener implements OutputListener { DataObject dObj; public HelloOutputListener(DataObject dObj) { this.dObj = dObj; } //Specify what should happen when the hyperlink is selected: public void outputLineSelected(OutputEvent arg0) { } //Specify what should happen when the hyperlink is clicked: public void outputLineAction(OutputEvent arg0) { OpenCookie open = (OpenCookie) dObj.getCookie(OpenCookie.class); open.open(); } //Specify what should happen when the hyperlink is cleared from the Output window: public void outputLineCleared(OutputEvent arg0) { } }And here is the result in the Output window which, when clicked, opens the specified file:

- Class StatusBar. Provided by the Editor Library API (which at some future point may end up getting deprecated in favor of Editor Library 2 API), this class gives you access to an editor's status bar. The editor's status bar consists of cells, some of which are in use by default (which you can also manipulate), plus you can add your own cells to it. A very handy tutorial has been written about this class, by one of our users, and I am hoping it will be released soon. There's a lot you can do with an editor's status bar, pretty much each part of it is customizable. Here's what I've figured out—how to display, in the editor's status bar, the fully qualified file name of the open document:

Again, be aware that this class is from an API that is on shaky footing, although I hope that whenever it is deprecated it will be replaced by some kind of alternative, but here's the code that makes the above possible:
//Get the data object: DataObject dObj = (DataObject) activatedNodes[0].getCookie(DataObject.class); //Get the file: File f = FileUtil.toFile(dObj.getPrimaryFile()); //Get the editor: JTextComponent editor = EditorRegistry.lastFocusedComponent(); //Get editor ui for editor: EditorUI editorUI = Utilities.getEditorUI(editor); //Get status bar for editor ui: StatusBar statusBar = editorUI.getStatusBar(); //Add label to status bar, //giving two strings, the first is the default length of the cell, //and the second is the maximum length, which here is the same as the content: JLabel cell = statusBar.addCell("My Cell", new java.lang.String[]{f.toString()}); cell.setText(f.toString());There's lots you can do with that status bar. For example, let's add a JTextField instead of a JLabel. This means we must get the panel from the status bar and then we can simply add whatever we like (within reason, given the space that we have available) to that panel:
JTextField cell = new JTextField(); cell.setText(f.toString()); JPanel panel = statusBar.getPanel(); panel.add(cell);
And now we have a JTextField instead of a JLabel:

- Class DialogDisplayer. Any discussion about messaging to users (and yourself) would be incomplete without mentioning the DialogDisplayer class. Normally, implementations of this class are found in close proximity to implementations of the NotifyDescriptor class. For example:
NotifyDescriptor d = new NotifyDescriptor.Message("Hello...", NotifyDescriptor.INFORMATION_MESSAGE); DialogDisplayer.getDefault().notify(d);So, what's the difference between DialogDisplayer and JOptionPane? I just phoned up Jarda Tulach to ask him. The answer is: "Better integration with the NetBeans Platform." For example, some actions are automatically disabled when the DialogDisplayer is shown, unlike with JOptionPane which doesn't know anything about the NetBeans Platform, and in addition the open Help window functions better in this case.
So, these are some handy approaches to communicating with the user (and yourself). [In the latter case, i.e., when communicating with yourself, you probably should be using the Debugger or the Profiler, by the way.] Of the above, I use the StatusDisplayer most frequently, because it is just one line and easy to remember. Also, using DialogDisplayer or JOptionPane means you have to click the OK button (potentially two zillion times, which is no fun). In the latter case, using the Output window (or System.out, of course) is a good choice. (By the way, I've found that I can only use System.out on Ubuntu Linux when I start the IDE from a terminal window. Then System.out prints to the terminal window.) The Output window is, of course, especially interesting when you're creating some kind of debugging tool for the user, with error messages appearing in the Output window, which can then be clicked to jump to the place where the error occurred in the editor. Using the editor's status bar is quite fun but, again, remember that the future of that approach is unclear.
Are there other ways of communicating with the user (and yourself)? Maybe you could print to a file outside the IDE, for example. But that's not an approach specific to the NetBeans APIs. You could also print messages to a TopComponent, which wouldn't be hard at all. Just create a TopComponent, add a JTextArea, and then whenever you need to communicate something, just use setText() on the JTextArea. Couldn't be much simpler. Basically you'd then have your own Output window.
In other news. Still investigating the different testing tools, will blog about these soon. But, nothing that I'll write about is not already available right now on the very good http://testtools.netbeans.org/ page.
Jul 24 2007, 05:48:10 AM PDT Permalink
Parsing the Command Line to Disable a Module
Yesterday we looked at how to disable a menu item, based on the presence/absence of an option on the command line. However, let's now go a whole step further and... disable a module based on something parsed from the command line. Let's assume we have a module that provides an Admin window, invoked from a menu item in the Help menu, as shown below:

Now, if anyone other than the admin user starts up the application, the window above, as well as the menu item that shows it, should not be there. So, unless the bit in bold below is present, the window and menu item above should be absent:
/bin/sh "/home/geertjan/netbeans-6.0m10/bin/netbeans" --name admin
Everything is the same as yesterday, i.e., create an OptionProcessor and register it in META-INF/services. At this point, you need to know about an XML file that conforms to the http://www.netbeans.org/dtds/module-status-1_0.dtd. Each installed module has such a file, written to the config/Modules folder in the application's user directory. This is how it looks for the module that installs the Admin window above:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//NetBeans//DTD Module Status 1.0//EN"
"http://www.netbeans.org/dtds/module-status-1_0.dtd">
<module name="org.netbeans.modules.adminwindowmodule">
<param name="autoload">false</param>
<param name="eager">false</param>
<param name="enabled">true</param>
<param name="jar">/home/geertjan/NetBeansProjects/
AdminWindowModule/build/cluster/modules/
org-netbeans-modules-adminwindowmodule.jar</param>
<param name="reloadable">true</param>
<param name="specversion">1.0</param>
</module>
I highlighted the 'enabled' parameter above because that's what we need to set to 'false', if the name set on the command line is not admin. How to do it? Regular readers of this blog will (or at least should) now immediately think "Hurray, we get to use the JAXB Wizard again!" Well, guess what? You're wrong. Initially I thought so too, until I learned about the brand new 6.0 API called Auto Update Services. Basically, this provides an abstraction layer on top of the Module XML file, so that you don't need to deal directly with JAXB, or some other XML parsing alternative. To give you a feel for it, here's my new implementation of the OptionProcessor.process method:
public void process(Env env, Map values) throws CommandException {
String[] args = (String[]) values.get(name );
//First check if the user specified 'admin',
//if NOT, continue with disabling the module:
if (args.length > 0 && !args[0].equals("admin")) {
//Get all the modules in the Plugin Manager:
List updateUnit = UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE);
for (int i = 0; i < updateUnit.size(); i++) {
//Get all the modules that are installed:
UpdateElement el = updateUnit.get(i).getInstalled();
//Of those that are installed, if it has the code name base
//of the module we are interested in, and it is enabled,
//continue with this procedure to disable it:
if (el != null && el.getCodeName().equals
("org.netbeans.modules.adminwindowmodule")
&& el.isEnabled()) {
try {
//Specify how we want to handle the module;
//here, we want to disable it:
OperationContainer oc = OperationContainer.createForDisable();
oc.add(el);
//Finally, do the operation,
//passing a progress handle or, as in this case, null:
OperationSupport supp = (OperationSupport) oc.getSupport();
supp.doOperation(null);
} catch (OperationException ex) {
Exceptions.printStackTrace(ex);
}
}
}
//Here, if the user DID specify 'admin',
//we do the same as the above, except that we enable
//the module if it is found to be disabled:
} else if(args[0].equals("admin")) {
List updateUnit = UpdateManager.getDefault().
getUpdateUnits(UpdateManager.TYPE.MODULE);
for (int i = 0; i < updateUnit.size(); i++) {
UpdateElement el = updateUnit.get(i).getInstalled();
if (el != null && el.getCodeName().equals
("org.netbeans.modules.adminwindowmodule")
&& !el.isEnabled()) {
try {
OperationContainer oc = OperationContainer.createForEnable();
oc.add(el);
OperationSupport supp = (OperationSupport) oc.getSupport();
supp.doOperation(null);
} catch (OperationException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
As a result of the above, the 'enabled' parameter is set to false, as shown below, whenever the user is not called admin:
<param name="enabled">false</param>
So now, without JAXB or any XML parsing whatsoever, I've changed one parameter in the Module XML file, based on something typed in the command line. Plus, the change is reflected immediately, i.e., the module is reinstalled. How cool is that? I think that's really exactly what I was looking for. No need to parse the XML directly. And no need to reinstall the module via some workaround after making the change. All that is handled by the aforementioned Auto Update Services API. Thanks a lot to the developers who helped me out with this today.
Jul 23 2007, 09:16:26 AM PDT Permalink
Parsing the Command Line to Enable a Menu Item
Let's set things up so that we have an Admin menu item that is always disabled, unless the following bit in bold is present within the command line:
/bin/sh "/home/geertjan/netbeans-6.0m10/bin/netbeans" --name admin
So, if the "name" option is not set to "admin", we will see a greyed out Admin menu item in the Tools menu:

"What?! Does that mean that my application on the NetBeans Platform can somehow parse the command line?!" Yes, that's exactly what that means. "But... wow... isn't that amazing?!" Well, yes, if you think so. :-)
To see how everything fits together, do the following:
- Create a 'conditionally enabled' action via the New Action wizard. Put it in the Tools menu (or anywhere else). Finish the wizard and you will have created an implementation of the CookieAction class. Now override the very cool enable method, as follows:
@Override protected boolean enable(Node[] arg0) { super.enable(arg0); if (getUserName().equals("admin")) { return true; } return false; }So, only if a method called getUserName returns "admin", will the menu item be enabled. In all other cases, it will be disabled, i.e., greyed out.
- Within the CookieAction implementation, add the following:
private static String name; static void setName(String string) { name = string; } private String getUserName(){ return name; } - Now let's feed the above setName method with the value of the 'name' option, retrieved from the command line. Add a dependency on the (brand! new!) Command Line Parsing API. (Well, it's been around for several months already, something like half a year, but it is for 6.0 only, already available in all milestones, so forget about 5.5 and 5.5.1 already and welcome to the future, also known as "6.0".)
Create a class called MyOptions and let it extend OptionProcessor. You will find that you now need to implement two methods, called getOptions and process. The names of these methods are very clear. The getOptions method is for... getting the options from the command line. The process method is for... doing something useful with the options that the getOptions method got for you.
But let's first define a new option. "Oh my god! I can define new command line options? You've got to be kidding me! That is so cool!" Relax, okay. In the greater scheme of things all this is arbitrary. But, nevertheless, here's our new option, neatly declared at the top of the class:
private static Option name = Option.requiredArgument(Option.NO_SHORT_NAME, "name");
Here, the option is required. However, it could also be an optional option instead. A variety of alternative methods could be set on the Option object. So, here we have an option called 'name'. And there's an 'Option.NO_SHORT_NAME' constant which, when I consult the javadoc, is a "constant that represents no short name indicator". Hmm. Not much wiser about that one then.
Next, we implement the two required methods:
public Set getOptions() { return Collections.singleton(name); } public void process(Env env, Map values) throws CommandException { String[] args = (String[]) values.get(name ); if (args.length > 0) { StatusDisplayer.getDefault().setStatusText("Hello " + args[0]); SomeAction.setName(args[0]); } }So, if getOptions returns an argument, we take the first option and we greet our user in the status bar, plus we pass the name to our CookieAction class. And that's all there is to it, in terms of coding.
- Finally, expand the META-INF services node, within the Important Files node. There are two nodes there. One is called "all services". Expand that. Scroll a looong way down, until you get to a node called 'org.netbeans.spi.sendopts.OptionProcessor'. Right-click that node. Choose 'Add New Service'. A dialog box pops up. Now browse to your MyOptions class, i.e., the class that extends OptionProcessor.
- Install the module.
- Restart the application, after making sure you type the name option on the command line. Try 'admin' as a value and notice that the menu item is enabled. Then type anything else as a value and notice that the menu item is greyed out. Hurray, you've parsed the command line and done something useful with the result.
Further reading:
- Command Line Parsing API
- How to use certain NetBeans APIs (in this document, search for 'Command Line API' and then you'll find lots of very useful information)
- Command Line Parsing API in NetBeans Platform and META-INF/services Browser for NBM project, which are both useful entries in Xzajo's Weblog
- Parsing the Command Line to Disable a Module
And now... happy command line parsing!
Jul 22 2007, 03:49:28 PM PDT Permalink
Testing Tools for Rich Client Applications
There are surprisingly many ways of testing applications built on the NetBeans Platform. Or any other kind of Java application in NetBeans IDE. In this regard, the URL that you should paint on your bedroom ceiling is this one:
http://testtools.netbeans.org/
That page sums everything up. Let's set up the IDE with the modules that will provide all the functionality we need. These modules will soon be in the update center, but for now can be downloaded here. When you go there, you will see this:

Be greedy and click 'all files in zip'. Download the ZIP and unzip it. You now have a bunch of NBM files. Use the Plugin Manager to install the whole lot, so that in the Plugin Manager you see this:

After installation, go to the New Project wizard and you will see a new Test Tools category, containing two sample projects:

If you complete the wizard for the Paint Application, you will find that you end up with the same familiar Paint application, together with two test classes:

Run the tests by right-clicking the Paint module and choosing from the XTest menu. Make sure to build the Paint Application suite first.
Meanwhile, the New File wizard now contains the following two templates, so that you can add some test classes to your own existing applications:

Experiment with the above and see the results. Then apply what you like to your own applications. I will be discussing my own discoveries in the coming blog entries on this topic. So, this is basically Part 1, where you now have everything set up and ready for some experimentation.
Jul 20 2007, 09:19:01 AM PDT Permalink
Peppy in the Plugin Portal
Toni made the start of the Peppy module available in the Plugin Portal (click here). One reason why I was hoping he'd do so is that I wanted to learn from his GlassPane code, because he now has Peppy in a dragable format, so that one can drag him around the entire frame of the IDE. Or your own application. That's quite fun. And a variety of random tips bubble from his mouth:

When you click the "More..." link in the speech bubble, you're taken straight to Kirk's Performance site, from whence his tips originate. How cool is that? I adapted Toni's code while playing with the JEditorPane I talked about yesterday and now it can be dragged around the IDE, just like Peppy:

However, of course, now that windows are undockable, I could simply undock the NetBeans Java Editor and have the same effect as the above. Plus, the undocked Java Editor can be dragged out of the IDE's frame.
I'm looking forward to extending Peppy. Via the layer, other Peppys should be addable. Something like this in the layer should be possible, so that each Peppy could be provided in a separate module:
<folder name = "peppies">
<folder name ="peppy">
<file name ="peppy.gif"/>
<file name ="performance-tips.properties"/>
</folder>
<folder name ="roman">
<file name ="roman.gif"/>
<file name ="ide-tips.properties"/>
</folder>
<folder name ="tor">
<file name ="tor.gif"/>
<file name ="ruby-tips.properties"/>
</folder>
</folder>
As stated in Toni's blog (here), anyone can send a GIF of their favorite (programming) hero, with some of their helpful tips and other pearls of wisdom, and then they will be transformed into the next Peppy. Can you imagine a nicer birthday present for a fellow programming colleague, for example?
Jul 19 2007, 12:57:23 PM PDT Permalink
NetBeans Java Editor in JEditorPane
Finally, thanks to Josh Sandusky, I managed to get code completion in a JEditorPane, by reusing the NetBeans Java Editor. Here's a TopComponent that contains the NetBeans Java Editor in a JEditorPane:

The syntax coloring is very basic, but that's the syntax coloring in my current version of NetBeans IDE 6.0. The code that you see above is the constructor of the TopComponent, which associates the NetBeans Java Editor with the JEditorPane.
One needs to create a Java file somewhere, as shown above. Would be better, and is possible I think, to create the file in memory. I've also noticed that the code completion doesn't work in some cases, such as sometimes when the JEditorPane is empty. So one could use the setText() method on the JEditorPane to add some text programmatically.
And now I can use code completion, as well as some of the other features of the NetBeans Java Editor. Here's what I see when I press Ctrl-Space, i.e., code completion, in the Exception statement below:

Would be nice if this were also possible in other Swing components, but without being able to set a MIME type, that's not possible, it seems. So, for example, a JTextField could not be used in this way.
Jul 18 2007, 03:43:31 PM PDT Permalink
PrintCookie (Part 2)
In PrintCookie (Part 1) we enabled the Print menu item. But, so what? That's like switching on the oven—nothing more than a means to an end. Let's now actually use the PrintCookie. But, first, how does it work? Wouldn't it be simpler to just create a "Print" button on our TopComponent, so that we could click it whenever we want to print the content of the TopComponent? Yes, it would, possibly. But then you wouldn't be providing the same user experience as provided by all the other windows in your application, which all make use of the Print menu item. And that's what's cool about the Print menu item. It provides the necessary infrastructure out of the box. Only once a PrintCookie is present, does the Print menu item become enabled. The underlying Print action, which is what's invoked when you press the Print menu item, is a context-sensitive CookieAction that listens for the presence of a PrintCookie. And because, in PrintCookie (Part 1), we created a node that provides a PrintCookie, and then added that node to the TopComponent's Lookup, the PrintCookie is present and hence the Print menu item is enabled.
When selected, our initial implementation of a Print menu item brings up a clear yet meaningless result:
class MyPrintCookie implements PrintCookie {
public void print() {
JOptionPane.showMessageDialog(null, "Printing...");
//Put all the standard Java printing code here!
}
}
Let's now put something in there so that we continue with the clarity but add some meaningfulness as well. We'll use Kirk Pepperdine's TopComponent, from a few days ago, as an example. This is what it looks like, displaying simulated output from some profiling tool that he's working on:

When we select the Print menu item, under the File menu, we will create a print preview, consisting of a screenshot:

The code used here comes from Chapter 22: Printing in the on-line version of Swing, by Matthew Robinson and Pavel Vorobiev. Possibly there's a new and better way of doing previews in the meantime. I found out from Toni Epple about printing JTables recently. But for preview functions, what follows is the only successful way I know how, without needing to make use of some additional library or something for which I need to pay. (I've seen some NbDocument methods that might be useful, but haven't looked at them in detail thus far.)
Ultimately, instead of showing a screenshot, it would be better to show the whole table, but I just haven't figured out how to do that yet, so the screenshot approach will be what I use here as an example, just to show how standard Java printing functionality integrates with the NetBeans API print infrastructure. Well, in fact, the NetBeans API side of the story is done, because the Print menu item is enabled. Everything else is pure JDK code. As Tom Wheeler wrote, in an e-mail to me last week: "NetBeans hasn't made printing substantially easier or harder, it is just a matter of putting normal Java printing code inside the PrintCookie.print() implementation."
Let's begin by creating a utility method for creating an image from a Swing component, which will be our JScrollPane, in this case:
private BufferedImage getImage(Container container) {
BufferedImage image = new BufferedImage(container.getWidth(),
container.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
container.paint(g2);
g2.dispose();
return image;
}
And here's our pretty unwieldy internal class, in the TopComponent, which follows the Printing API's requirements, with the addition of our NetBeans API PrintCookie:
class MyPrintCookie implements PrintCookie, Printable, ImageObserver {
/* image to print */
protected RenderedImage image;
/** Prepare the image to fit on the given page, within the given margins.
* Returns null if it is unable to prepare the image for the given page.
* Throws a IllegalArgumentException if the page were too small for the image.
**/
protected RenderedImage prepareImage(PageFormat pf) throws IllegalArgumentException {
try {
AffineTransform af = new AffineTransform();
pf.setOrientation(PageFormat.LANDSCAPE);
image = (RenderedImage) getImage(jScrollPane1);
/** notify if too big for page **/
if (pf.getImageableWidth() - pf.getImageableX() < image.getWidth() ||
pf.getImageableHeight() - pf.getImageableY() < image.getHeight()) {
throw new IllegalArgumentException("Page too small for image"); //NOI18N
}
AffineTransformOp afo = new AffineTransformOp(
af, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
BufferedImage o = (BufferedImage) image;
BufferedImage i = new BufferedImage(o.getWidth() + (int) pf.getImageableX(),
o.getHeight() + (int) pf.getImageableY(), o.getType());
return afo.filter((BufferedImage) image, i);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/** Print the content of the object. */
public void print() {
Printable printable = new MyPrintCookie();
new PrintPreview(printable);
}
/* Implements Printable */
public int print(Graphics graphics, PageFormat pageFormat, int page)
throws PrinterException {
if (page != 0) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) graphics;
if (image == null) {
/** prepareImage() failed,
* most probably cause is image does not implement RenderedImage,
* just draw the image then.
**/
graphics.drawImage(getImage(jScrollPane1), (int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY(), this);
} else {
g2.drawRenderedImage(image, new AffineTransform());
}
return Printable.PAGE_EXISTS;
}
public boolean imageUpdate(java.awt.Image image, int flags, int param2,
int param3, int param4, int param5) {
return false;
}
}
The method in bold above is the only bit that isn't standard JDK code. There, we send our Printable class to be handled by a JFrame called PrintPreview, which is again straight from the book. To see it, click here. The class creates our preview frame and adds the screenshot to it. And that's it. Plain JDK code hooked into the NetBeans API PrintCookie class. The cool thing is that now the Print menu item works for your TopComponent, just like any other TopComponent or editor in the IDE, so the user experience is the same throughout, which would not be the case if you created some kind of Print button on your TopComponent. The aim, ultimately, is to blend in with the existing environment, which means using the Print menu item somehow, the above being one way of doing so.
Jul 17 2007, 02:44:11 AM PDT Permalink
Fun with Java in Munich
Toni Epple sent me a link to some pics from the NetBeans User Group meeting in Munich last week. Click here to see them. I found some more on my own camera, taken at the Gap bar in Munich afterwards. They're of some of the attendees chilling out and talking about... hmmm... I can't remember what about. But it was definitely fun, as can be seen here:
Above, sitting next to me is Toni Epple from GenoMatix, the creator of 'Peppy', the new Clippy that will take the NetBeans world by storm! Some of the others in the pic are his colleagues, while still others are new friends met during the evening at the user group meeting.
Jul 16 2007, 03:05:19 AM PDT Permalink
Kirk Pepperdine: Further Integrated into NetBeans IDE
I met with Toni Epple in a Munich beer garden this afternoon and we talked about my recent blog entry where I ported some of Kirk Pepperdine's code to the NetBeans Platform. Jokingly, I had entitled that blog entry 'Porting Kirk Pepperdine to the NetBeans Platform'. Toni suggested that further integration would entail a GlassPane, with Kirk appearing at unexpected moments to give random performance tips to the unwary NetBeans user. We came up with 'Peppy' as the name of this imaginary little wizard-like figure. Well, imaginary Peppy is no more, since I received this screenshot from Toni:
Cool, isn't it? Soon Toni will explain in his blog how he did it. But he needs to find his password first. Maybe Peppy can help him!
Update: Toni has now posted his instructions here. Plus, he explains why Peppy is called Peppy (and I thought it was just an amalgamation of Clippy and Pepperdine)!
Jul 14 2007, 10:52:02 AM PDT Permalink


