![]() ![]() ![]() ![]() ![]() |
Top Contents Index Glossary |
Link Summary
|
Glossary Terms |
Now that you know what a DOM looks like internally, you'll be better prepared to modify a DOM or construct one from scratch . Before going on to that, though, this section presents some modifications to the JTreeModel that let you produce a more user-friendly version of the JTree suitable for use in a GUI.
Displaying the DOM in tree form is all very well for experimenting and to learn how a DOM works. But it's not the kind of "friendly" display that most users want to see in a JTree. However, it turns out that very few modifications are needed to turn the TreeModel adapter into something that will present a user-friendly display. In this section, you'll make those modifications.
Note:
The code discussed in this section is inDomEcho03.java
. The file it operates on isslideSample01.xml
.
When you modify the adapter, you're going to compress the view of the DOM, eliminating all but the nodes you really want to display. Start by defining a boolean variable that controls whether you want the compressed or uncompressed view of the DOM:
public class DomEcho extends JPanel { static Document document;boolean compress = true; static final int windowHeight = 460; ...
The next step is to identify the nodes you want to show up in the tree. To
do that, go to the area where you defined the names of all the element types
(in the typeName
array), and add the code highlighted below:
public class DomEcho extends JPanel { ... public static void makeFrame() { ... } // An array of names for DOM node-types static String[] typeName = { ... };final int ELEMENT_TYPE = 1; // The list of elements to display in the tree static String[] treeElementNames = { "slideshow", "slide", "title", // For slideshow #1 "slide-title", // For slideshow #10 "item", }; boolean treeElement(String elementName) { for (int i=0; i<treeElementNames.length; i++) { if ( elementName.equals(treeElementNames[i]) ) return true; } return false; }
With this code, you set up a constant you can use to identify the ELEMENT node type, declared the names of the elements you want in the tree, and created a method tells whether or not a given element name is a "tree element". Since slideSample01.xml has title elements and slideSample10.xml has slide-title elements, you set up the contents of this arrays so it would work with either data file.
Note:
The mechanism you are creating here depends on the fact that structure nodes like slideshow and slide never contain text, while text usually does appear in content nodes like item. Although those "content" nodes may contain subelements in slideShow10.xml, the DTD constrains those subelements to be XHTML nodes. Because they are XHTML nodes (an XML version of HTML that is constrained to be well-formed), the entire substructure under an item node can be combined into a single string and displayed in the htmlPane that makes up the other half of the application window. In the second part of this section, you'll do that concatenation, displaying the text and XHTML as content in the htmlPane.
The next step is to modify the AdapterNode's childCount function so that it only counts "tree element" nodes -- nodes which are designated as displayable in the JTree. Make the modifications highlighted below to do that:
public class DomEcho extends JPanel { ... public class AdapterNode { ... public AdapterNode child(int searchIndex) { ... } public int childCount() {if (!compress) { // Indent this return domNode.getChildNodes().getLength();} int count = 0; for (int i=0; i<domNode.getChildNodes().getLength(); i++) { org.w3c.dom.Node node = domNode.getChildNodes().item(i); if (node.getNodeType() == ELEMENT_TYPE && treeElement( node.getNodeName() )) { ++count; } } return count; } } // AdapterNode
The only tricky part about this code is checking to make sure the node is an element node before comparing the node. The DocType node makes that necessary, because it has the same name, "slideshow", as the slideshow element.
Finally, you need to modify the AdapterNode's child function to return the Nth item from the list of displayable nodes, rather than the Nth item from all nodes in the list. Add the code highlighted below to do that:
public class DomEcho extends JPanel { ... public class AdapterNode { ... public int index(AdapterNode child) { ... } public AdapterNode child(int searchIndex) { //Note: JTree index is zero-based. org.w3c.dom.Node node = domNode.getChildNodes().item(searchIndex);if (compress) { // Return Nth displayable node int elementNodeIndex = 0; for (int i=0; i<domNode.getChildNodes().getLength(); i++) { node = domNode.getChildNodes().item(i); if (node.getNodeType() == ELEMENT_TYPE && treeElement( node.getNodeName() ) && elementNodeIndex++ == searchIndex) { break; } } } return new AdapterNode(node); } // child } // AdapterNode
There's nothing special going on here. It's a slightly modified version the same logic you used when returning the child count.
When you compile and run this version of the app on slideSample01.xml
,
and then expand the nodes in the tree, you see the results shown in Figure 1.
The only nodes remaining in the tree are the high-level "structure"
nodes.
Figure 1: Tree View with a Collapsed Hierarchy
The way the app stands now, the information that tells the app how to compress the tree for display is "hard coded". Here are some ways you could consider extending the app:
Now that the tree is being displayed properly, the next step is to concatenate the subtrees under selected nodes to display them in the htmlPane. While you're at it, you'll use the concatenated text to put node-identifying information back in the JTree.
Note:
The code discussed in this section is inDomEcho04.java
.
When you concatenate the sub nodes under an element, the processing you do is going to depend on the type of node. So the first thing to is to define constants for the remaining node types. Add the code highlighted below to do that:
public class DomEcho extends JPanel { ... // An array of names for DOM node-types static String[] typeName = { ... }; static final int ELEMENT_TYPE = 1;static final int ATTR_TYPE = 2; static final int TEXT_TYPE = 3; static final int CDATA_TYPE = 4; static final int ENTITYREF_TYPE = 5; static final int ENTITY_TYPE = 6; static final int PROCINSTR_TYPE = 7; static final int COMMENT_TYPE = 8; static final int DOCUMENT_TYPE = 9; static final int DOCTYPE_TYPE = 10; static final int DOCFRAG_TYPE = 11; static final int NOTATION_TYPE = 12;
Next, you need to define add the method that concatenates the text and subnodes for an element and returns it as the element's "content". To define the content method, you'll need to add the big chunk of code highlighted below, but this is the last big chunk of code in the DOM tutorial!.
public class DomEcho extends JPanel { ... public class AdapterNode { ... public String toString() { ... }public String content() { String s = ""; org.w3c.dom.NodeList nodeList = domNode.getChildNodes(); for (int i=0; i<nodeList.getLength(); i++) { org.w3c.dom.Node node = nodeList.item(i); int type = node.getNodeType(); AdapterNode adpNode = new AdapterNode(node); if (type == ELEMENT_TYPE) { if ( treeElement(node.getNodeName()) ) continue; s += "<" + node.getNodeName() + ">"; s += adpNode.content(); s += "</" + node.getNodeName() + ">"; } else if (type == TEXT_TYPE) { s += node.getNodeValue(); } else if (type == ENTITYREF_TYPE) { // The content is in the TEXT node under it s += adpNode.content(); } else if (type == CDATA_TYPE) { StringBuffer sb = new StringBuffer( node.getNodeValue() ); for (int j=0; j<sb.length(); j++) { if (sb.charAt(j) == '<') { sb.setCharAt(j, '&'); sb.insert(j+1, "lt;"); j += 3; } else if (sb.charAt(j) == '&') { sb.setCharAt(j, '&'); sb.insert(j+1, "amp;"); j += 4; } } s += "<pre>" + sb + "\n</pre>"; } } return s; } ... } // AdapterNode
value
.value
. However, since
the text in this case may contain angle brackets and ampersands, you need
to convert them to a form that displays properly in an HTML pane. Unlike
the XML CDATA tag, the HTML <pre> tag does preclude the parsing of
character-format tags, break tags and the like. So you have to convert left-angle
brackets (<) and ampersands (&) to get them to display properly.On the other hand, there are quite a few node types you are not processing with the code above. It's worth a moment to examine them and understand why:
With the content-concatenation out of the way, only a few small programming steps remain. The first is to modify toString so that it uses the node's content for identifying information. Add the code highlighted below to do that:
public class DomEcho extends JPanel { ... public class AdapterNode { ... public String toString() { ... if (! nodeName.startsWith("#")) { s += ": " + nodeName; }if (compress) { String t = content().trim(); int x = t.indexOf("\n"); if (x >= 0) t = t.substring(0, x); s += " " + t; return s; } if (domNode.getNodeValue() != null) { ... } return s; }
Returning now to the app's constructor, create a tree selection listener and use to wire the JTree to the JEditorPane:
public class DomEcho extends JPanel { ... public DomEcho() { ... // Build right-side viewJEditorPane htmlPane = new JEditorPane("text/html",""); htmlPane.setEditable(false); JScrollPane htmlView = new JScrollPane(htmlPane); htmlView.setPreferredSize( new Dimension( rightWidth, windowHeight )); tree.addTreeSelectionListener( new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { TreePath p = e.getNewLeadSelectionPath(); if (p != null) { AdapterNode adpNode = (AdapterNode) p.getLastPathComponent(); htmlPane.setText(adpNode.content()); } } } );
Now, when a JTree node is selected, it's contents are delivered to the htmlPane
.
Note:
The TreeSelectionListener in this example is created using an anonymous inner-class adapter. If you are programming for the 1.1 version of the platform, you'll need to define an external class for this purpose.
If you compile this version of the app, you'll discover immediately that the htmlPane needs to be specified as final to be referenced in an inner class, so add the keyword highlighted below:
public DomEcho04() { ... // Build right-side viewfinal JEditorPane htmlPane = new JEditorPane("text/html",""); htmlPane.setEditable(false); JScrollPane htmlView = new JScrollPane(htmlPane); htmlView.setPreferredSize( new Dimension( rightWidth, windowHeight ));
When you compile the app and run it on slideSample10.xml
,
you get a display like that shown in Figure 2. Expanding the hierarchy shows
that the JTree now includes identifying text for a node whenever possible.
Figure 2: Collapsed Hierarchy Showing Text in Nodes
Selecting an item that includes XHTML subelements produces a display like that shown in Figure 3:
Figure 3: Node with <em> Tag Selected
Selecting a node that contains an entity reference causes the entity text to be included, as shown in Figure 4:
Figure 4: Node with Entity Reference Selected
Finally, selecting a node that includes a CDATA section produces results like those shown in Figure 5:
Figure 5: Node with CDATA Component Selected
A full discussion of the mechanisms for modifying the JTree's underlying data model is beyond the scope of this tutorial. However, a few words on the subject are in order.
Most importantly, note that if you allow the user to modifying the structure by manipulating the JTree, you have take the compression into account when you figure out where to apply the change. For example, if you are displaying text in the tree and the user modifies that, the changes would have to be applied to text subelements, and perhaps require a rearrangement of the XHTML subtree.
When you make those changes, you'll need to understand more about the interactions
between a JTree, it's TreeModel, and an underlying data model. That subject
is covered in depth in the Swing Connection article, Understanding
the TreeModel.
You now understand pretty much what there is know about the structure of a DOM, and you know how to adapt a DOM to create a user-friendly display in a JTree. It has taken quite a bit of coding, but in return you have obtained valuable tools for exposing a DOM's structure and a template for GUI apps. In the next section, you'll make a couple of minor modifications to the code that turn the app into a vehicle for experimentation, and then experiment with building and manipulating a DOM.
![]() ![]() ![]() ![]() ![]() |
Top Contents Index Glossary |