Saturday June 30, 2007
Visual Editor (Part 3)
I got slightly carried away while preparing for the third part of this series—as a result, I seem to have created a mostly complete JavaHelp Editor. Below, you see four buttons in the top of the visual view, one is for the XML source of the JavaHelp TOC file, while the other three are the names of the highest elements in the TOC hierarchy. When you click one of the buttons, you get a list of topics, but only those that relate (i.e., are below in the hierarchy) to the name in the button. In other words, the buttons are dynamically created, so maybe not a good idea (if you have 20 top level topics, you might end up having a problem because of buttons and spacing).
Within the body of the visual view, you see the outline of the help content. If a topic does not have a 'target' attribute set, the text field is outlined in red, because that means it is a 'bucket', containing help topics. If it is a help topic, the text field is outlined in blue. In that case, you can right-click, click a menu item, and then the topic itself opens in the IDE, so you can edit it right away. (This is made possible by parsing the Map file, via JAXB, and then comparing the target attribute in the Map file with the target attribute in the TOC file. By the way, I assume that the Map file is in the same folder as the TOC file, and that there is only one Map file in that folder, which I think are fair assumptions.) And what if the background is yellow? That signifies a broken link! In one glance, without needing to do anything at all, I can see all the broken links! That's about as useful as a JavaHelp Editor can be.

I learned quite a lot while making this JavaHelp Editor and I hope to continue with this series tomorrow, building on from yesterday, with the screenshot above as the target. In the meantime, I'll be sure to continue developing this tool. For example, a progress bar is needed, because the JAXB parsing sometimes take a little bit of time, especially the first time round. (Thus far, not more than 5 seconds. That time could be decreased if I focus on handling the JAXB parsing more efficiently. It definitely isn't a JAXB problem, just a coding issue.) Also, currently the link check is done for a tab whenever a tab is opened; maybe that should be deferred so that the user can choose when the link check should be done. And, as before, the synchronization hasn't been done yet, so that changes to the TOC source view do change the visual view, but not the other way round. Something else is that the visual view should be organized better—for example, the "Servers" text in the screenshot above should be in the top orange-backgrounded header, while what is there currently is superfluous, since it simply repeats the text in the button. Are there other things that could be added? I'd be glad to take suggestions.
Jun 30 2007, 12:27:05 PM PDT Permalink
Visual Editor (Part 2)
Let's continue exactly where we left off yesterday. Yesterday's end result was an absolutely minimal visual editor framework. Today we'll build on top of that, a little bit, so that we'll end up with a slightly more complex visual view on top of TOC XML files. At the end of this blog entry, when you open any TOC file (which must conform to the MIME resolver constraints that we set yesterday), a visual view will open on top of the XML source view. It will look like this:

