FileDocCategorySizeDatePackage
XI.javaAPI DocExample6714Fri Feb 01 13:10:40 GMT 2002None

XI.java

import java.io.IOException;
import java.net.URL;
import java.util.Vector;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * XInclude-like Processing Instruction
 */
public final class XI extends XMLFilterImpl
implements LexicalHandler, Locator
{
    // Act as a proxy for whatever the current locator is.
    private Locator		locator;

    // to avoid circular inclusion
    private Vector		pending = new Vector (5, 5);

    private LexicalHandler	lexicalHandler;

    private static String	lexicalID =
	    "http://xml.org/sax/properties/lexical-handler";

    public void setDocumentLocator (Locator l)
    {
	locator = l;
	super.setDocumentLocator (this);
    }

    public String getSystemId ()
	{ return (locator == null) ? null : locator.getSystemId (); }
    public String getPublicId ()
	{ return (locator == null) ? null : locator.getPublicId (); }
    public int getLineNumber ()
	{ return (locator == null) ? -1 : locator.getLineNumber (); }
    public int getColumnNumber ()
	{ return (locator == null) ? -1 : locator.getColumnNumber (); }


    // manage the current locator,
    // and filter out events that would be incorrect to report
    private class Scrubber extends XMLFilterImpl implements LexicalHandler
    {
	Locator		savedLocator;
	LexicalHandler	next;

	Scrubber (Locator l, LexicalHandler n)
	    { savedLocator = l; next = n; }

	// maintain proxy locator
	// only one startDocument()/endDocument() pair per event stream
	public void setDocumentLocator (Locator l)
	    { locator = l; }
	public void startDocument ()
	    { }
	public void endDocument ()
	    { locator = savedLocator; }
	
	private void reject (String message) throws SAXException
	    { throw new SAXParseException (message, locator); }

	// only the DTD from the "base document" gets reported
	public void startDTD (String root, String publicId, String systemId)
	throws SAXException
	    { reject ("DTD: " + systemId); }
	public void endDTD ()
	throws SAXException
	    { reject ("DTD"); }
	// ... so this should never happen
	public void skippedEntity (String name) throws SAXException
	    { reject ("entity: " + name); }

	// since we rejected DTDs, only builtin entities can be reported
	public void startEntity (String name)
	throws SAXException
	    { next.startEntity (name); }
	public void endEntity (String name)
	throws SAXException
	    { next.endEntity (name); }

	// other lexical events cause no worries
	public void startCDATA () throws SAXException
	    { next.startCDATA (); }
	public void endCDATA () throws SAXException
	    { next.endCDATA (); }
	public void comment (char buf[], int off, int len) throws SAXException
	    { next.comment (buf, off, len); }
    }


    // count is zero in the document prologue and epilogue
    private int		count;

    public void startElement (String u, String l, String q, Attributes a)
    throws SAXException
	{ count++; super.startElement (u, l, q, a); }

    public void endElement (String u, String l, String q)
    throws SAXException
	{ --count; super.endElement (u, l, q); }
    
    public void startDocument () throws SAXException
	{ pending.addElement (locator.getSystemId ()); super.startDocument (); }

    public void endDocument () throws SAXException
	{ pending.clear (); super.endDocument (); }


    // handle <?XInclude URI?> processing instructions
    public void processingInstruction (String target, String data)
    throws SAXException
    {
	if ("XInclude".equals (target)) {
	    // this should do full XML Base processing
	    // instead we just handle relative and absolute URLs
	    try {
		URL		url = new URL (getSystemId ());

		url = new URL (url, data.trim ());
		data = url.toString ();
	    } catch (Exception e) {
		throw new SAXParseException (
		    "XInclude, can't use URI: " + data, locator, e);
	    }
	    xinclude (data);
	} else
	    super.processingInstruction (target, data);
    }

    // this might be called from startElement too
    private void xinclude (String uri)
    throws SAXException
    {
	XMLReader	helper;
	Scrubber	scrubber;

	if (count == 0)
	    throw new SAXParseException (
		    "XInclude, illegal location", locator);
	if (pending.contains (uri))
	    throw new SAXParseException (
		    "XInclude, circular inclusion", locator);

	// start with another parser acting just like us
	helper = XMLReaderFactory.createXMLReader ();
	helper.setEntityResolver (this);
	helper.setErrorHandler (this);

	// Set up the proxy locator and event filter.
	scrubber = new Scrubber (locator, this);
	locator = null;
	scrubber.setContentHandler (this);
	helper.setContentHandler (scrubber);
	helper.setProperty (lexicalID, scrubber);

	// we INTEND to discard DTDHandler and DeclHandler events

	// Merge the included document, except its DTD
	try {
	    pending.addElement (uri);
	    helper.parse (uri);
	} catch (java.io.IOException e) {
	    SAXParseException	err;
	    ErrorHandler	handler;
	    
	    err = new SAXParseException (uri, locator, e);
	    handler = getErrorHandler ();
	    if (handler != null)
		handler.fatalError (err);
	    throw err;
	} finally {
	    pending.removeElement (uri);
	}
    }

    // LexicalHandler interface
    public void startEntity (String name)
    throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.startEntity (name); }

    public void endEntity (String name)
    throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.endEntity (name); }
    
    public void startDTD (String root, String publicId, String systemId)
    throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.startDTD (root, publicId, systemId); }

    public void endDTD () throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.endDTD (); }
    public void startCDATA () throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.startCDATA (); }
    public void endCDATA () throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.endCDATA (); }
    public void comment (char buf[], int off, int len) throws SAXException
	{ if (lexicalHandler != null) lexicalHandler.comment (buf, off, len); }

    // so this works as a "consumer"
    public void setProperty (String uri, Object handler)
    throws SAXNotRecognizedException, SAXNotSupportedException
    {
	if (lexicalID.equals (uri))
	    lexicalHandler = (LexicalHandler) handler;
	else
	    super.setProperty (uri, handler);
    }

    // so this works as a "producer"
    public void parse (InputSource in)
    throws SAXException, IOException
    {
	XMLReader	parent = getParent ();

	if (parent != null)
	    parent.setProperty (lexicalID, this);
	super.parse (in);
    }
}