The Java API already provides a graphical view of trees with the
JTree
class. It displays the nodes in a window and they can be expanded
and collapsed by clicking on their handles. This kind of display can also be obtained by
XSL-FO (see section 6.9 of [7]) but few systems currently implement
the full specification which would allow this to happen. Internet Explorer (top right of
Figure 1.2) and Firefox use a similar scheme when displaying
XML files but it is based on the visible
property of Cascading
Style Sheets (CSS). This form of interaction is used on most operating systems to
display the contents of directories. For example, the XML file of Example 2.2 (page ) can be displayed in a
window like the one shown in Figure 8.1. Nodes are shown there with
directory icons and can be expanded or collapsed by clicking on the triangle to the left
of the icon. A node showing a collapsed subtree has a triangle that points downward when
it is expanded. A different display can be obtained by changing the look and
feel in the Java API but the principle stays the same.
Creating such a view from a DOM structure is only a matter of traversing
the structure to create nodes that will be part of the JTree
display.
Its nodes are instances of the predefined DefaultMutableTreeNode
class.
To obtain the display in Figure 8.1, Example 8.1 (line 48‑)
creates a new
TreeViewer
instance that displays a JTree
using a tree model built from the DOM tree constructed using the
jTreeBuild
method. Example 8.6 defines the
class TreeViewer
(line 13‑) that
creates a window to display the
JTree
instance. It is assigned an
initial constant position and size and made scrollable; the application should
terminate when the window is closed.
jTreeBuild
(line 21‑) is a static
method so it is called with the instruction
TreeViewer.jTreeBuild(.)
,
(line 21‑ and line 48‑
of Example 8.2). It
uses the same algorithm as
compact
(line 83‑
of Example 8.1) by recursively
processing element or text DOM
nodes. In the case of an element node (line 25‑
), it creates a new
DefaultMutableTreeNode
for the element and adds
attributes as its first child; it then processes each child by recursively building
its subtree (line 37‑) which is added as a
child of the current node. A non-empty text node is simply a
DefaultMutableTreeNode
having the text as label.
Example 8.6. [TreeViewer.java
]: JTree
building with
DOM and StAX Processing of an XML file
1 import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; 5 import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.events.XMLEvent; 10 import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; public class TreeViewer extends JFrame{TreeViewer(JTree jtree) { 15 super("Tree viewer"); setBounds(100,100,600,450); getContentPane().add(new JScrollPane(jtree)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } 20 public static DefaultMutableTreeNode jTreeBuild(Node node) {
if (node == null) return null; switch (node.getNodeType()) { 25 case Node.ELEMENT_NODE: {
DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(node.getNodeName()); NamedNodeMap attrs = node.getAttributes(); for (int i = 0; i < attrs.getLength(); i++) 30 treeNode.add( new DefaultMutableTreeNode( '@'+attrs.item(i).getNodeName()+ '['+attrs.item(i).getNodeValue()+']')); // process children 35 Node child = node.getFirstChild(); while(child != null){ DefaultMutableTreeNode childTree = jTreeBuild(child);
if(childTree!=null) treeNode.add(childTree);
40 child = child.getNextSibling(); } return treeNode; } case Node.TEXT_NODE: { 45 String text = node.getNodeValue().trim(); return text.length()==0 ? null
: new DefaultMutableTreeNode(text); } default : return null; // ignore other types of nodes 50 } } public static DefaultMutableTreeNode jTreeBuild
(XMLStreamReader xmlsr) throws XMLStreamException{ 55 if(xmlsr.isStartElement()){
DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(xmlsr.getLocalName()); int count = xmlsr.getAttributeCount();// attributes
for (int i = 0; i < count; i++) { 60 treeNode.add( new DefaultMutableTreeNode( "@"+xmlsr.getAttributeLocalName(i)+ '['+xmlsr.getAttributeValue(i)+']')); } 65 while (true){
do {xmlsr.next();} while(xmlsr.isWhiteSpace()); if(xmlsr.getEventType()==XMLEvent.END_ELEMENT)break; DefaultMutableTreeNode childTree = jTreeBuild(xmlsr); if(childTree!=null) treeNode.add(childTree); 70 } return treeNode; } else if (xmlsr.isCharacters()){
return new DefaultMutableTreeNode(xmlsr.getText()); } else 75 return null; } }
|
Inherits from |
|
Recursive method for creating a |
|
When the node is an element node, creates a new |
|
Recursive building of the tree corresponding to a child. |
|
Adds the new children |
|
In the case of a non-empty text node, creates a simple
|
|
Recursive method for creating a |
|
Creates a new tree node with the name of the element. |
|
Adds a node for each attribute name and value. |
|
Loops on children nodes that are not whitespace and adds a new child for each new tree built. |
|
Creates a node with the character content. |
A JTree
using SAX processing is is created with a
special-purpose handler whose methods will be called when XML elements are
encountered during the parsing process. This is similar to the process described in
Section 8.2 where we only needed to keep the current
indentation value.
In Example 8.7, we build a tree using
DefaultMutableTreeNode
instances and keep track of the current node
being built (line 13‑). When an XML element
start-tag is encountered, in
startElement
(line 19‑), a new tree node is created and made
the current node (line 25‑
). Its attributes are
then added as children (line 29‑
). A text
node is simply added as a child of the current node (line 41‑
). When an end-tag is encountered (line 34‑
), we set the current node to the parent of
the current node. The constructor (line 15‑
)
only keeps a copy of the
JTree
that will be displayed. Its model is
initialized on the first call of startElement
(line 22‑).
To create a JTree
, we add (line 40‑
of Example 8.3) a new
JTreeHandler
given as argument
to the SAX parser. Display of the tree is achieved by creating an instance
(line 42‑) of the
TreeViewer
class (line 13‑
of Example 8.6). The
JTree
instance is built by
calling the SAX parser and giving it a JTreeHandler
(line 40‑).
SAX allows the pipelining of handlers that work in
succession during a single parse of the file, a process called
filtering, but here we simplify by parsing the file twice:
once for the textual input (line 36‑
of Example 8.3) and once more for the tree display (line 40‑
).
Example 8.7. [JTreeHandler.java
]: JTree
building with
SAX Processing of an XML file
1 import org.xml.sax.SAXException; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; 5 import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; public class JTreeHandler extends DefaultHandler{ 10 private DefaultTreeModel treeModel; private DefaultMutableTreeNode node = null; private JTree jtree;15 JTreeHandler(JTree jtree){
this.jtree=jtree; } public void startElement(String uri, String localName,
20 String raw, Attributes attrs) throws SAXException { super.startElement(uri,localName,raw,attrs);
if(node==null) //initialise node and model
jtree.setModel( new DefaultTreeModel( 25 node=new DefaultMutableTreeNode(localName)));
else // add child and set node to the child node.add(node=new DefaultMutableTreeNode(localName)); // add attributes as children for (int i = 0; i < attrs.getLength(); i++)
30 node.add(new DefaultMutableTreeNode( '@'+attrs.getLocalName(i)+"["+attrs.getValue(i)+']')); } public void endElement(String uri, String localName, String raw)
35 throws SAXException { super.endElement(uri,localName,raw);
node = (DefaultMutableTreeNode) node.getParent(); } 40 public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch,start,length); String text = new String(ch,start,length).trim(); if(text.length()>0) node.add(new DefaultMutableTreeNode(text)); 45 } }
|
The |
|
Initializes the |
|
Deals with a start element. |
|
Calls the start method of the superclass. |
|
If the node is not initialized, creates a new tree model and uses it for the root. |
|
Creates a new node and adds it to the current |
|
Adds attributes as nodes of the current node. |
|
Processes the end tag. |
|
Calls the |
|
Processes a text node by creating a new |
A JTree
using StAX processing is created with the
same recursive process described for the DOM approach. Looking at Example 8.6,
we immediately see the strong similiraties between the two approaches.
It is only a matter of traversing
the structure to create nodes that will be part of the JTree
display.
Its nodes are instances of the predefined DefaultMutableTreeNode
class.
To obtain the display in Figure 8.1, Example 8.5 (line 38‑)
creates a new
TreeViewer
instance that displays a JTree
by parsing again the file, this is why we create a new instance of a XMLStreamReader
that is given as parameter to the second TreeViewer.JTreeBuild
static method in
Example 8.6
It
uses the same algorithm as compact
(line 83‑
of Example 8.1) by recursively
processing element or text DOM
nodes. In the case of an element node (line 55‑
), it creates a new
DefaultMutableTreeNode
for the element and adds
attributes as its first children (line 65‑); it then processes each child by recursively building
its subtree (line 65‑
) which is added as a
child of the current node. In the case of character content (line 72‑
), a single node with this
character content is returned.