X

Geertjan's Blog

  • October 8, 2009

NetBeans Diff API

Geertjan Wielenga
Product Manager
If you're creating some kind of text-based IDE or editor on the NetBeans Platform, integrating a Diff Viewer is possible via the NetBeans Diff API. It's quite new, since NetBeans IDE 6.7.

Here's a simple action that shows how the Diff API works (in its simplest scenario). For example, in real life, you wouldn't hardcode the files, but somehow identify them programmatically:

public final class DiffAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
StreamSource local = StreamSource.createSource("name1",
"title1", "text/html", new File("/home/geertjan/file1.html"));
StreamSource remote = StreamSource.createSource("name2",
"title2", "text/html", new File("/home/geertjan/file2.html"));
diff(local, remote);
}
public void diff(final StreamSource local, final StreamSource remote) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
DiffView view = Diff.getDefault().createDiff(local, remote);
TopComponent tc = new TopComponent();
tc.setDisplayName("Diff Viewer");
tc.setLayout(new BorderLayout());
tc.add(view.getComponent(), BorderLayout.CENTER);
tc.open();
tc.requestActive();
} catch (IOException ex) {
}
}
});
}
}

The code above gives you a whole new window, exactly as shown below, with a visual diff of the two files you sent to the Diff API:

And this is how I register the above action in the layer. Take note especially of the line in bold, which is new in 6.8 and ensures that the action will be invoked asynchronously (and a spinning cursor automatically appears during processing):

<folder name="Actions">
<folder name="Tools">
<file name="org-demo-diffimpl-DiffAction.instance">
<attr name="delegate" newvalue="org.demo.diffimpl.DiffAction"/>
<attr name="displayName" bundlevalue="org.demo.diffimpl.Bundle#CTL_DiffAction"/>
<attr name="instanceCreate" methodvalue="org.openide.awt.Actions.alwaysEnabled"/>
<attr name="noIconInMenu" boolvalue="false"/><attr name="asynchronous" boolvalue="true"/>
</file>
</folder>
</folder>
<folder name="Menu">
<folder name="Tools">
<file name="org-demo-diffimpl-DiffAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/org-demo-diffimpl-DiffAction.instance"/>
<attr name="position" intvalue="0"/>
</file>
</folder>
</folder>

Another good thing is that the sample code shown in the Use Cases section of the NetBeans Diff API javadoc is very clear and so you can begin using this component in your own applications right away.

Join the discussion

