Building an XML Tree View Application

To exemplify the easy usage of XML, we will show you how to develop a basic XML file viewer. This is done
 1. by using the SpeedJG XML classes, and
 2. by means of an XMLTreeModel implementation based on the W3C DOM.

XML Tree View

The GUI of this application consists of a JSplitPane with a JTree to navigate on the left and a JTextArea to show the contents on the right. In addition, there is a JPopupMenu available on the text area to copy the contents into the system clipboard.

To generate the source code, open the project ${YourPathToSpeedJG}/Examples.gpr. Select the JFrame xmlTreeView and follow these steps to get a first outline of the Java source code:

  • Press the Generate View Java Code button on the tool bar and save the generated code into a file named XmlTreeView.java.
  • Press the Generate Controller Java Code button on the tool bar. Accept the default listener recommendations and save the generated code into a file named XmlTreeViewController.java.

Customizing the generated Source Code

To customize the source code according to our needs in order to show the expected results we only have to make changes within the XmlTreeView.java file and there only within the model class XmlTreeView. The generated view code (XmlTreeViewGUI) and controller code (XmlTreeViewController) remain unchanged. Thus, if you modify the layout of your GUI with SpeedJG and re-generate the code, your individually added code lines handling the GUI access remain untouched and valid because SpeedJG by default only overwrites the previously generated view and controller code.

First, add the following import statements at the beginning of the XmlTreeView.java file because we need these for some methods we will call later on.

import speed.util.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import javax.swing.event.*;
import java.io.*;

Next, replace the generated declaration and instantiation code lines of the model class XmlTreeView from:

public class XmlTreeView
{
  public XmlTreeView()
  { GUIObject gui = new XmlTreeViewGUI();
    //XmlTreeViewController controller = new XmlTreeViewController(gui);
    JFrame frame = (JFrame) gui.getComponent("xmlTreeView");
    frame.show();
  }
...

to:

public class XmlTreeView extends XmlTreeViewController
{
  public XmlTreeView()
  { super(new XmlTreeViewGUI());
    xmlTreeView.show();
  }
...

For performance and resource reasons SpeedJG implements its own lean XML DOM and also a JTree model that handles a XML document in a JTree-specific manner. Thus you are able not only to handle XML documents within a JTree but also to substitute the default JTree model with an XML-based model!

In our example the instantiation of the SpeedJG XMLDocumentTree is done when opening an XML file.

private XMLDocumentTree openXMLDocument() throws Exception
{ File file = getXMLFile();
  if (file == null) return null;
  return new XMLDocumentTree(XMLDocument.fromFile(file.getPath()).getContent());
}

private class XMLFileFilter extends javax.swing.filechooser.FileFilter
{ public boolean accept(File file)
  { return file.getName().toLowerCase().endsWith(".gpr")
        || file.getName().toLowerCase().endsWith(".xml")
        || file.isDirectory();
  }
  public String getDescription()
  { return "SpeedJG Files, XML Files ( *.gpr, *.xml )";
  }
}

private File getXMLFile()
{ JFileChooser jfc = new JFileChooser();
  jfc.setCurrentDirectory(new File("."));
  jfc.setFileFilter(new XMLFileFilter());
  jfc.removeChoosableFileFilter(jfc.getAcceptAllFileFilter());
  int result = jfc.showOpenDialog(null);
  if (result != JFileChooser.APPROVE_OPTION)
  { return null;
  }
  else
  { return jfc.getSelectedFile();
  }
}

At last, to make our example run, we have to overwrite some methods inherited from the controller class XmlTreeViewController.

  1. Remove the default model from our JTree when starting the application.
    void initialize()
    { selectionTree.setModel(null);
    }
    
  2. Assign our own XMLTreeModel when opening a new XML file.
    void handleOpenMenuItemActionPerformedEvent(ActionEvent e) throws Exception
    { selectionTree.setModel(openXMLDocument());
      xmlTextArea.setText(null);
    }
    
