Extension Interfaces
SAX provides several extension interfaces. These are interfaces that SAX parsers are not required to support; you'll find these interfaces in org.xml.sax.ext. In some cases, you'll have to download these directly from the SAX web site (http://www.saxproject.org), although most parsers will include these in the parser download.
|
LexicalHandler
The first of these handlers is probably the most useful: org.xml.sax.ext.LexicalHandler. This handler provides methods that can receive notification of several lexical events in an XML document, such as comments, entity declarations, DTD declarations, and CDATA sections. In ContentHandler, these lexical events are essentially ignored, and you just get the data and declarations without notification of when or how they were provided. This is not really a general-use handler, as most apps 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 documentand not just its contentsthen 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 JTreeHandler implements ContentHandler, ErrorHandler, LexicalHandler {
To get started, 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 (I've coded up versions appropriate for SAXTreeViewer 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 notes the system ID and public ID of the DTD. Continuing on, there is a pair of similar methods for entity references, startEntity( ) and endEntity( ). These are triggered before and after processing entity references. You can add a visual cue for this event as well:
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 usage-terms entity reference is included within an "Entity" tree node. Simple enough. Next are two events for CDATA sections:
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, 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); }
This method behaves just like the characters( ) and ignorableWhitespace( ) methods. Keep in mind that only the text of the comment is reported to this method, not the surrounding <!-- and --> delimiters. Finally, register this handler with your XMLReader. Since the reader isn't required to support LexicalHandler, you can't just call setLexicalHandler( ); instead, use setProperty( ):
// Register lexical handler reader.setProperty("http://xml.org/sax/properties/lexical-handler", jTreeHandler);
With these changes in place, you can compile the example program and run it. You should get output similar to that shown in .
|
The LexicalHandler implementation reports a DTD (in addition to an entity reference for that DTD), as well as a comment and the usage-terms entity

DeclHandler
A lesser used interface, DeclHandler is another of the extended SAX interfaces. This interface defines methods that receive notification of specific events within a DTD, such as element and attribute declarations. This is another item only good for very specific cases; again, XML editors and components that must know the exact lexical structure of documents and their DTDs come to mind. I'm not going to show you an example of using the DeclHandler; at this point you know more than you'll probably ever need to about handling callback methods. Instead, I'll just give you a look at the interface, shown in .
The DeclHandler interface isn't used often, but it's a real boon if you need to write code that deals directly with DTDs

The DeclHandler interface is fairly self-explanatory. The first two methods handle the <!ELEMENT> and <!ATTLIST> constructs. The third, externalEntityDecl( ), reports entity declarations (through <!ENTITY>) that refer to external resources. The final method, internalEntityDecl( ), reports entities defined inline. That's all there is to it.
Attributes2, Locator2, and EntityResolver2
SAX provides three other interesting interfaces in org.xml.sax.ext: Attributes2, Locator2 , and EntityResolver2. These all extend their respective core interfaces from org.xml.sax (Attributes, Locator, and EntityResolver), and class diagrams are shown for all three in .
The Attributes2, EntityResolver2, and Locator2 interfaces

These interfaces provide additional information for use in parsing, ranging from whether an attribute was specified in a DTD to the encoding of an XML document (pulled from the XML declaration). You can find out if your parser supports and uses these extensions via the getFeature( ) method:
// Check for Attributes2 usage featureURI = "http://xml.org/sax/features/use-attributes2"; jTreeHandler.setUsingAttributes2(reader.getFeature(featureURI)); // Check for Locator2 usage featureURI = "http://xml.org/sax/features/use-locator2"; jTreeHandler.setUsingLocator2(reader.getFeature(featureURI));
|
Unfortunately, most parsers don't support these extensions, so any sort of detailed coverage of them is infeasible. I could show you how they behave under lesser-used parsers that support them, but that's hardly helpful when you move to more mainstream parsers like Apache Xerces. If you are interested in these extensions, check out the SAX Javadoc at http://www.saxproject.org/apidoc/org/xml/sax/ext/package-summary.html, and hope that by the next version of this tutorial, these will be more commonly supported (and then I'll spend some time on them!).
|