Processing Xml With Java - A Guide To Sax, Dom, Jdom, Jaxp, And Trax Free Open Book

Processing Xml With Java - A Guide To Sax, Dom, Jdom, Jaxp, And Trax

Previous Section Next Section

Multihandler Adapters

The callback interfaces in SAX are reminiscent of the callback interfaces in the AWT, such as ActionListener and MouseListener. There is, however, one crucial difference between the patterns that the two callback interfaces follow. The AWT allows you to register multiple listener objects with any one component, whereas SAX limits you to just one listener of a certain type per parser. Filters allow you to parse a document through several listeners in turn. However, sometimes it's desirable to install multiple handlers that are not filters.

For example, the DocBook XML source for this book is parsed multiple times to produce HTML, XHTML, PDF, and several other output formats. Each time a new output format is required, the parser must reparse the document. This is really wasted effort because it's the same document. If it's well-formed and valid for HTML output, then it's well-formed and valid for PDF and XHTML output. There's no need to run the checks anew for each separate output document, as long as the input is the same. It makes sense to attach multiple handlers to the parser to avoid wasting time by unnecessarily reparsing the same document.

A properly designed filter can dispatch its events to multiple handlers. The trick is that rather than storing a single ContentHandler/DTDHandler/ErrorHandler the filter stores a list of each. Then each callback method iterates through one of the lists, invoking the method on each of the registered listeners in turn.

When changing the architecture of a class in a subclass as I'm doing here, the naming conventions of the superclass class often don't make perfect sense in the subclass. In this case, it's not clear what the various setFooHandler() methods should do. Do they add a new handler to the list? If so, what happens when null is passed (the idiom for removing a handler from an XMLReader)? Or should I provide new addFooHandler() and removeFooHandler() methods? And which object from the list should getFooHandler() return? There's no obvious answer to these questions. Indeed, I changed my mind several times while designing this class and writing this chapter. Here, I chose to keep the interface as similar to the interface of the superclass as possible. Thus the three setFooHandler() methods add a new handler without replacing the existing handler. But if null is passed, the entire list is cleared. There's no easy way to remove a handler or retrieve or delete a specific one from the list. The getFooHandler() method simply returns the first handler in the list. This is not the most flexible design, but it does keep the interface the same as the interface of the superclass. For example, the following shows how content handlers are stored, added, and removed:

private List contentHandlers = new ArrayList(2); 

public void setContentHandler(ContentHandler handler) {

  if (handler == null) {
    contentHandlers.clear();
  }
  contentHandlers.add(handler);

}

public ContentHandler getContentHandler() {
  if (contentHandlers.isEmpty()) return null;
  return (ContentHandler) contentHandlers.get(0);
}

Other approaches are certainly workable as well.

However the list is filled, the startElement() method would iterate through the list as follows:

public void startElement(String namespaceURI, String localName, 
 String qualifiedName, Attributes atts) throws SAXException {

  Iterator handlers = contentHandlers.iterator();
  while(handlers.hasNext()) {
    ContentHandler handler = (ContentHandler) handlers.next();
    handler.startElement(namespaceURI, localName,
     qualifiedName, atts);
  }

}

The other handlers and methods are implemented similarly. Example 8.15 demonstrates the entire class.

Example 8.15 Attaching Multiple Handlers of the Same Type to a Single Parser
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
import java.util.*;


public class MultiHandlerAdapter extends XMLFilterImpl {

  public MultiHandlerAdapter(XMLReader parent) {
    super(parent);
  }

  List contentHandlers = new ArrayList(2);
  List dtdHandlers = new ArrayList(2);
  List errorHandlers = new ArrayList(2);

  public void setContentHandler(ContentHandler handler) {

    if (handler == null) {
      contentHandlers.clear();
    }
    contentHandlers.add(handler);

  }

  // There's no good way to handle this within the XMLReader
  // interface. I just pick the first in the list.
  public ContentHandler getContentHandler() {
    if (contentHandlers.isEmpty()) return null;
    return (ContentHandler) contentHandlers.get(0);
  }

  public void setDTDHandler(DTDHandler handler) {

    if (handler == null) {
      dtdHandlers.clear();
    }
    dtdHandlers.add(handler);

  }

  public DTDHandler getDTDHandler() {
    if (dtdHandlers.isEmpty()) return null;
    return (DTDHandler) dtdHandlers.get(0);
  }

  public void setErrorHandler(ErrorHandler handler) {

    if (handler == null) {
      errorHandlers.clear();
    }
    errorHandlers.add(handler);

  }

  public ErrorHandler getErrorHandler() {
    if (errorHandlers.isEmpty()) return null;
    return (ErrorHandler) errorHandlers.get(0);
  }