  3. Show the contents of an XML node when selecting a node within the tree.
    void handleSelectionTreeValueChangedEvent(TreeSelectionEvent e) throws Exception
    { Object[] path = e.getPath().getPath();
      XMLElement nodeElement = (XMLElement)path[path.length - 1];
      xmlTextArea.setText(nodeElement.asXMLString());
      xmlTextArea.setCaretPosition(0);
    }
    
  4. Copy the outlined text of an XML node into the system Clipboard.
    void handleCopyMenuItemActionPerformedEvent(ActionEvent e) throws Exception
    { Clipboard clipBoard = Toolkit.getDefaultToolkit().getSystemClipboard();
      clipBoard.setContents(new StringSelection(xmlTextArea.getText()), null);
    }
    
  5. Exit the application.
    void handleExitMenuItemActionPerformedEvent(ActionEvent e) throws Exception
    { System.exit(0);
    }
    

If you want to test and experiment with this example you have to obtain the following files:

XML Tree View Application Files
SpeedJG.jar Contains the classes GUIObject and XMLDocumentTree.
Download and un-zip the SpeedJG.zip file and place the SpeedJG.jar file on your CLASSPATH.
XmlTreeView.java Generated by SpeedJG and modified within this example.
XmlTreeViewController.java Generated by SpeedJG.

XML Tree View Application based on the W3C DOM

If you prefer a tree model that works together with all current implementations of the W3C DOM, or if you want to build an application without the need to include classes from the SpeedJG.jar, you can also use the XmlTreeModel we have provided for these purposes, and that works similar to the SpeedJG XMLDocumentTree. I will not go into detail here because this would be a chapter of its own. So you can study this class and look into the respective literature for particular explanations. But already here I want to mention, that the XmlTreeModel.class references a XmlTreeNodeFilter interface that will not be explained until the next example - XML Tree Edit - because it is not used within this example.

To adapt our example above, we first have to modify the import statements at the beginning of the XmlTreeView.java file, and make the following changes:

fromto
...
import speed.jg.*;
import speed.util.*;
...
...
import javax.xml.parsers.*;
import org.w3c.dom.*;
...

Also comment out the following import statement at the beginning of the XmlTreeViewController.java file:

...
//import speed.jg.*;
...

Next we have to change the openXMLDocument() method that provides a new tree model for us:

private XmlTreeModel openXMLDocument() throws Exception
{ File file = getXMLFile();
  if (file == null) return null;
  DocumentBuilder documentBuilder = 
    DocumentBuilderFactory.newInstance().newDocumentBuilder();
  XmlTreeModel xmlTreeModel =
    new XmlTreeModel(documentBuilder.parse(file).getDocumentElement());
  return xmlTreeModel;
}

At last we have to modify the method handling the tree selection events because these events now deliver Nodes as defined within the W3C DOM interfaces:

void handleSelectionTreeValueChangedEvent(TreeSelectionEvent e) throws Exception
{ Object[] path = e.getPath().getPath();
  Node nodeElement = (Node)path[path.length - 1];
  xmlTextArea.setText(nodeElement.toString());
  xmlTextArea.setCaretPosition(0);
}

If you want to test and experiment with this example you have to store the following files into a directory of your choice and subsequently compile them:

W3C DOM XML Tree View Application Files
GUIObject.java Superclass of all view classes generated by SpeedJG.
XmlTreeView.java Generated by SpeedJG and modified within this example.
XmlTreeViewController.java Generated by SpeedJG.
XmlTreeModel.java TreeModel for XML Documents that are implemented according to the W3C DOM standard.
XmlTreeNodeFilter.java Interface to be implemented by classes that interact with the XmlTreeModel.

If you execute this example you will see some differences to the example based on the SpeedJG XML classes. This is because:

  • SpeedJG only implements XML Elements and no Nodes.
  • SpeedJG shows the element name with the toString() method.
  • It is not defined what your DOM imlementation will show with the toString() method. The org.apache.crimson implementation (JDK 1.4.x) outlines the whole contents of a node. If you use a DOM Level 3 implementation you can attach a DOMWriter.

If you want to customize the representation of an XML document within a JTree, look at the next example - XML Tree Edit - where the XmlTreeNodeFilter comes into play.