Geertjan's Blog

  • January 8, 2013

How to Create an Editable Diff Viewer

Geertjan Wielenga
Product Manager

Well, I've learned a lot of cool things about the NetBeans APIs over the years, but this probably takes the cake. All you need to do if you want an editable diff viewer is put a FileObject into the Lookup of a Node and then you can reuse the standard Diff Action that's part of NetBeans IDE.

In the right pane, you can type, i.e., the right pane is editable. All the editing functionality, e.g., code completion, is available too. That's exactly how it is when using NetBeans IDE's diff functionality. No surprise there, since the Diff Action in the IDE is being reused, completely as-is, unchanged, in this scenario.

Here's the definition of the Node above:

public class CustomerNode extends BeanNode<Customer> {
    public CustomerNode(Customer bean) throws IntrospectionException {
       this(bean, new InstanceContent());
    private CustomerNode(Customer bean, InstanceContent ic)
            throws IntrospectionException {
        super(bean, Children.LEAF, new AbstractLookup(ic));
    public Action[] getActions(boolean context) {
        return new Action[]{FileUtil.getConfigObject(

What's magical about the above is that the org-netbeans-modules-diff-DiffAction becomes active when one or more Nodes are selected and opens the Diff Viewer for the FileObjects found in the Nodes. So, therefore, since my Customer object is defined by a FileObject read from the layer file, when multiple Customers are selected multiple FileObjects are available (because I put the FileObject into the Node Lookup).

Now, what happens if MORE than two FileObjects are available, i.e., more than two Nodes are selected (or the two selected Nodes have more than two FileObjects in their Lookups)? Well, automatically, the Diff Action is disabled:

And what about if there are LESS than two FileObjects? Then you see the following, i.e., you are prompted to browse on disk for a file that you'd like to compare with the available FileObject:

Next, just make sure that the editor modules for the language of your choice are in the application. For example, because I have the HTML Editor modules, the diff view uses the HTML Editor to display the two FileObjects... because the two FileObjects are recognized as being HTML files, since their file extension is "html".

And what if your Customer object does not define a FileObject, i.e., you want to compare something different? Somehow, create a FileObject anyway. Put into that FileObject the data you'd like to diff. For example, I have this in the module's layer file:

    <folder name="customers">
        <file name="james.html" url="resources/james.html"/>
        <file name="john.html" url="resources/john.html"/>
        <file name="henry.html" url="resources/henry.html"/>

I then construct the Node hierarchy from the layer folder you see above:

public class CustomerChildFactory extends ChildFactory<Customer> {
    protected boolean createKeys(List<Customer> list) {
        FileObject customersFolder = FileUtil.getConfigFile("customers");
        for (FileObject customerFile : customersFolder.getChildren()) {
            list.add(new Customer(customerFile));
        return true;
    protected Node createNodeForKey(Customer key) {
        CustomerNode node = null;
        try {
            node = new CustomerNode(key);
        } catch (IntrospectionException ex) {
        return node;

Each file you see in the layer folder snippet above is a FileObject, defined by a name (ending in "html", so that the HTML Editor will be used to display the file) and an HTML file that is within the module. Once I have the Node hierarchy, I put the FileObject into the Node Lookup, as shown in the CustomerNode definition above, after which I reuse the Diff Action, as you can see in the getActions method of the CustomerNode. That's all it takes.

Join the discussion

Comments ( 9 )
  • guest Wednesday, January 9, 2013

    That is a nice shortcut indeed!

    But what I really like about NetBeans are the possibilities to do stuff manually as well. In this case consider a FileObject fo:

    String contentType = fo.getMIMEType();

    DiffView view = Diff.getDefault().createDiff(

    StreamSource.createSource("Before", "Before", contentType,

    new StringReader(beforeText)),

    StreamSource.createSource("After", "After", contentType,

    new StringReader(afterText)));

    Component diffViewComponent = view.getComponent();

    And there you have a nice diff component. If used from within the NetBeans

    infrastructure with the respective handlers for the file's mime type installed you automatically have syntax highlighted previews of your changes.

  • Kevin Wednesday, January 9, 2013

    When I create a CustomerNode and copy your code in NB 7.2, getActions does not override or implement a method from a supertype.

  • Geertjan Wednesday, January 9, 2013

    Because you're not extending BeanNode. Since you don't know how Nodes work, please see the "NetBeans Platform Fundamentals" section on the NetBeans Platform Learning Trail.

  • Kevin Wednesday, January 9, 2013

    Actually, it was because util.Mutex.Action was imported instead of swing.Action.

  • Geertjan Wednesday, January 9, 2013

    So now the code works for you and the Diff Action is working?

  • markiewb Thursday, January 10, 2013

    Nice, thank you Geertjan.

    FYI: I created a FAQ entry at http://wiki.netbeans.org/DevFaqEditorHowToAddDiffView

  • Kevin Thursday, January 10, 2013

    Not quite. When I put the Customers folder under / in layer.xml, it never appeared when I ran the app. So I put it under Menu/Tools. Created the html files, which are reachable from the Project window Important Files | XML Layer | Menu | Tools | Customers | xxx.html. CustomerChildFactory.createKeys getConfigFile refers to "Menu/Tools/Customers."

    But when I run it, a breakpoint at CustomerChildFactory.createKeys is never reached. Tools | Customers shows an empty list.

  • Geertjan Thursday, January 10, 2013

    It's just a plain ordinary Node hierarchy, with a FileObject in the Node Lookup. So, if it's not working for you, you really need to follow some basic tutorials about creating Node hierarchies.

  • Geertjan Thursday, January 10, 2013

    I.e., you haven't got a TopComponent defined. Because that's where you call the ChildFactory from. Again, this is common knowledge to anyone who understands how Node hierarchies work.

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