FileDocCategorySizeDatePackage
XStack.javaAPI DocExample4718Fri Feb 01 13:05:26 GMT 2002None

XStack.java

import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Maintaining an Element/Attribute Stack
 */
public class XStack extends DefaultHandler
    implements LexicalHandler, DeclHandler
{
    static class StackEntry
    {
	final String	 nsURI, localName;
	final String	 qName;
	final Attributes atts;
	final StackEntry parent;

	StackEntry (
	    String namespace, String local,
	    String name,
	    Attributes	attrs,
	    StackEntry	next
	) {
	    this.nsURI = namespace;
	    this.localName = local;
	    this.qName = name;
	    this.atts = new AttributesImpl (attrs);
	    this.parent = next;
	}
    }

    private Locator		locator;
    private StackEntry		current;
    private Hashtable		extEntities = new Hashtable ();

    private static final String	xmlNamespace
	= "http://www.w3.org/XML/1998/namespace";

    private void addMarker (String label, String uri)
    throws SAXException
    {
	AttributesImpl	atts = new AttributesImpl ();

	if (locator != null && locator.getSystemId () != null)
	    uri = locator.getSystemId ();

	// guard against InputSource objects without system IDs
	if (uri == null)
	    throw new SAXParseException ("Entity URI is unknown", locator);

	// guard against illegal relative URIs (Xerces)
	try { new URL (uri); }
	catch (IOException e) {
	    throw new SAXParseException ("parser bug: relative URI", locator);
	}

	atts.addAttribute (xmlNamespace, "base", "xml:base", "CDATA", uri);
	current = new StackEntry ("", "", label, atts, current);
    }

    // walk up stack to get values for xml:space, xml:lang, and so on
    public String getInheritedAttribute (String uri, String name)
    {
	String		retval = null;
	boolean		useNS = (uri != null && uri.length () != 0);

	for (StackEntry here = current;
		retval == null && here != null;
		here = here.parent) {
	    if (useNS)
		retval = here.atts.getValue (uri, name);
	    else
		retval = here.atts.getValue (name);
	}
	return retval;
    }

    // knows about XML Base rec, and xml:base attributes
    // can be used in callbacks for elements, PIs, comments,
    // characters, ignorable whitespace, and so on.
    public URL getBaseURI ()
    throws IOException
    {
	return getBaseURI (current);
    }

    private URL getBaseURI (StackEntry here)
    throws IOException
    {
	String		uri = null;

	while (uri == null && here != null) {
	    uri = here.atts.getValue (xmlNamespace, "base");
	    if (uri != null)
		break;
	    here = here.parent;
	}

	// marker for document or entity boundary?  absolute.
	if (here.qName.charAt (0) == '#')
	    return new URL (uri);

	// else it might be a relative uri.
	int		offset = uri.indexOf (":/");

	if (offset == -1 || uri.indexOf (':') < offset)
	    return new URL (getBaseURI (here.parent), uri);
	else
	    return new URL (uri);
    }

    // from ContentHandler interface
    public void startElement (
	String		namespace,
	String		local,
	String		name,
	Attributes	attrs
    ) throws SAXException
	{ current = new StackEntry (namespace, local, name, attrs, current); }

    public void endElement (String namespace, String local, String name)
    throws SAXException
	{ current = current.parent; }

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

    public void startDocument ()
    throws SAXException
	{ addMarker ("#DOCUMENT", null); }

    public void endDocument ()
	{ current = null; }

    // DeclHandler interface

    public void externalEntityDecl (String name, String publicId, String systemId)
    throws SAXException
    {
	if (name.charAt (0) == '%')
	    return;
	// absolutize URL
	try {
	    URL	url = new URL (locator.getSystemId ());
	    systemId = new URL (url, systemId).toString ();
	} catch (IOException e) {
	    // what could we do?
	}
	extEntities.put (name, systemId);
    }

    public void elementDecl (String name, String model) { }
    public void attributeDecl (String element, String name,
    		String type, String mode, String defaultValue) {}
    public void internalEntityDecl (String name, String value) { }

    // LexicalHandler interface
    public void startEntity (String name)
    throws SAXException
    {
	String	uri = (String) extEntities.get (name);
	if (uri != null)
	    addMarker ("#ENTITY", uri);
    }

    public void endEntity (String name)
    throws SAXException
	{ current = current.parent; }
    
    public void startDTD (String root, String publicId, String systemId) {}
    public void endDTD () {}
    public void startCDATA () {}
    public void endCDATA () {}
    public void comment (char buf[], int off, int len) {}
}