/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. *****************************************************************************/ package org.eclipse.buckminster.sax; import java.util.Stack; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.XMLFilterImpl; /** * Instances of this class represents the top handler, i.e. the handler that * deals with the XML document itself. This handler will receive the first call * to {@link #startElement(String, String, String, Attributes)}and then typicall * push a {@link ChildHandler}for that root element. * * @author Thomas Hallgren */ public abstract class TopHandler extends AbstractHandler { /** * The SAXParserWrapper class is useful for parser implementations that want * to expose themselves as a {@link ContentHandler}. The ContentHandler * interface of the <code>TopHandler</code> is already taken and deciated to * a handler that handles the top element only. An instance of * <code>SAXParserWrapper</code> will dispatch all events to the current * <code>ContentHandler</code> of the internal {@link XMLReader} and thus * handle the full document. * * @author thhal */ protected class SAXParserWrapper implements ContentHandler { @Override public void characters(char[] ch, int start, int length) throws SAXException { reader.getContentHandler().characters(ch, start, length); } @Override public void endDocument() throws SAXException { reader.getContentHandler().endDocument(); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { reader.getContentHandler().endElement(uri, localName, qName); } @Override public void endPrefixMapping(String prefix) throws SAXException { reader.getContentHandler().endPrefixMapping(prefix); } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { reader.getContentHandler().ignorableWhitespace(ch, start, length); } @Override public void processingInstruction(String target, String data) throws SAXException { reader.getContentHandler().processingInstruction(target, data); } @Override public void setDocumentLocator(Locator locator) { reader.getContentHandler().setDocumentLocator(locator); } @Override public void skippedEntity(String name) throws SAXException { reader.getContentHandler().skippedEntity(name); } @Override public void startDocument() throws SAXException { reader.getContentHandler().startDocument(); } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { reader.getContentHandler().startElement(uri, localName, qName, atts); } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { reader.getContentHandler().startPrefixMapping(prefix, uri); } } private Locator documentLocator; private XMLReader reader; private final Stack<ContentHandler> handlerStack = new Stack<ContentHandler>(); /** * Create a <code>TopHandler</code> and assing a <code>XMLReader</code> * parent. The created <code>TopHandler</code> will become the content * handler of the parent. * * @param parent * The XMLReader (the actual parser most likely). */ protected TopHandler(XMLReader parent) { reader = parent; parent.setContentHandler(this); } /** * Pop the current handler. * * @param uri * The Namespace URI, ignored. * @param localName * The local name, ignored. * @param qName * The qualified name, ignored. */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { this.popHandler(); } /** * Obtains the current handler from the reader and returns it. * * @return The handler that is current */ public final AbstractHandler getCurrentHandler() { return (AbstractHandler) reader.getContentHandler(); } /** * Returns the encoding of the current document. This method must only be * after the call to {@link #startDocument()} has completed and not after * {@link #endDocument()} has completed. * * @return The document encoding for current document. */ public String getEncoding() { return Utils.getEncoding(documentLocator); } /** * Returns the string "root". */ @Override public String getTAG() { return "root"; //$NON-NLS-1$ } @Override public final TopHandler getTopHandler() { return this; } /** * Pop the filter that was last pushed. */ public void popFilter() { if (reader instanceof XMLFilterImpl) { XMLFilterImpl filter = (XMLFilterImpl) reader; reader = filter.getParent(); reader.setContentHandler(filter.getContentHandler()); filter.setParent(null); filter.setContentHandler(null); } } /** * Push a filter that will sit between the XMLReader and this handler. * * @param filter * The filter. */ public void pushFilter(XMLFilterImpl filter) { filter.setContentHandler(reader.getContentHandler()); reader.setContentHandler(filter); filter.setParent(reader); reader = filter; } /** * Called from parsers that support the <code>Locator</code> interface. */ @Override public final void setDocumentLocator(Locator locator) { documentLocator = locator; } /** * Instruct this parser to use a specific EntityResolver. */ public final void setEntityResolver(EntityResolver resolver) { reader.setEntityResolver(resolver); } /** * Instruct this parser to use a specific ErrorHandler. */ public final void setErrorHandler(ErrorHandler errorHandler) { reader.setErrorHandler(errorHandler); } /** * Instruct this parser to use namespaces. */ public final void setNamespaceAware(boolean flag) { try { reader.setFeature("http://xml.org/sax/features/namespaces", flag); //$NON-NLS-1$ } catch (SAXException e) { // All XMLReaders are required to support the namespace // feature, so this should be regarded as fatal. // throw new RuntimeException(e); } } /** * This method must be implemented by a subclass. The expected behavior is * to push a {@link ChildHandler}that maps to the root element of the XML * document. * * @param uri * The Namespace URI, ignored. * @param localName * The local name, ignored. * @param qName * The qualified name, passed on to the thrown exception. * @param attributes * The attributes attached to the element, ignored * @throws UnrecognizedElementException * unless overridden. */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { throw new UnrecognizedElementException("root", qName, this.getDocumentLocator()); //$NON-NLS-1$ } /** * Returns the document <code>Locator</code> currently associated with this * handler, or <code>null</code> if no locator has been set. * * @return The document <code>Locator</code> or <code>null</code>. */ @Override protected final Locator getDocumentLocator() { return documentLocator; } /** * Returns the XMLReader currently associated with this instance. This is * either the reader that was passed to the constructor or a filter that was * added using {@link #pushFilter(XMLFilterImpl)}. * * @return the XMLReader currently associated with this instance. */ protected final XMLReader getXMLReader() { return reader; } /** * Pop the last pushed handler form the stack and make it the * <code>ContentHandler</code> of the current <code>XMLReader</code>. */ protected final void popHandler() { setHandler(handlerStack.pop()); } /** * Calls the {@link #handleAttributes(Attributes)}method of the supplied * handler. The handler is then made the <code>ContentHandler</code> of the * current <code>XMLReader</code> and the previous handler is stacked. * * @param handler * The new handler * @param attrs * Attributes to pass to the new handler. * @throws SAXException */ protected final void pushHandler(ChildHandler handler, Attributes attrs) throws SAXException { handlerStack.push(reader.getContentHandler()); setHandler(handler); handler.handleAttributes(attrs); } private void setHandler(ContentHandler handler) { reader.setContentHandler(handler); if (handler instanceof LexicalHandler) { try { reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); //$NON-NLS-1$ } catch (SAXException e) { // Ignore. Not all parsers can cope with lexical handlers. } } } }