  // ContentHandler implementation
  public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes atts) throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.startElement(namespaceURI, localName,
       qualifiedName, atts);
    }
  }

  public void endElement(String namespaceURI, String localName,
   String qualifiedName) throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.endElement(namespaceURI, localName,
       qualifiedName);
    }

  }

  public void characters(char[] text, int start, int length)
   throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.characters(text, start, length);
    }

  }

  public void ignorableWhitespace(char[] text, int start,
   int length) throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.ignorableWhitespace(text, start, length);
    }
  }

  public void processingInstruction(String target, String data)
   throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.processingInstruction(target, data);
    }
  }

  public void skippedEntity(String name)
   throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.skippedEntity(name);
    }
  }

  public void startPrefixMapping(String prefix, String uri)
   throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.startPrefixMapping(prefix, uri);
    }

  }

  public void endPrefixMapping(String prefix)
   throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.endPrefixMapping(prefix);
    }

  }

  public void startDocument() throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.startDocument();
    }
  }

  public void endDocument() throws SAXException {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.endDocument();
    }

  }

  public void setDocumentLocator(Locator locator) {

    Iterator handlers = contentHandlers.iterator();
    while(handlers.hasNext()) {
      ContentHandler handler = (ContentHandler) handlers.next();
      handler.setDocumentLocator(locator);
    }

  }

  // DTDHandler implementation
  public void notationDecl(String name, String publicID,
   String systemID) throws SAXException {

    Iterator handlers = dtdHandlers.iterator();
    while(handlers.hasNext()) {
      DTDHandler handler = (DTDHandler) handlers.next();
      handler.notationDecl(name, publicID, systemID);
    }

  }

  public void unparsedEntityDecl(String name, String publicID,
   String systemID, String notationName) throws SAXException {

    Iterator handlers = dtdHandlers.iterator();
    while(handlers.hasNext()) {
      DTDHandler handler = (DTDHandler) handlers.next();
      handler.unparsedEntityDecl(name, publicID, systemID,
       notationName);
    }

  }

  // ErrorHandler implementation
  public void warning(SAXParseException exception)
   throws SAXException {

    Iterator handlers = errorHandlers.iterator();
    while(handlers.hasNext()) {
      ErrorHandler handler = (ErrorHandler) handlers.next();
      handler.warning(exception);
    }

  }

  public void error(SAXParseException exception)
   throws SAXException {

    Iterator handlers = errorHandlers.iterator();
    while(handlers.hasNext()) {
      ErrorHandler handler = (ErrorHandler) handlers.next();
      handler.error(exception);
    }

  }

  public void fatalError(SAXParseException exception)
   throws SAXException {

    Iterator handlers = errorHandlers.iterator();
    while(handlers.hasNext()) {
      ErrorHandler handler = (ErrorHandler) handlers.next();
      handler.fatalError(exception);
    }

  }

}

Note

MultiHandlerAdapter is a little on the long side. It contains a lot of duplicated code. This is definitely a case in which the use of templates could save some space.


MultiHandlerAdapter provides only the three main callback interfaces: ContentHandler, DTDHandler, and ErrorHandler. I chose not to provide EntityResolver because having more than one of those responding to the same request doesn't make sense. Each entity must be provided exactly once, not two, or three, or seventeen times. Potentially, you could register multiple entity resolvers and then iterate through them in sequence until one found the entity you were looking for. But because order would be very significant in that case, a proper API would be much trickier to design.

I also chose not to implement the property-based handlers, LexicalHandler and DeclHandler. First, not all parsers support them. Second, they are not part of the standard XMLFilterImpl class. Most important, adding and removing these objects solely by setting and getting features and properties would be very messy. If you need multiples of one of them, it seems simpler to implement the multi-way dispatching in a LexicalHandler/DeclHandler implementation rather than directly in the filter. A similar scheme could work for ContentHandler, DTDHandler, and ErrorHandler, but here the filter approach seems the most natural.

    Previous Section Next Section


         Main Menu
    Main Page
    Table of content
    Copyright
    Praise for Elliotte Rusty Harold's 'Processing XML with Java™'
    List of Examples
    List of Figures
    Preface
    Part I: XML
    Part II: SAX
    Chapter 6. SAX
    Chapter 7. The XMLReader Interface
    Chapter 8. SAX Filters
    The Filter Architecture
    The XMLFilter Interface
    Content Filters
    The XMLFilterImpl Class
    Parsing Non-XML Documents
    Multihandler Adapters
    Summary
    Part III: DOM
    Part IV: JDOM
    Part V: XPath/XSLT
    Part VI: Appendixes


    More Books
    PHP Hacks
    Processing Xml With Java - A Guide To Sax, Dom, Jdom, Jaxp, And Trax
    The Koran (Holy Qur'an)
    Macromedia Flash 8 Bible
    Search Engine Optimization for Dummies
    YouTube Traffic
    PHP 5 for Dummies
    Harry Potter and The Chamber of Secrets
    Harry Potter and the Sorcerer's Stone
    The Pilgrim's Progress
    Wireless Hacks
    Flash Hacks. 100 Industrial-Strength Tips & Tools
    PayPal Hacks. 100 Industrial-Strength Tips and Tools
    Amazon Hacks
    Pdf Hacks
    The Da Vinci Code
    Google Hacks
    The Holy Bible
    Windows XP For Dummies
    Harry Potter and the Half-Blood Prince
    Seo Book
    Upgrading and Repairing Networks
    Macromedia Dreamweaver 8 UNLEASHED
    Windows XP Annoyances
    Windows XP Hacks
    Microsoft Windows XP Power Toolkit
    Teach Yourself MS Office In 24Hours
    iPod & iTunes Missing Manual
    PC Hacks 100 Industrial-Strength Tips and Tools
    PC Overclocking, Optimization, and Tuning - 2th Edition
    PC Hardware In A Nutshell 3rd Edition
    PC Hardware in a Nutshell, 2nd Edition
    Upgrading and Repairing PCs
    Google for Dummies
    MySQL Cookbook
    Teach Yourself Macromedia Flash 8 In 24 Hours
    PHP CookBook
    Sams Teach Yourself JavaScript in 24 Hours
    PHP5 Manual
    Free Games Paper Airplanes
    500 Juegos Gratis 500 Giochi Gratis 500 Jeux Gratuits 500 Jogos Gratis 500 Kostenlose Spiele