Comments ( 8 )
  • Dominique De Vito Thursday, October 8, 2009
  • ocean Thursday, October 8, 2009

    It's a bit worrisome to see all those singletons floating around the netbeans api. Are there any plans to move towards something like osgi?


  • Toni Epple Friday, October 9, 2009

    @Dominique: Could you add a little more detail to your comment? How would moving to OSGi help and what singletons are you referring to? Don't get me wrong, I like OSGi as you might be able to see from my blog, but I simply didn't understand your comment :-).

    Toni


  • Walter Tuesday, October 27, 2009

    Hi, I'm trying to use de Diff API as it says in the javadoc, the use case. But gives me two NullPointerExceptions in these classes:

    org.netbeans.modules.diff.builtin.visualizer.editable.DecoratedEditorPane.paintComponent(DecoratedEditorPane.java:128)

    org.netbeans.modules.diff.builtin.visualizer.editable.LineNumbersActionsBar.paintComponent(LineNumbersActionsBar.java:333)

    It's something about the Editor settings being Null. I don't have it in an Action, only in a method inside the TopComponent, I don't know if there is a difference in that.

    I will thank you any help you can give me


  • Henrik Tuesday, January 18, 2011

    Hello and thank you for a great blog. I have been learning a lot from you!

    Now to my question... I am trying to implement a Diff viewer with merge capabilities just like when you diff files in the Netbeans IDE but after spending a whole day on it I am still getting nowhere. Could you maybe write a new post where you extend whats created in this post with merge functions? I can't find any information on the Internet about how to use the Diff API to create a Diff/Merge editor so hopefully many people could benefit from that! :)

    Regards,

    Henrik


  • Geertjan Wielenga Tuesday, January 18, 2011
  • Henrik Wednesday, January 19, 2011

    Yes I have looked at the documentation. It mentions a class called MergeVisualizer that I have been trying to use but can't figure out how to.

    The code I wrote: http://pastebin.com/3B0ucA3V

    I have found three scenarios that all produce different result:

    1) Using two files where a line have been removed from the latest file.

    current.txt:

    aaaaaaaa

    bbbbbbbb

    cccccccc

    previous.txt:

    aaaaaaaa

    bbbbbbbb

    cccccccc

    dddddddd

    This gives the output:

    Number of diffs: 1

    Diff 0

    First test: dddddddd

    Second text: null

    And the exception:

    java.lang.NullPointerException

    at org.netbeans.modules.merge.builtin.visualizer.MergeControl.initialize(MergeControl.java:102)

    at org.netbeans.modules.merge.builtin.visualizer.GraphicalMergeVisualizer.createView(GraphicalMergeVisualizer.java:134)

    [catch] at DiffTest.MergeVisualizerAction$1.run(MergeVisualizerAction.java:44)

    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)

    at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)

    at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:137)

    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)

    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)

    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)

    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)

    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)

    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

    Looking at the source for MergeControl.java (http://www.java2s.com/Open-Source/Java-Document/IDE-Netbeans/diff/org/netbeans/modules/merge/builtin/visualizer/MergeControl.java.htm) it seem to check if the last difference end with a \\n. In this case the second text is null so a NullPointerException is received.

    2) Using two files with only minor changes so that no null diffs are found

    current.txt:

    aaaaaaaa

    bbbbbbbb

    #cccccccc

    dddddddd

    previous.txt:

    aaaaaaaa

    bbbbbbbb

    cccccccc

    #dddddddd

    This gives the output:

    Number of diffs: 1

    Diff 0

    First test: cccccccc

    #dddddddd

    Second text: #cccccccc

    dddddddd

    And the exception:

    java.lang.IndexOutOfBoundsException: Invalid line index=4 >= lineCount=4

    at org.netbeans.editor.LineRootElement.getElement(LineRootElement.java:115)

    at org.openide.text.NbDocument$DocumentRenderer.run(NbDocument.java:707)

    at org.netbeans.editor.BaseDocument.render(BaseDocument.java:1409)

    at org.openide.text.NbDocument$DocumentRenderer.renderToInt(NbDocument.java:682)

    at org.openide.text.NbDocument.findLineOffset(NbDocument.java:187)

    at org.netbeans.modules.merge.builtin.visualizer.MergePane.getRowStartFromLineOffset(MergePane.java:165)

    at org.netbeans.modules.merge.builtin.visualizer.MergePane.addHighlight(MergePane.java:119)

    at org.netbeans.modules.merge.builtin.visualizer.MergePanel.highlightRegion1(MergePanel.java:1478)

    at org.netbeans.modules.merge.builtin.visualizer.MergeControl.setDiffHighlight(MergeControl.java:206)

    at org.netbeans.modules.merge.builtin.visualizer.MergeControl.initialize(MergeControl.java:133)

    at org.netbeans.modules.merge.builtin.visualizer.GraphicalMergeVisualizer.createView(GraphicalMergeVisualizer.java:134)

    [catch] at DiffTest.MergeVisualizerAction$1.run(MergeVisualizerAction.java:44)

    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)

    at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)

    at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:137)

    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)

    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)

    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)

    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)

    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)

    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

    I do not have a clue about this exception.

    3) Using two files with even less changes:

    current.txt:

    aaaaaaaa

    bbbbbbbb

    #cccccccc

    dddddddd

    previous.txt:

    aaaaaaaa

    bbbbbbbb

    cccccccc

    dddddddd

    This gives the output:

    Number of diffs: 1

    Diff 0

    First test: cccccccc

    Second text: #cccccccc

    It does not throw a exception and the TopComponent is opened but it is only a gray window. So the returned Component c does not seem to contain anything.

    If I do a System.out.println(c.toString()) on it gives the following: c = org.netbeans.modules.merge.builtin.visualizer.MergeDialogComponent[Merge Conflicts Resolver,0,0,547x470,invalid,layout=java.awt.GridBagLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=256,maximumSize=,minimumSize=,preferredSize=]

    But maybe I am using the class wrong, or should be using another class entirely?

    Regards,

    Henrik


  • Ranga Friday, December 30, 2011

    This is a terrific addition to the platform.

    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
Oracle

Integrated Cloud Applications & Platform Services