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;
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 (); }
// Inner Filter Class: 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 built-in 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 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 inner 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);
}
}