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) {}
}
|