This is not really a general-use handler, as most applications
don't need to know if text was in a CDATA
section or not. However, if you are working with an XML editor,
serializer, or other component that must know the exact
format of the input document, not just its
contents, the LexicalHandler can really help you
out. To see this guy in action, you first need to add an import
statement for org.xml.sax.ext.LexicalHandler to
your SAXTreeViewer.java source
file. Once that's done, you can add
LexicalHandler to the
implements clause in the nonpublic class
JTreeContentHandler in that source file:
class JTreeContentHandler implements ContentHandler, LexicalHandler {
// Callback implementations
}
By reusing the content handler already in this class, our lexical
callbacks can operate upon the JTree for visual
display of these lexical callbacks. So now you need to add
implementations for all the methods defined in
LexicalHandler. Those methods are as follows:
public void startDTD(String name, String publicID, String systemID)
throws SAXException;
public void endDTD( ) throws SAXException;
public void startEntity(String name) throws SAXException;
public void endEntity(String name) throws SAXException;
public void startCDATA( ) throws SAXException;
public void endCDATA( ) throws SAXException;
public void comment(char[] ch, int start, int length)
throws SAXException;
To get started, let's look at the first lexical event that
might happen in processing an XML document: the start and end of a
DTD reference or declaration. That triggers the startDTD(
) and endDTD( ) callbacks, shown here:
public void startDTD(String name, String publicID,
String systemID)
throws SAXException {
DefaultMutableTreeNode dtdReference =
new DefaultMutableTreeNode("DTD for '" + name + "'");
if (publicID != null) {
DefaultMutableTreeNode publicIDNode =
new DefaultMutableTreeNode("Public ID: '" +
publicID + "'");
dtdReference.add(publicIDNode);
}
if (systemID != null) {
DefaultMutableTreeNode systemIDNode =
new DefaultMutableTreeNode("System ID: '" +
systemID + "'");
dtdReference.add(systemIDNode);
}
current.add(dtdReference);
}
public void endDTD( ) throws SAXException {
// No action needed here
}
This adds a visual cue when a DTD is encountered, and a system ID and
public ID if present. Continuing on, there are a pair of similar
methods for entity references, startEntity( ) and
endEntity( ). These are triggered before and after
(respectively) processing entity references. You can add a visual cue
for this event as well, using the code shown here:
public void startEntity(String name) throws SAXException {
DefaultMutableTreeNode entity =
new DefaultMutableTreeNode("Entity: '" + name + "'");
current.add(entity);
current = entity;
}
public void endEntity(String name) throws SAXException {
// Walk back up the tree
current = (DefaultMutableTreeNode)current.getParent( );
}
This ensures that the content of, for example, the
OReillyCopyright entity reference is included
within an "Entity" tree node. Simple enough.
Because the next lexical event is a CDATA section,
and there aren't any currently in the contents.xml document, you may want to make
the following change to that document (the CDATA
allows the ampersand in the title element's
content):
<?xml version="1.0"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">
<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
xmlns:ora="http://www.oreilly.com"
>
<title ora:series="Java"><![CDATA[Java & XML]]></title>
<!-- Other content -->
</book>
With this change, you are ready to add code for the
CDATA callbacks. Add in the following methods to
the JTreeContentHandler class:
public void startCDATA( ) throws SAXException {
DefaultMutableTreeNode cdata =
new DefaultMutableTreeNode("CDATA Section");
current.add(cdata);
current = cdata;
}
public void endCDATA( ) throws SAXException {
// Walk back up the tree
current = (DefaultMutableTreeNode)current.getParent( );
}
This is old hat by now; the title element's content now appears
as the child of a CDATA node. And with that, only
one method is left, that which receives comment notification:
public void comment(char[] ch, int start, int length)
throws SAXException {
String comment = new String(ch, start, length);
DefaultMutableTreeNode commentNode =
new DefaultMutableTreeNode("Comment: '" + comment + "'");
current.add(commentNode);
}