X

Geertjan's Blog

  • November 7, 2010

How to Create a Diff History Viewer

Geertjan Wielenga
Product Manager
If you've followed the last few blog entries, you'll be able to diff databases. However, now we'd like to go a step further and provide the user with a history of diffs, so that various diffs can be compared to each other:

The basis of the Diff History Viewer was laid yesterday, when we created a folder in the application's filesystem, where all diffs are stored, as follows:

The next step is pretty easy, since we simply need to read the above folder (FileUtil.getConfigFile("Diffs")) and display that folder to the user (using BeanNode, BeanTreeView, ExplorerManager, and TopComponent).

We don't even need to use the "New Window" wizard. Just create a standard Java class and define it as follows:

public class DiffHistoryViewer extends TopComponent implements ExplorerManager.Provider {
private ExplorerManager em = new ExplorerManager();
public DiffHistoryViewer() {
setDisplayName("Diff History");
Children diffHistoryChildren = Children.create(new HistoryChildFactory(), true);
Node rootNode = new AbstractNode(diffHistoryChildren);
setLayout(new BorderLayout());
BeanTreeView btv = new BeanTreeView();
btv.setRootVisible(false);
add(btv, BorderLayout.CENTER);
em.setRootContext(rootNode);
associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
}
@Override
public ExplorerManager getExplorerManager() {
return em;
}
private class HistoryChildFactory extends ChildFactory<String> implements FileChangeListener {
FileObject diffsFolder = null;
public HistoryChildFactory() {
diffsFolder = FileUtil.getConfigFile("Diffs");
diffsFolder.addFileChangeListener(this);
}
@Override
protected boolean createKeys(List<String> list) {
for (FileObject diffFolder : diffsFolder.getChildren()) {
list.add(diffFolder.getName());
}
return true;
}
@Override
protected Node createNodeForKey(String key) {
DiffFolderNode node = null;
try {
node = new DiffFolderNode(key);
} catch (IntrospectionException ex) {
Exceptions.printStackTrace(ex);
}
return node;
}
@Override
public void fileFolderCreated(FileEvent fe) {
refresh(true);
}
@Override
public void fileDataCreated(FileEvent fe) {
refresh(true);
}
@Override
public void fileChanged(FileEvent fe) {
refresh(true);
}
@Override
public void fileDeleted(FileEvent fe) {
refresh(true);
}
@Override
public void fileRenamed(FileRenameEvent fre) {
refresh(true);
}
@Override
public void fileAttributeChanged(FileAttributeEvent fae) {
refresh(true);
}
}
private class DiffFolderNode extends BeanNode{
private DiffFolderNode(String key) throws IntrospectionException {
super(key);
setDisplayName(key);
}
@Override
public Action[] getActions(boolean context) {
List<? extends Action> diffFolderNodeActions = Utilities.actionsForPath("Actions/DiffFolderNodeActions");
return diffFolderNodeActions.toArray(new Action[diffFolderNodeActions.size()]);
}
}
}

The above is all standard NetBeans Platform code, with one of the only relatively interesting parts being the "createKeys", where a folder is accessed in the filesystem, after which the names of its children are read and displayed as the names of the Node created in the BeanTreeView. Also, note the usage of "org.openide.filesystems.FileChangeListener", which we add to the folder of interest so that we receive notifications of any changes to the folder (e.g., when a new subfolder is added for a new diff), at which point we call "refresh" on the "ChildFactory", so that the Diff History Viewer will be updated dynamically at runtime.

Also note that the Actions for the Node are read from a folder named "DiffFolderNodeActions", although we haven't created that folder yet. That's where we'll later create a new Action for opening a diff from the Diff History Viewer.

Now use the "New Action" wizard to create an "Always Enabled" Action, which should be registered in the "Window" category and displayed anywhere in the "Window" menu. Name the class "OpenDiffHistoryViewerAction", with display name "Open Diff History Viewer".

Define the new Action class as follows:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.openide.windows.Mode;
import org.openide.windows.WindowManager;
public final class OpenDiffHistoryViewerAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
Mode propertiesMode = WindowManager.getDefault().findMode("properties");
DiffHistoryViewer dhv = new DiffHistoryViewer();
propertiesMode.dockInto(dhv);
dhv.open();
dhv.requestActive();
}
}

Run the application and invoke the new Action from the Window menu, which should show you the following "Diff History" window in the "properties" mode, as indicated below:

Now, we'll create a conditionally enabled Action on the Node in the Diff History window. When invoked, the Action will open the selected diff into the Diff Viewer. Make sure to make the conditionally enabled Action sensitive to "org.openide.nodes.Node" (i.e., you set that in the "New Action" wizard), register the Action into the "DiffFolderNodeActions" folder, which is the one you hooked into the context-sensitive menu of the Node, as shown above. Name the Action "OpenHistoricalDiffAction", with "Open Historical Diff" as the display name.

Here's the Action's definition:

public final class OpenHistoricalDiffAction implements ActionListener {
private final Node context;
public OpenHistoricalDiffAction(Node context) {
this.context = context;
}
@Override
public void actionPerformed(ActionEvent ev) {//Look in the "Diffs" folder:
FileObject diffsFolder = FileUtil.getConfigFile("Diffs");
if (diffsFolder != null) {//Get the children of the folder:
FileObject[] diffsFolderChildren = diffsFolder.getChildren();
if (diffsFolderChildren != null) {//Iterate through the children of the folder:
for (final FileObject stampFolder : diffsFolderChildren) {//Get the name of the folder:
String stampFolderName = stampFolder.getName();//Get the name of the currently selected Node:
String nodeName = context.getDisplayName();//If the name of the folder matches the name of the Node:
if (accept(stampFolderName, nodeName)) {
Runnable r = new Runnable() {
@Override
public void run() {//Create two new FileObjects for the
//two children of the stamped folder:

FileObject createdFile0 = null;
FileObject createdFile1 = null;//Iterate through the stamped folder:
FileObject[] children = stampFolder.getChildren();//Assign one child to the first FileObject,
//the other to the second FileObject:

for (int i = 0; i < children.length; i++) {
FileObject fileObject = children[i];
if (i == 0) {
createdFile0 = fileObject;
} else if (i == 1) {
createdFile1 = fileObject;
}
}//Create StreamSource objects for the Diff Viewer:
File textFile1 = FileUtil.toFile(createdFile0);
File textFile2 = FileUtil.toFile(createdFile1);
StreamSource project0 = StreamSource.createSource("name1",
"Project 1", "text/plain",
textFile1);
StreamSource project1 = StreamSource.createSource("name2",
"Project 2", "text/plain",
textFile2);
//Create the Diff Viewer:
diff(project0, project1, stampFolder);
}
};
r.run();
}
}
}
}
}
private boolean accept(String stampName, String nodeName) {
if (stampName.equals(nodeName)) {
return true;
} else {
return false;
}
}
public void diff(final StreamSource project0, final StreamSource project1, final FileObject stampFolder) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
DiffView view = Diff.getDefault().createDiff(project0, project1);
TopComponent tc = new TopComponent() {
@Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
};
tc.setDisplayName("Diff Viewer" + " -- " + stampFolder.getName());
tc.setLayout(new BorderLayout());
tc.add(view.getComponent(), BorderLayout.CENTER);
tc.open();
tc.requestActive();
} catch (IOException ex) {
}
}
});
}
}

And now you're done. Right-click on any node in the Diff History window, invoke the Action, and the Diff Viewer will open for the selected historical diff.

Be the first to comment

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