X

Geertjan's Blog

  • June 28, 2007

Visual Editor (Part 1)

Geertjan Wielenga
Product Manager
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.

  1. Create a new Java application and name it 'JAXBWizardOutput'.

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

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

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

  5. 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.

B. Creating the Source View

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.

  1. 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'.

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

  3. 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.

  4. 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.

  1. 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.

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

  3. 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.

  4. 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;

  5. 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.

  6. 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();
    }

  7. 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!

Join the discussion

Comments ( 15 )
  • sunny jain Thursday, August 2, 2007
    Hi
    I is their a way to directly add multi view xml editor to any panel.
  • Geertjan Thursday, August 2, 2007
    Yes.
  • sunny jain Thursday, August 2, 2007
    But without using all those DataObjects.
    As i want to manage the synch my self.
    Steps required:
    1.Add XML multi view Top component.
    2.Facility to add dtd validation.
    3.Object of other sub panels.
  • Parth Saturday, August 11, 2007

    Hi Geertjan

    From where i can get the source of this tutorial.


  • Geertjan Saturday, August 11, 2007

    Nowhere, Parth. Just follow the steps and then you'll create the sources yourself.


  • Parth Saturday, August 11, 2007

    Reasong for asking was, i tried to identify a new xml file type by public id, but it seems not working for me. Other two ways for identifying a xml file i.e by extension and root element are working fine.


  • JS Sunday, July 20, 2008

    Geertjan, first of all, great tutorial!

    Second, please note that the JS-WS 2.1 is now an API module (so no need for implemention version), XML Multiview is still not API so there it is needed.

    And finally, please note that when you create a standalone application rather than an integrated module, upon testing the application you can get an error upon changing to 'Design' view saying:

    java.lang.NullPointerException at javax.swing.ImageIcon.<init>(ImageIcon.java:161)

    which I assume has something to do with the PATH to the image corresponding to the filetype not being inherited. Do you know how to fix that?


  • Monty Wednesday, January 5, 2011

    I'm not getting the same results. I'm sure I'm doing something wrong. Is there a completed tutorial to see where I went wrong?


  • Geertjan Wednesday, January 5, 2011

    It is completely impossible to help you. You have not provided a single clue about what's going wrong.


  • monty Wednesday, January 5, 2011

    I hoping there would be a completed tutorial like the netbeans doc center so I could see where I went wrong. Rather not spend time posting specifics. Thanks for your help.


  • Geertjan Wednesday, January 5, 2011

    I still don't get your point. The above IS a completed tutorial, it starts from complete scratch...


  • monty Thursday, January 6, 2011

    If yours is completed, can you post the sources for your completed project - per Parth's request? Most nb tutorials have their source available as d/l. Despite xml multi view not being official, any downloadable tutorial source would be better than nothing.


  • Geertjan Wielenga Thursday, January 6, 2011

    I'm going to try one more time :-) and ask you: where in the above steps is something not working for you?


  • demoneyes Sunday, April 24, 2011

    I have a problem with this tutorial. When I open the file, two TopComponents open at the same time. The first one is the multi view, and the second one is the default xml editor.

    After I removed the following two lines:

    CookieSet cookies = getCookieSet();

    cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));

    The default xml editor didn't get open again. I tried this tutorial with Netbeans 6.9.1. I don't know what I do wrong. At least currently it is working, how it should do.

    Another question: How can I remove the combobox from the "Design" view ?


  • David Saturday, February 18, 2012

    I'm using platform 6.9.1. When I build this module I get:

    No dependent module org.netbeans.modules.websvc.jaxws21

    Would you please give me clue? Thanks


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha