/* XMLBaseHandler.java -- Handler for the XML Base standard.
Copyright (C) 2005 The University of Sheffield.
This file is part of the CASheW-s editor.
The CASheW-s editor is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
The CASheW-s editor is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with The CASheW-s editor; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
*/
package nongnu.cashews.xml;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Stack;
import java.util.logging.Handler;
import java.util.logging.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* This class deals with the parsing of XML using the
* XML Base standard.
* The current base URI is monitored using a stack, with the default
* URI at the bottom.
*
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
public class XmlBaseHandler
extends DefaultHandler
{
/**
* The XML namespace.
*/
public static final String
XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
/**
* The stack of base URIs.
*
* @serial the base URI stack.
*/
private Stack uris;
/**
* A Logger
instance to log events generated by the
* parsing process.
*/
private Logger xmlBaseLogger;
/**
* The current hierarchical level.
*/
private long level;
/**
* The last element at which a base URI was specified.
*/
private String lastElement;
/**
* The last level at which a base URI was specified.
*/
private long lastLevel;
/**
* Constructs a new XmlBaseHandler
, using the specified
* handler for log messages, and default base URI.
*
* @param handler the handler to use for log messages.
*/
public XmlBaseHandler(Handler handler)
{
xmlBaseLogger = Logger.getLogger("nongnu.cashews.xml.XmlBaseHandler");
xmlBaseLogger.addHandler(handler);
xmlBaseLogger.setLevel(handler.getLevel());
uris = new Stack();
level = -1;
lastLevel = -1;
lastElement = "";
}
/**
* Sets the default base URI. This must be done before parsing begins.
* Any existing URIs are discarded. This method should not be used once
* parsing has begun. If this occurs, the behaviour of the parser is
* undefined (and is likely to lead to errors).
*
* @param baseURI the default base URI.
* @throws IllegalArgumentException if the URI is invalid.
*/
public void setBaseURI(String baseURI)
{
if (!uris.empty())
uris = new Stack();
try
{
pushURI(baseURI);
}
catch (SAXException e)
{
throw new IllegalArgumentException("The default base URI couldn't " +
"be parsed: " + e, e);
}
}
/**
* Captures the start of the document and checks that the initial
* state is correct (i.e. we have some default base URI).
*
* @throws IllegalStateException if the default base URI is not set.
*/
public void startDocument()
{
if (uris.empty())
{
xmlBaseLogger.severe("No default base URI specified.");
throw new IllegalStateException("No default base URI specified.");
}
}
/**
* Captures the start of an XML element.
*
* @param uri the namespace URI.
* @param localName the local name of the element inside the namespace.
* @param qName the local name qualified with the namespace URI. This
* may or may not be provided, as we don't ask for namespace
* prefixes.
* @param attributes the attributes of this element.
* @throws SAXException if some error occurs in parsing.
*/
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException
{
xmlBaseLogger.finest("uri: " + uri + ", localName: " + localName);
logAttributes(attributes);
String value = attributes.getValue(XML_NAMESPACE, "base");
if (value != null)
{
pushURI(value);
lastElement = uri + localName;
lastLevel = level;
}
++level;
xmlBaseLogger.finest("startElement - level: " + level);
}
/**
* Captures the end of an XML element.
*
* @param uri the namespace URI.
* @param localName the local name of the element inside the namespace.
* @param qName the local name qualified with the namespace URI. This
* may or may not be provided, as we don't ask for namespace
* prefixes.
* @throws SAXException if some error occurs in parsing.
*/
public void endElement(String uri, String localName,
String qName)
throws SAXException
{
xmlBaseLogger.finest("uri: " + uri + ", localName: " + localName);
xmlBaseLogger.finest("endElement - level: " + level);
if (lastElement.equals(uri + localName) && lastLevel == level)
popURI();
--level;
}
/**
* Push a new base URI on to the stack and notify the logger of this
* event.
*
* @param baseURI the new URI to add.
* @throws SAXException if the URI can't be parsed.
*/
private void pushURI(String baseURI)
throws SAXException
{
URI newURI;
if (baseURI == null)
baseURI = "";
if (uris.empty())
{
try
{
newURI = new URI(baseURI);
}
catch (URISyntaxException e)
{
throw new SAXException("Failed to parse URI: " + e, e);
}
}
else
{
URI currentBase = getBaseURI();
newURI = currentBase.resolve(baseURI);
}
uris.push(newURI);
xmlBaseLogger.fine("New base URI found: " + newURI);
}
/**
* Remove the most recent base URI from scope.
*/
private void popURI()
{
URI baseURI = uris.pop();
xmlBaseLogger.fine("Leaving scope of base URI: " + baseURI);
}
/**
* Retrieve the most recent base URI.
*
* @return the most recent base URI.
*/
protected URI getBaseURI()
{
return uris.peek();
}
/**
* Retrieve the current level in the hierarchy.
*
* @return the current hierarchical level.
*/
protected long getHierarchicalLevel()
{
return level;
}
/**
* Logs a set of XML attributes received by the parser.
*
* @param attributes the attributes to log.
*/
private void logAttributes(Attributes attributes)
{
int length = attributes.getLength();
for (int a = 0; a < length; ++a)
{
xmlBaseLogger.finest("Attribute " + a + ": " + attributes.getURI(a) +
attributes.getLocalName(a));
}
}
}