NetBeans RCP: Editable diff viewer using custom base source

Some days ago Geertjan showed you how to create an editable diff viewer [1] . His approach was based on the integration of the standard “Diff to…”-action (org.netbeans.modules.diff.DiffAction), which requires the selection of nodes.

As a sequel i will show you today how to create a diff action too, BUT this time you are free to define the base you are comparing to. This hasn’t to be another node. Such base can be another file, a revision from source code management, text from a DB and so on. The base source will be shown on the left side and the modified (and editable) source (based on your Node/DataObject, where we attach our action to) will be shown on the right side of the diff window.

The following sample app will compare the given FileObject (from the Lookup) with its base. The base  – in this very simplified example – is a String read from a file.

The main point is to implement your own editable StreamSource for the modified source. Return true from isEditable() and provide a Reader for your FileObject (from node of the modified source)  in createReader(). That’s all. The rest of the sourcecode is only plain standard stuff like creating a diff control, adding this control to a new TopComponent and creating an action, which will open the TopComponent. Geertjan already blogged about it at [2].

example[1]

@ActionID(category = "Edit", id = "de.markiewb.netbeans.sample.editablediff.EditableDiffAction")
@ActionRegistration(displayName = "#CTL_DiffAction")
@ActionReferences({
    @ActionReference(path = "Editors/Popup")})
@Messages("CTL_DiffAction=Editable diff...")
public final class EditableDiffAction implements ActionListener {

    private final FileObject file;

    public EditableDiffAction(FileObject context) {
	this.file = context;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
	final String baseText = getOriginalText(file);
	final StreamSource base = StreamSource.createSource("name1", "base", file.getMIMEType(), new StringReader(baseText));
	final StreamSource modified = EditableStreamSource.createEditableSource("name2", "modified", file.getMIMEType(), file);

	openDiffWindow(file, modified, base, "Diff of " + file.getNameExt() + " to original");
    }

    public void openDiffWindow(final FileObject localFile, final StreamSource local, final StreamSource remote, final String title) {
	SwingUtilities.invokeLater(new Runnable() {
	    @Override
	    public void run() {
		try {
		    final TopComponent tc = new TopComponent();
		    tc.setDisplayName(title);
		    tc.setLayout(new BorderLayout());
		    makeDiffWindowSaveable(tc, localFile);
		    tc.add(DiffController.createEnhanced(remote, local).getJComponent(), BorderLayout.CENTER);
		    tc.open();
		    tc.requestActive();
		} catch (IOException ex) {
		}
	    }
	});
    }

    /**
     * Put the node of dataObject of the fileObject into "globallookup". This
     * allows saving via CTRL-S shortkey from within the editable diff TC. See
     * http://netbeans.org/bugzilla/show_bug.cgi?id=223703
     *
     * @param tc
     * @param fileObject
     */
    private void makeDiffWindowSaveable(TopComponent tc, FileObject fileObject) {
	if (tc != null) {
	    Node node;
	    try {
		node = DataObject.find(fileObject).getNodeDelegate();
	    } catch (DataObjectNotFoundException e) {
		node = new AbstractNode(Children.LEAF, Lookups.singleton(fileObject));
	    }
	    tc.setActivatedNodes(new Node[]{node});
	}
    }

    public String getOriginalText(FileObject file) {
	// TODO this is only a mockup
	// TODO get original text from other sources like SCM, DB, template files..
	try {
	    return file.asText("UTF-8").replace("public ", "public final ");
	} catch (IOException ex) {
	    Exceptions.printStackTrace(ex);
	}
	return "";
    }

    public static class EditableStreamSource extends StreamSource {

	private String name, title, mimeType;
	private FileObject fileObject;

	private EditableStreamSource(String name, String title, String mimeType, FileObject fileObject) {
	    this.name = name;
	    this.title = title;
	    this.mimeType = mimeType;
	    this.fileObject = fileObject;
	}

	public static StreamSource createEditableSource(String name, String title, String mimeType, FileObject fileObject) {
	    return new EditableStreamSource(name, title, mimeType, fileObject);
	}

	@Override
	public String getName() {
	    return this.name;
	}

	@Override
	public String getTitle() {
	    return this.title;
	}

	@Override
	public Lookup getLookup() {
	    return Lookups.fixed(fileObject);
	}

	@Override
	public boolean isEditable() {
	    return fileObject.canWrite();
	}

	@Override
	public String getMIMEType() {
	    return mimeType;
	}

	@Override
	public Reader createReader() throws IOException {
	    return new FileReader(FileUtil.toFile(fileObject));
	}

	@Override
	public Writer createWriter(Difference[] conflicts) throws IOException {
	    return null;
	}
    }
}

PS: There is a small trick to enable the “save”-action (Menubar File->Save / CTRL-S) for your new editable diff, which won’t get enabled after changing content in the right editor pane of the diff viewer by default. You have to associate your node to the TopComponent – see makeDiffWindowSaveable(). Thanks to  Ondrej Vrabec for the solution – see [3].

The full source code of this sample is available at [4] and also documented at [5]

[1] https://blogs.oracle.com/geertjan/entry/how_to_create_an_editable
[2] https://blogs.oracle.com/geertjan/entry/netbeans_diff_api
[3] http://netbeans.org/bugzilla/show_bug.cgi?id=223703
[3] https://github.com/markiewb/nb-api-samples/tree/master/CustomEditableDiff
[4] http://wiki.netbeans.org/DevFaqEditorHowToAddDiffView

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s