/* 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)); } } }