Only the highest level TOC items will be exposed to the visual view at this point. And when we edit the text in the visual view, the underlying source won't be changed. That will come later. However, editing the source will result in the visual view being updated. To get to that point, we will mainly focus on working with the JAXB objects we generated yesterday. Via JAXB, we generated Java objects so that we can traverse the elements and attributes of XML files that conform to the JavaHelp DTD. Today, we'll see how to do that in the IDE. We'll use a code template that I discussed a few days ago, which was very recently introduced into the IDE, so you'll need a 6.0 development build from the last few days before continuing.
- In step C4 yesterday, we had a very simple dummy method skeleton for retrieving the Toc Java object. Here's what it looked like:
public Toc getToc() { return null; }Delete the body of the method and replace it with this line, to create a new instance of our object:
Toc toc = new Toc();
A red underline marking appears, because a Toc object is expected to be returned, which we haven't done yet. But, don't worry, we'll do so a bit later.
- Below the line above, type these letters and press Tab right after them:
jaxbu
Woohoo! You now have a whole JAXB unmarshalling code snippet! Note also that our 'toc' object has been inserted into the first line of the snippet. Your method should now look as follows:
public Toc getToc() { Toc toc = new Toc(); try { JAXBContext jaxbCtx = JAXBContext.newInstance(toc.getClass().getPackage().getName()); Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); toc = unmarshaller.unmarshal(new File("File path")); } catch (JAXBException ex) { // TODO handle exception Logger.getLogger("global").log(Level.SEVERE, null, ex); } } - Press Ctrl-Shift-I to generate the necessary import statements. One underline remains, for the "File path". Replace the new File instantiation with the following:
FileUtil.toFile(this.getPrimaryFile())
The above will extrapolate our file (which will be an XML file conforming to the JavaHelp DTD) from the data object. Let the IDE cast your Toc object correctly and add a return statement. You should now have this:
public Toc getToc() { Toc toc = new Toc(); try { JAXBContext jaxbCtx = JAXBContext.newInstance(toc.getClass().getPackage().getName()); Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); toc = (Toc) unmarshaller.unmarshal(FileUtil.toFile(this.getPrimaryFile())); } catch (JAXBException ex) { // TODO handle exception Logger.getLogger("global").log(Level.SEVERE, null, ex); } return toc; }Now that we have code to unmarshall our object, let's display something from the object in the visual panel. Okay? That's all we'll do. Nothing too scary.
- Open the TocToolBarMVElement.java file. Remember from yesterday, that this is the controller of our XML Multiview Editor API implementation. This class is where most of the action happens. Today we'll do the absolutely simplest thing that we can do under the circumstances.
In the TocToolBarMVElement class, find an inner class that you created yesterday, in step C7, called TocView. This class extends SectionView. The SectionView class determines the sections that will be displayed in the visual editor. There are three kinds of views that you can think about here:
- A brand new tab. Currently, we have a Design view and a Source view, each with a toggle button. You could add another panel, with its own toggle button. You could have as many as you like. This level is the highest in the hierarchy of panels in a visual editor. To create new panels, you need to work with the DesignMultiViewDesc class, which was mentioned in step C3 yesterday. It will come back (to haunt us) in this blog series, but not today. You will see that we can even generate those tabs, with those toggle buttons, on the fly. In other words, they don't all need to be pre-defined. But we'll find out more about that later.
- A section container. Section containers... contain sections. They have a small plus/minus icon, for expanding and collapsing the sections within them. To create section containers, you need to work with the SectionContainer class. Sections are added to this class via the SectionContainer.add method. Section containers, in turn, are added to section views via the SectionView.add method.
- A section panel. Lowest in the hierachy, the section is represented by a single JPanel or TopComponent. In this context, a JPanel is probably simplest. That's what we'll use here.
- First, let's look at what we're going to display in the visual view. Call up code completion for one of the instances of your toc object. For example, you'll find one already in the TocToolBarMVElement class, within the inner TocView class. You'll see code completion, i.e., the methods that the JAXB objects make available to you:

Let's work with the getTocitem() method. That will give us a list of TOC items. For now, we'll simply display the highest level of TOC items in the visual view. (Later, in future blog entries, we'll retrieve lower-level TOC items from the higher-level TOC items.)
- Type this line in the TocView class, right below the Toc object's instantiation:
List
list = toc.getTocitem(); You will now need to let the IDE create an import statement for the Tocitem object, as well as for java.util.List.
- Now we'll iterate through the list:
List<Tocitem> list = toc.getTocitem(); for (int i = 0; i < list.size(); i++) { Tocitem item = list.get(i); item.getText(); }At the end of each iteration, we have a value retrieved from the text element, which sets the TOC item's name. So, we now have the highest level names of our XML file! Hurray! Let's try it out immediately. Set a new dependency, on UI Utilities. That will let us use the NetBeans API StatusDisplayer object, to display our values in the status bar. Build a Stringbuilder and print it to the status bar, like this:
StringBuilder sb = new StringBuilder(); List<Tocitem> list = toc.getTocitem(); for (int i = 0; i < list.size(); i++) { Tocitem item = list.get(i); item.getText(); sb.append("[ " + item.getText() + " ] "); } StatusDisplayer.getDefault().setStatusText(sb.toString());Now install the module and open some random TOC file. Depending on what's in there, your status bar should now display the top level topics, as shown here:

Congratulations. Now all we need to do is display the above items in the visual view. You can now actually comment out the code that we've created in this step. We simply put the code here to test out our object. Now that we know that we can correctly retrieve the values, we can continue with our little procedure. Later we'll find the above snippet useful, but for now, to avoid confusion, just comment it out. So, right now, the TocView class looks as follows:
private class TocView extends SectionView { TocView(TocDataObject dObj) { super(factory); Toc toc = dObj.getToc(); // StringBuilder sb = new StringBuilder(); // List<Tocitem> list = toc.getTocitem(); // for (int i = 0; i < list.size(); i++) { // Tocitem item = list.get(i); // item.getText(); // sb.append("[ " + item.getText() + " ] "); // } // StatusDisplayer.getDefault().setStatusText(sb.toString()); Node itemNode = new ItemNode(toc); setRoot(itemNode); } } - Below the itemNode, still within the TocView class above, i.e., the constructor of the inner TocView class, add the following two lines, which will create the new section and add it to the section view:
SectionPanel topLevel = new SectionPanel(this, itemNode, toc); add(topLevel);
Because of the infrastructure we set up yesterday, there's nothing more that you need to do right now. Just install the module and open a TOC file. You should now see an empty visual view, apart from the name of the file, which comes from the ItemNode, defined at the end of our TocToolBarMVElement class. This is what you should now see:

- Now we'll put the top level TOC values in the above visual view. Open TocPanel.java. This class extends SectionInnerPanel. Here, we will copy the snippet of code that we worked with in step 7. However, we have one problem. We currently do not have access to the Toc object. We need to receive it from the PanelFactory, which will instantiate the TocPanel. The PanelFactory needs to receive the Toc object from the ItemNode. And the ItemNode receives it from the SectionView. Now do you understand why this API is complex?
So now, in TocToolBarMVElement, pass the Toc object to the ItemNode. This is already done, in step C7 from yesterday. Now go to the PanelFactory and change the createInnerPanel method, so that the Toc object will be sent to the TocPanel. The only change from yesterday is the bit in bold below:
public SectionInnerPanel createInnerPanel(Object key) { return new TocPanel((SectionView)editor.getContentView(), (Toc)key); }Now you can let the TocPanel receive the Toc object in the TocPanel constructor. Copy over the snippet of code from earlier and put it in the constructor. That's a good starting point. Remove the StringBuilder and StatusDisplayer pieces.
Now... we will generate a text field for each value retrieved from the Toc object. And then we'll add those to the TocPanel.
- I don't know any other way of generating Swing components than to use GridBagLayout. I'm sure that's a result of my own ignorance and I am hoping someone will prove that to be true. Remember that we need the text fields to be generated one after the other, below each other. Only with GridBagLayout have I managed to do this, but there must be other ways and ideally this could somehow be done visually via the Matisse GUI Builder.
Nonetheless, in the meantime, change the JPanel layout to GridBagLayout. Just use the Set Layout menu item that appears when you right-click in the Design view. And then add this code, which positions new text fields correctly and fills in the text from the retrieved top level TOC item:
public TocPanel(SectionView view, Toc toc) { super(view); initComponents(); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(2, 5, 1, 1); // spacer gbc.weightx = 1.0; // allow horiz dispersion gbc.anchor = GridBagConstraints.WEST; // align left (requires ^) gbc.gridwidth = GridBagConstraints.REMAINDER; // one component per row JTextField textField = new JTextField(); List<Tocitem> list = toc.getTocitem(); for (int i = 0; i < list.size(); i++) { Tocitem item = list.get(i); String oneItem = item.getText(); textField = new JTextField(); add(textField, gbc); textField.setText(oneItem); } }Now all this wasn't so hard after all, was it. Just install the module again and... you'll have as many top level items in the visual view as there are in the underlying TOC file. Here, I have three:

Remember: The panel's layout must be set to GridBagLayout, as mentioned in the 2nd paragraph of step 10.
Now, the question is: "Does it work?" In other words, when we change something in the visual view, will the change be reflected in the source view? No. Not yet. We need to implement the ModelSynchronizer class for this, which will come in a future blog entry. However, simply by tweaking the data object in one significant place, you can get the other way to work. In other words, when you make changes in the source view, and if you then save those changes, the design view will immediately be updated. To implement this, go to your data object and make sure that the createNodeDelegate method looks as follows:
protected Node createNodeDelegate() {
return new TocDataNode(this);
}
By default, the above method gets the Lookup from the node, which we don't want, because the visual view has its own Lookup. That conflict of Lookups causes problems. So tweak the above method so that it looks as the above, and you will have one-way editing in your visual view. In particular, Vadiraj should be happy with this news, because he had a significant problem in this area and I hope this fixes it. And note that two way editing will come later... once I have it figured out myself.
Jun 29 2007, 02:38:27 PM PDT Permalink
Visual Editor (Part 1)
This is the first of what promises to be a multi-part draft tutorial, at the end of which you will (hopefully) have a working multiview editor for XML files. I say 'hopefully', because I haven't actually got to a successful end point yet, myself. So, potentially, this tutorial will come to an abrupt end three weeks from now, when I throw my hands in the air and say 'I give up!'. But I doubt that. Somehow or another everything should come together somewhere along the line. At the end of this first part, you will simply have a two-pane editor for JavaHelp TOC (i.e., 'table of contents') files, with a source view and an empty visual view:


You will also have generated Java objects from the DTD that defines the TOC. You will do this so that we can access the XML elements and attributes in the TOC file and build a visual representation for them in a later installment of this tutorial. The Java objects will be generated from JAXB, using the new JAXB Wizard, which you can find in some of the recent development builds for 6.0. In this installment, we will not touch JAXB at all in the code, we will simply use the JAXB wizard in the preparatory phase, add them to our module, and then build the basic visual editor framework that you can see in the screenshots above. In the next part, hopefully tomorrow, we'll begin dealing with the JAXB objects.
If this tutorial reaches a successful conclusion, it will (once the XML Multiview API is stabilized) become one of the official NetBeans Platform tutorials. If you (like me) want to see this API stabilized (i.e., currently it is a 'friend' API, which means that NetBeans does not officially support it and no javadoc is currently available for this API), please add your comments to issue 107858 and/or add your vote to that issue.
A. Preparatory Steps
The purpose of this section is to create JAXB objects and put them in a new module project, where we will create our visual editor.
- Create a new Java application and name it 'JAXBWizardOutput'.
- If you already have a DTD or Schema that you want to create an editor for, you can skip this step. If not, follow this step to pick out a DTD or Schema from the NetBeans repository. Create a new empty DTD file, from the XML category in the New File wizard. Name it 'InputDTD'. Go to Tools > DTDs and Schemas.

Pick out some DTD or Schema that you want to provide support for. Click the 'Open in Editor' button. Copy that content into your empty DTD file. (Ctr-A, Ctrl-C to copy all, then Ctrl-A, Ctrl-V to paste all.) Now you have a DTD or Schema that you can use in the next step.
Alternatively, assuming you want to follow along exactly with these instructions and create a visual editor for JavaHelp TOC files, just download the DTD here: http://java.sun.com/products/javahelp/toc_2_0.dtd
- Go to the New File wizard. In the New File wizard, find 'JAXB Binding' in the 'XML' category. Click Next. You are now in the JAXB Wizard. Use the JAXB Wizard to generate Java objects from the DTD or Schema:

- Create a new module project. Call it 'CoolMultiEditor'. Add a package called 'model' and copy the generated classes into it. This is where you can find them in the Java application, note that they are in the 'build' folder, which is only visible in the Files window:

- Set a dependency on the JAX-WS 2.1 module. This is a non-API module. It requires that you set an 'implementation dependency', which is described below in the context of the XML Multiview API. Once you have set this dependency, the error messages in the JAXB objects that you copied into the 'model' package will disappear, because the JAX-WS 2.1 module contains the JAXB packages that the generated JAXB objects depend on.
The purpose of this section is to let the IDE recognize XML files that conform to our selected DTD or Schema. We use the New File Type wizard for this purpose, which will also generate code for opening our file in the NetBeans editor. In effect, we will be creating the source view for our XML file in this section.
- Use the New File Type wizard to recognize XML files with the name space supported by the DTD or Schema you selected in step 1. In this panel, make sure that the MIME type ends in "+xml" and then paste the public id into the 'by XML Root Element' field:

Now complete the wizard. I set 'Toc' as the class name prefix, with the result that my data object ended up being called 'TocDataObject'. I also have classes called 'TocDataLoader' and 'TocDataNode'.
- After you complete the wizard, you need to tweak the MIME type resolver (TocResolver.xml) so that the public id of your DTD or Schema is used to recognize the file type:
<doctype public-id="-//Sun Microsystems Inc.//DTD JavaHelp TOC Version 2.0//EN"/>

Alternatively, you could use some other approach, such as using the root element's name. But the above is safer, because the root element is not necessarily unique, while the public id is undoubtedly a unique identifier for an XML file. (I mean, that's the whole point of a public id.)
- Install the module. Look in the Projects window, Files window, or Favorites window, for a file with that public id and notice the new icon.
- Double-click it and it opens in the NetBeans editor as an XML file.
We now have a source view. Let's use the XML Multiview Editor API to create a design view.
C. Creating the Design View
The purpose of this section is to create the simplest imaginable design view (i.e., the empty one shown at the start of this blog entry) for the XML file that we're dealing with, i.e., for JavaHelp TOC files.
- Dependencies. Set dependencies on both MultiView APIs:

Note: The XML Multiview Editor API doesn't always appear in the list above the first time round. Sometimes, I need to close the above dialog box, and the one below that, and then reopen them and switch the checkbox on and off, and do various other random things before it shows up.
Make sure that you set an 'implementation dependency' on the XML Multiview Editor API, otherwise you will not be able to refer to it in your code. You will get a compile error about the API not being a friend of your module. So, for the XML Multiview Editor, click Edit in the Libraries panel (in the Project Properties dialog box) and simply click 'Implementation Version', as shown here:

Note: You should have done the same earlier, for the JAX-WS 2.1 module.
- XmlMultiViewDataObject. The very first step, before any other, in implementing this API, is to change the data object so that it extends XmlMultiViewDataObject. Do so by typing it in the data object class, as shown here:

Now you will need to fix imports (Ctrl-Shift-I). Then click on the lightbulb and let the IDE generate skeleton abstract methods that you will need to implement to conform to the API.
You now have this in the data object definition:
public class TocDataObject extends XmlMultiViewDataObject { public TocDataObject(FileObject pf, TocDataLoader loader) throws DataObjectExistsException, IOException { super(pf, loader); CookieSet cookies = getCookieSet(); cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies)); } protected Node createNodeDelegate() { return new TocDataNode(this, getLookup()); } public @Override Lookup getLookup() { return getCookieSet().getLookup(); } protected DesignMultiViewDesc[] getMultiViewDesc() { throw new UnsupportedOperationException("Not supported yet."); } protected String getPrefixMark() { throw new UnsupportedOperationException("Not supported yet."); } }Above, the only really interesting method is the one in bold. It returns an instance of DesignMultiViewDesc, which is comparable to Interface MultiViewDescription in the MultiView Windows API. It simply describes the design view. We will fill it out in the next step.
- DesignMultiViewDesc. Let's describe our design view and return it in the getMultiViewDesc() method, discussed in the previous step. We need a description for each design view. In this case, (at least, at this stage), we only have one design view, but multiple design views could be returned. Each design view is a new tab in the editor. Have a look in the web.xml editor, for an example of an editor with multiple design views. Here we return our design view:
protected DesignMultiViewDesc[] getMultiViewDesc() { return new DesignMultiViewDesc[]{new DesignView(this, TYPE_TOOLBAR)}; }Here we declare the variable, representing the type of design view, which will be discussed in some future installment of this series:
private static final int TYPE_TOOLBAR = 0;
And now let's define our first design view:
private static class DesignView extends DesignMultiViewDesc { private int type; DesignView(TocDataObject dObj, int type) { super(dObj, "Design"); this.type = type; } public org.netbeans.core.spi.multiview.MultiViewElement createElement() { TocDataObject dObj = (TocDataObject) getDataObject(); return new TocToolBarMVElement(dObj); } public java.awt.Image getIcon() { return org.openide.util.Utilities.loadImage("org/netbeans/modules/coolmultieditor/icon-16.png"); //NOI18N } public String preferredID() { return "Toc_multiview_design" + String.valueOf(type); } }Above, the interesting method is in bold. The createElement() method does all the work in this class. It returns an instance of the the standard Multiview API's MultiViewElement interface, which is why we needed a dependency on that API. Here we return an instance of ToolBarMultiViewElement, which inherits from MultiViewElement. This class is the heart of this API. Once you come to terms with this class, everything starts making some sense. One can see this class as the controller of your implementation of the API. It instantiates a factory that generates panels. Each panel is generated in an inner class that extends SectionView. In step 7 below, we have a dummy implementation of this class. Ultimately, all the complexity of this API will be implemented in this dummy implementation.
- JAXB. First, in your data object, add this method, which gives you a connection to one of your JAXB objects:
public Toc getToc() { return null; }This will, later, return one of our JAXB objects. The TOC object that is returned here comes from the 'model' package that you defined earlier, so you should end up with this new import statement:
import org.netbeans.modules.coolmultieditor.model.Toc;
- InnerPanelFactory. Let's also create our panel factory. It will generate new panels, depending on the type of object that you want the panel to represent. (Multiple panels could appear within the same design view, as we will discover later.) So create a new class called PanelFactory.java and let it extend InnerPanelFactory. Here it is, the simplest implementation imaginable:
public class PanelFactory implements InnerPanelFactory { private TocDataObject dObj; private ToolBarDesignEditor editor; PanelFactory(ToolBarDesignEditor editor, TocDataObject dObj) { this.dObj=dObj; this.editor=editor; } public SectionInnerPanel createInnerPanel(Object key) { return new TocPanel((SectionView)editor.getContentView()); } }Above, the createInnerPanel method, which is in bold, will generate new panels in the visual view, depending on what kind of object is sent here.
- SectionInnerPanel. Now we will create the TocPanel, referred to in the previous class. Create a new JPanel class called TocPanel.java. Later, we will be able to use the 'Matisse' GUI Builder to design our visual view. Change its extension class from javax.swing.JPanel to SectionInnerPanel. You will need to create some dummy implementations of the methods setValue, linkButtonPressed, and getErrorComponent. Also, the constructor should be as follows:
public TocPanel(SectionView view) { super(view); initComponents(); } - ToolBarMultiViewElement. We now have all the pieces that the controller, i.e., an implementation of ToolBarMultiViewElement, needs. Here is a minimal implementation of this class, which gathers up the panel factory and the panel that we created in the previous two steps, using them to generate the visual view that you see in the introduction of this blog entry. The class below will be discussed in a lot of detail in future installments. Just take it on trust for now:
class TocToolBarMVElement extends ToolBarMultiViewElement { private TocDataObject dObj; private SectionView view; private ToolBarDesignEditor comp; private PanelFactory factory; public TocToolBarMVElement(TocDataObject dObj) { super(dObj); this.dObj = dObj; comp = new ToolBarDesignEditor(); factory = new PanelFactory(comp, dObj); setVisualEditor(comp); } public SectionView getSectionView() { return view; } public void componentShowing() { super.componentShowing(); view = new TocView(dObj); comp.setContentView(view); view.open(); } private class TocView extends SectionView { TocView(TocDataObject dObj) { super(factory); Toc toc = dObj.getToc(); Node itemNode = new ItemNode(toc); setRoot(itemNode); } } private class ItemNode extends org.openide.nodes.AbstractNode { ItemNode(Toc toc) { super(org.openide.nodes.Children.LEAF); setDisplayName(dObj.getPrimaryFile().getNameExt()); } } }
Hurray. That's it. You're done. You have now created the framework for a visual editor. The Projects window should look something like this:

When you install the module, you should see exactly what is shown at the start of this blog entry. Future installments will continue from this point, using the generated JAXB objects to present a visual view on top of XML files that define the TOC in a JavaHelp system.
In other news. Check out Wade Chandler's cool new article called NetBeans: Introductions to the Open-Source Project, More Than an IDE on http://www.developer.com. It is the first of a (hopefully long) series. Congratulations, Wade!
Jun 28 2007, 06:05:39 AM PDT Permalink
Code Template Registration
In the last day or so, Girish Patil, the engineer responsible for the (brand new!) JAXB tooling in NetBeans IDE 6.0, integrated some handy code templates. If you type 'jaxbm' in the editor, and you then press Tab to expand the abbreviation, a snippet of JAXB marshalling code is generated, with some of the variables automatically filled in, based on the code available around the invocation of the template. Similarly, when you type 'jaxbu', you'll get a snippet of JAXB unmarshalling code.
So, apart from playing with that a bit and adding it to a (brand new!) tutorial that will describe all of this, I asked him how he registered these code templates. That's something I've never done. It turns out that you simply need to create an XML file that conforms to the EditorCodeTemplates-1_0.dtd. Then, drop that in the MIME type's layer folder in Editors/MIME-type/CodeTemplates. And then you're done! Here you see Girish's XML file and also where he put it in the Java MIME type's CodeTemplates folder:

Wow, that's easy. Now I can create others, stick them in a module, and distribute them to whoever wants them. In particular, registering code templates is a small yet powerful thing in support of some other larger feature, such as the JAXB tooling that Girish is working on. So... let's now all go out there and create some cool new code templates!
Jun 27 2007, 01:25:01 PM PDT Permalink
Validation for Visual Editors
Made some progress but still quite a long way to go. Below you see an XML file that conforms to the Palette Editor Item DTD, which is the DTD that defines the XML files that are used to display items in the palette. For example, the HTML code snippets in NetBeans IDE are defined by means of this XML file. Of course, a visual view for this XML file is completely superfluous, because one would never edit these by hand. A developer would let the user perform actions to generate these, without the user even knowing that this is happening. The user would never have any direct interaction with this XML file. However, it's interesting for purposes of playing with the XML Multiview API. Here's the current status of the editor:
One interesting thing is that this API comes with its own validation framework. The "WARNING" dialog box that you see above was not created by me. Instead, the API comes with a listener that you can set on a text component and then, whenever a change is made to the text component, the API's documentChanged() method is called, which returns an error message (that can be seen in the bottom left corner above), as well as the WARNING dialog box. Here's an example of this documentChanged() method, filled out for the first text field above:
public void documentChanged(javax.swing.text.JTextComponent comp, String value) {
if (comp == jTextField1) {
String val = value;
if (val.length() == 0) {
getSectionView().getErrorPanel().setError(new Error(Error.MISSING_VALUE_MESSAGE, "class", comp));
return;
}
getSectionView().getErrorPanel().clearError();
}
}
However, since this API isn't 'stable', it isn't officially supported, and I couldn't even find official Javadoc for it. I'm hoping we'll either accept this API, even though it is complex, or create a new simple one. Either way, many developers want an API that provides a visual editor such as the above and the current complexity of the API shouldn't result in developers not being able to use it at all.
I still have various problems with the editor. Although, thanks to Vadiraj, the visual part works better than before, it doesn't work correctly yet. The source view correctly updates the visual view. But when the visual view changes, weird things happen in the source view, such as the fact that the entire DOCTYPE declaration disappears! And so do the comments near the top of the XML file. And only sometimes is the source correctly updated. On the other hand, the "Save" button is always correctly enabled, whenever I make a change to the visual side (as well as to the source). But these weirdnesses just indicate that I have some more work to do.
Jun 26 2007, 11:59:24 AM PDT Permalink
JAXB and Visual Editors in 6.0
[Read More]
Jun 25 2007, 05:13:50 PM PDT
Permalink
Integrating the XML Editor for Your File Type (Part 2)
I discovered I was wrong yesterday. Instead of replacing MultiDataObject with XMLDataObject, you must simply make sure that the MIME type ends in "+xml". For example, something like text/x-foo+xml. Once you've done that, the document will open in the XML Editor. In other words, you'll have syntax coloring for free, simply by ensuring that the MIME type is correct. Adding the cookies described in step 4 of yesterday's blog entry will give you the XML functionality for checking XML, validating XML, and transforming it via XSLT. And all that, despite the fact that the file type doesn't end in ".xml".
Finally, let's add a navigator, so that we can get an outline of our XML file and so that we can jump from a node in the Navigator to the related line in the XML Editor. Here's how you add the XML Navigator:
- Make very sure that your MIME type is in the form text/x-foo+xml, otherwise the XML Navigator will not work. Instead, when you click on a node, a new document will open, and the new document will not have syntax coloring, in other words, the content of your document will open in the plain text editor. Look in (at least) three places to make sure that your MIME type is correct—the MIME type resolver, the layer file, and the data loader.
- In the Important Files node, expand XML Layer and then expand "this layer in context". Go to the Navigator folder, create a new folder within the Navigator folder. The name of the folder must be the same as your MIME type. Then copy the content of the XML folder into your own folder. Congrats, you've just made the XML Navigator available to your own MIME type.
- Install the module. If you're installing in the development IDE, you may need to restart the IDE for your registration of the XML Navigator to take effect.
And now, when the document is in focus, the XML Navigator presents an outline of the document. In addition, you can navigate from the XML Navigator to anywhere you want within the document, as shown here:

Jun 24 2007, 01:03:10 AM PDT Permalink
Integrating the XML Editor for Your File Type (Part 1)
Thanks to help from Vadiraj, I now know how to open non-XML files in the XML editor. Sometimes you have XML content in a file that does not end in ".xml". How do you let the IDE treat it like an XML file? Below, you see a file with the extension ".test" open in the XML editor. You know it is open there because of the syntax coloring and indentation, and because of the additional menu items that relate specifically to XML files:

How to do this:
- Use New File Type wizard, which will give you various classes. The only class we will need to change is the XxxDataObject.
- Add a dependency on XML Tools API.
- In the data object, change the extension class from MultiDataObject to XMLDataObject.
- Fill out the constructor with additional functionality by providing cookies for checking XML, validating XML, and transforming XML via XSLT, like this:
public TestDataObject(FileObject pf, TestDataLoader loader) throws DataObjectExistsException, IOException { super(pf, loader); CookieSet cookies = getCookieSet(); InputSource is = DataObjectAdapters.inputSource(this); Source source = DataObjectAdapters.source(this); cookies.add(new CheckXMLSupport(is)); cookies.add(new ValidateXMLSupport(is)); cookies.add(new TransformableSupport(source)); cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies)); } - Make sure the import statements are correct:
import java.io.IOException; import javax.xml.transform.Source; import org.netbeans.spi.xml.cookies.CheckXMLSupport; import org.netbeans.spi.xml.cookies.DataObjectAdapters; import org.netbeans.spi.xml.cookies.TransformableSupport; import org.netbeans.spi.xml.cookies.ValidateXMLSupport; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObjectExistsException; import org.openide.loaders.XMLDataObject; import org.openide.nodes.CookieSet; import org.openide.nodes.Node; import org.openide.text.DataEditorSupport; import org.xml.sax.InputSource;
That's it. You're done. Your file will now be treated as an XML file, like all other XML files.
Jun 23 2007, 01:03:00 AM PDT Permalink
Elvis, Einstein, and Advanced NetBeans API Wizards
Just like Elvis and Einstein were 'best of breed', so the enhanced NetBeans API wizards that I've blogged about in the past few days are in a class of their own. 'Best of breed', if you will. So, with that unlikely comparison freshly introduced to your mind, click the link below and you'll find yourself in the NetBeans Plugin Portal page dedicated to the Elvis/Einstein of the NetBeans API wizard world:
http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=2880
Click the giant "Download" button on the above Plugin Portal page, install the NBM file in a very (very) recent 6.0 development build and then you'll find... Elvis and Einstein have infiltrated your New Project wizard:
(For the visually challenged, the two new icons you see above in the Samples category are of Elvis and Einstein.)
When you complete the wizard for both these samples, you'll find that you have the sources for the two new NetBeans API wizards:
Because I set an implementation dependency on the apisupport/project module, which is a sin, but not unforgivable in this case, it was possible to extract the basis of these wizards from the apisupport/project sources, which I then enhanced, as described over the last few days. You can now, since you're a user of the NetBeans APIs (otherwise you'll have lost interest in this topic by this stage of this blog entry) study these sources and learn how NetBeans API wizards are constructed. You'll find, as I did, that these wizards are very easy to create. Also, you can install the modules, with the result that you have two new wizards available for NetBeans module development, as you can see here:
It is important to note that these two new enhanced wizards must be used in combination. The enhanced New Window Component wizard creates, among many other things, a DataObject that should replace the DataObject that you get from the enhanced New File Type wizard. The enhanced New File Type wizard lets you generate an OpenSupport class, which presupposes that you have a TopComponent, which you can get from the enhanced (or standard) New Window Component wizard. Hence, a little bit (a very little bit) of tweaking is needed at the end of using the two wizards. But the result is... as discussed yesterday... that you get a pretty advanced editor infrastructure for your file type. Hurray. Thanks Elvis and Einstein.
Jun 19 2007, 08:11:32 AM PDT Permalink
Extending the NetBeans API Wizards (Part 3)
After some further 'enrichments' of the New Window Component wizard and the New File Type wizard, I am able to generate all the code needed for creating the following editor infrastructure:


So this is a Multiview Editor, with a Visual Library implementation in the Visual view and a palette in the Source view. To generate the code needed for this infrastructure, I needed to set the following options, first in the New Window component wizard:

And then in the New File Type wizard:

After completing these two wizards, my module contained all the classes below (as well as many new entries in the layer.xml and project.xml files) and I needed to perform no extra steps, no tweaking of the code, nothing at all:

The Multiview Editor that is created is not an implementation of the XML MultiView Editor API. It uses the 'old' Multiview Editor API. For XML files, the XML MultiView Editor API should be included in these wizards, with additional panels for analyzing the XML and deriving editor sections, followed by design choices and a visual library implementation for laying out the visual view... those are some of the areas to work on when extending this scenario to create a complete XML Visual Editor Generator.
Jun 18 2007, 12:36:39 AM PDT Permalink
Extending the NetBeans API Wizards (Part 2)
Similar to yesterday's enhancements of the New Window Component wizard, I changed the "File Recognition" panel in the New File Type wizard to a "Features" panel. Now you can let the wizard create an editor palette or an OpenSupport implementation class, in addition to the file recognition code for which this wizard was originally intended:

By default, both the new checkboxes are unselected so that by default you get exactly the same code as before. Both cannot be selected at the same time, because the palette that is created is currently specifically for editors (e.g., you get an Editor Palette Item XML file, with the sample code snippet above as the body that will be added to the editor when you drop the item), while the OpenSupport implementation assumes that you'll be creating a TopComponent at some point. If you choose to let the wizard create a palette, then you'll get a PaletteFactory.java class, an XML file that conforms to the Editor Palette Item DTD, various entries in the layer.xml file, a new dependency in project.xml, and two images (one 16x16 pixels and the other 32x32 pixels) that can represent the item in the palette. As soon as the wizard is complete, you can install the module and then your new file type will have a new palette, with one item, so that you (as the palette developer) will be able to see how to create other items for your palette. if you selected "Open into TopComponent", you'll need to create a TopComponent at some stage, and then tweak the generated OpenSupport implementation slightly, but those bits are already in the generated code, just stubbed out, with lots of explanatory comments. I always find the OpenSupport class, despite the fact that you only need a very few lines, quite tricky to implement. There are lots of little things that can go wrong and the code is a bit dense. Now, with this wizard, even though I need to tweak things afterwards (because the TopComponent is created separately), I at least have all the information I need (i.e., via comments and code snippets) right inside my module. One cool thing I've done so far with these enhancements is... I generated an OpenSupport implementation and then used yesterday's enhancement to generate a TopComponent with the "Enable Visual Library" checkbox selected! As a result, now I can open a file into a Visual Library scene, without having done almost any coding at all!
It really isn't hard to make these kinds of changes to the NetBeans API wizards. First, as stated yesterday, check out apisupport/project. (Note that this is different to apisupport. Make sure you check out apisupport/project, which is a module in itself.) Then find the wizard you want to change. For example, the loader package defines the New File Type wizard. Here's what the package looks like, at least, here's what it looks like for me, since I've made several changes:

Just create a template for the class that you want to have generated, then look in the iterator class for how other templates are created. And there are a lot of really powerful utility methods that you can call. All you need to know are the parameters to send to the utility method, which isn't hard to work out if you look at the rest of the iterator code. And the utility methods are really clearly named, so you know what to do with them. Have a look at them here:

Not bad, right? Methods for adding dependencies, entries in the layer.xml file, attributes, bundle settings, and so on. Really cool. An entire framework for NetBeans API wizards. So, even though it might take a bit of work, at least initially, to enhance these wizards, once you've done so you'll never need to create boilerplate code again. After all, the basis of every editor palette is the same, the basis of every OpenSupport implementation is the same. And there are many such scenarios that are identical for each module. And how many times have you had to create those by hand? Seems like a lot of unnecessary work. Invest a little bit of time in enhancing the apisupport/project module for your environment by letting the wizards generate your most commonly typed code and then you'll be able to use them to generate more and more of the code you need from that point on. After all, people using these wizards are NetBeans API implementors anyway, so it's not such a stretch to suggest that those who want to use the NetBeans APIs can hone their skills by extending the existing code generators (and adding new ones) for their specific subset of boilerplate code. Hence, my suggestion is, whenever you find yourself typing the same lines of code over and over again, you need to wonder whether you shouldn't try to enhance one of the wizards to do that work for you. Yes, you'll have a customized version of the apisupport/project module, which means that when a new release of that module comes out with NetBeans IDE, you'll need to manually upgrade or do something if you want to make use of new enhancements in the official release, but that's life. Every advantage has a disadvantage.
Jun 17 2007, 12:36:33 AM PDT Permalink
Extending the NetBeans API Wizards (Part 1)
Discovered a very cool thing—it is extremely easy to add functionality to the NetBeans API wizards. Here's one example. I added an "Enable Visual Library" checkbox to the New Window Component wizard:

When that checkbox is selected, the Visual Library API is declared in the project.xml file, a simple GraphScene implementation class is added to the generated files, and the TopComponent constructor is extended to create the scene (together with a satelitte view). As soon as the wizard is complete, the module can be installed (i.e., nothing extra needs to be done) and one immediately has a simple implementation of a scene, as can be seen here:

How to do this (and other similar changes)? Check out the apisupport/project module from the NetBeans sources. Then find the wizard you want to change. Then... this is the cool bit... you suddenly realize that an entire framework has been set up for you. For example, don't know how to programmatically add dependencies to the project.xml file? It really is simple:
fileChanges.add(fileChanges.addModuleDependency("org.netbeans.api.visual"));
The above would add the Visual Library as a dependency in the project.xml file. How does that work? I don't know. I don't care. I don't need to know or care. I just looked in the iterator, found similar entries for other dependencies, and then just added my own. Similar utility methods exist for adding entries to the layer.xml file and all other supporting resources. Just look through the iterator, find something similar to what you want to do, and then just add your little piece of code.
Of course, the big problem is distribution. Once I've changed the sources in this way, if I want to share this changed wizard with someone else, I need to change the version of the apisupport/project module and then make that incremented version available to others. Since the module is not limited to the file-level wizards, but also the project-level wizards, and everything else, that's a lot to distribute just for a change to a file-level wizard. One other cool thing is that the apisupport/project module is self-contained. It doesn't depend on any other modules in the NetBeans sources. So distributing it as a single NBM file is possible. But, aside from these concerns relating to distribution, I've definitely found a very useful area to explore. Adding new wizards is easy too. Just copy some other wizard, rewrite the (very readable and well structured) code and then bob's your auntie.
Jun 16 2007, 12:42:50 AM PDT Permalink
Drag and Drop Tables into Swing Containers
Since issue 91711, you can drag a table from the Services (previously called Runtime) window into a JTable in a container in the 'Matisse' GUI Builder. I didn't know about this until today. Though this has always been possible in Creator/Visual Web Pack, it hasn't been for the GUI Builder. I've also been able to drag a field from the Services window into a JTextField in a container and I guess other combinations must be possible too.
For example, once you have a container with a JTable, as shown below, you can simply drag a table from the Services window, drop it on the JTable, and then the JTable will be populated with the corresponding values, once you run the application.
That's going to make GUI design (and mock ups) even faster to throw together than before!
Jun 15 2007, 08:21:02 AM PDT Permalink
NetBeans Keyboard Shortcut of the Week (7)
The last "NetBeans KeyBoard Shortcut of the Week" was on November 28 last year, so it is probably time for another one! And the winner is... Ctrl+Q, which is new in the upcoming NetBeans IDE 6.0. One of the many extremely useful new features, this shortcut opens the document in which you last changed something and puts the cursor at the specific place where the change took place. Handily, it has a toolbar button to speed this up even further:

I've found this very useful so far and use it all the time, especially when 10 or more documents are open at the same time and I've lost the plot as to where I was last working... In honor of this 'weekly' award, the Ctrl+Q keyboard shortcut has been given pride of place in the left sidebar of this blog, where it will stay for (at least) one week.
Jun 14 2007, 10:51:08 AM PDT Permalink
Tip of the Day Functionality for NetBeans Platform Applications
Thanks to R.J. Lorimer's excellent Javalobby article on the SwingLabs project's JXTipOfTheDay (click here to read it), I've created a simple module that provides this functionality for NetBeans IDE:

The interesting bit is that the tips are defined in the application's user directory, in a properties file, which means that you can change the tips whenever you feel like it. So you're not locked into the tips provided by the module. Also, thanks to this snippet in the layer.xml file...
<folder name="Favorites">
<file name="tipsproperties.shadow">
<attr name="originalFile" stringvalue="Preferences/tips.properties"/>
<attr name="originalFileSystem" stringvalue="SystemFileSystem"/>
</file>
</folder>
...the tips properties file is automatically available in the Favorites window, so that you can use the Properties Editor to add/modify the tips:

I've tried the same module in other applications on the NetBeans Platform and, as one might expect, it works there too. I'd make the module available on the Plugin Portal, but I don't know (and can't find) the SwingLabs licensing conditions. By the way, because of usage of the NbPreferences API, the module only works from 6.0 onwards.
But, considering the fact that there are now several new/changed keyboard shortcuts, because of the many 6.0 improvements, isn't this a cool way to learn them, one by one? Whenever the IDE starts, you see a new tip. You can also call up the dialog from a menu item, so that you can scroll through all the available tips whenever you want them. And, as pointed out above, you can delete all the tips, if you want, and then just add your own!
Jun 13 2007, 06:52:43 AM PDT Permalink


