/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2005 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution37.javolution.xml.sax;

import java.lang.CharSequence;
import java.nio.ByteBuffer;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

import javolution37.javolution.lang.Reusable;
import javolution37.javolution.xml.pull.XmlPullParser;
import javolution37.javolution.xml.pull.XmlPullParserException;
import javolution37.javolution.xml.pull.XmlPullParserImpl;

import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;

/**
 * 

This class provides a real-time SAX2-like XML parser; this parser is * extremely fast and does not create temporary objects * (no garbage generated and no GC interruption).

* *

The parser input source can be either a {@link #parse(Reader) Reader}, * an {@link #parse(InputStream) InputStream} or even a {@link * #parse(ByteBuffer) ByteBuffer} (e.g. MappedByteBuffer).

* *

The parser is implemented as a SAX2 wrapper around the real-time * {@link XmlPullParserImpl} and share the same characteristics.

* *

Note: This parser is a SAX2-like parser with the * java.lang.String type replaced by the more generic * java.lang.CharSequence in the {@link ContentHandler}, * {@link Attributes} interfaces and {@link DefaultHandler} base class. * If a standard SAX2 or JAXP parser is required, you may consider using * the wrapping class {@link XMLReaderImpl}. Fast but not as fast as * java.lang.String instances are dynamically allocated * while parsing.

* * @author Jean-Marie Dautelle * @version 3.2, April 2, 2005 */ public class XmlSaxParserImpl implements Reusable { /** * Holds the default handler instance. */ private static DefaultHandler DEFAULT_HANDLER = new DefaultHandler(); /** * Holds the content handler. */ private ContentHandler _contentHandler; /** * Holds the error handler. */ private ErrorHandler _errorHandler; /** * Holds the pull parser used for parsing. */ private final XmlPullParserImpl _pullParser = new XmlPullParserImpl(); /** * Holds the document locator. */ private final LocatorImpl _locator = new LocatorImpl(); /** * Default constructor. */ public XmlSaxParserImpl() { // Sets default handlers. setContentHandler(DEFAULT_HANDLER); setErrorHandler(DEFAULT_HANDLER); } /** * Allows an application to register a real-time content event handler. * *

If the application does not register a content handler, all * content events reported by the SAX parser will be silently * ignored.

* *

Applications may register a new or different handler in the * middle of a parse, and the SAX parser must begin using the new * handler immediately.

* * @param handler the real-time content handler. * @throws NullPointerException if the handler argument is null. * @see #getContentHandler */ public void setContentHandler(ContentHandler handler) { if (handler != null) { _contentHandler = handler; } else { throw new NullPointerException(); } } /** * Returns the current real-time content handler. * * @return the current real-time content handler, or null * if none has been registered. * @see #setContentHandler */ public ContentHandler getContentHandler() { return (_contentHandler == DEFAULT_HANDLER) ? null : _contentHandler; } /** * Allows an application to register an error event handler. * *

If the application does not register an error handler, all * error events reported by the SAX parser will be silently * ignored; however, normal processing may not continue. It is * highly recommended that all SAX applications implement an * error handler to avoid unexpected bugs.

* *

Applications may register a new or different handler in the * middle of a parse, and the SAX parser must begin using the new * handler immediately.

* * @param handler the error handler. * @throws NullPointerException if the handler argument is null. * @see #getErrorHandler */ public void setErrorHandler(ErrorHandler handler) { if (handler != null) { _errorHandler = handler; } else { throw new NullPointerException(); } } /** * Returns the current error handler. * * @return the current error handler, or null if none * has been registered. * @see #setErrorHandler */ public ErrorHandler getErrorHandler() { return (_errorHandler == DEFAULT_HANDLER) ? null : _errorHandler; } /** * Parses an XML document from the specified input stream (UTF-8 encoding). * * @param in the input stream with UTF-8 encoding. * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception. * @throws IOException an IO exception from the parser, * possibly from a byte stream or character stream * supplied by the application. * @see javolution37.javolution.io.Utf8StreamReader */ public void parse(InputStream in) throws IOException, SAXException { _pullParser.setInput(in); parseAll(); } /** * Parses an XML document from the specified ByteBuffer * (UTF-8 encoding). * * @param byteBuffer the byte buffer with UTF-8 encoding. * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception. * @throws IOException an IO exception from the parser, * possibly from a byte stream or character stream * supplied by the application. * @see javolution37.javolution.io.Utf8ByteBufferReader */ public void parse(ByteBuffer byteBuffer) throws IOException, SAXException { _pullParser.setInput(byteBuffer); parseAll(); } /** * Parses an XML document using the specified reader. * * @param reader the document reader. * @throws SAXException any SAX exception, possibly wrapping another * exception. * @throws IOException an IO exception from the parser, possibly from * a byte stream or character stream supplied by the application. */ public void parse(Reader reader) throws IOException, SAXException { _pullParser.setInput(reader); parseAll(); } /** * Looks up the value of a feature. * *

Recognizes http://xml.org/sax/features/namespaces * and the http://xml.org/sax/features/namespace-prefixes * feature names.

* * @param name the feature name, which is a fully-qualified URI. * @return the current state of the feature (true or false). * @throws org.xml.sax.SAXNotRecognizedException when the XMLReader does * not recognize the feature name. * @throws org.xml.sax.SAXNotSupportedException when the XMLReader * recognizes the feature name but cannot determine its value * at this time. * @see #setFeature */ public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name.equals("http://xml.org/sax/features/namespaces")) { return true; } else if (name .equals("http://xml.org/sax/features/namespace-prefixes")) { return true; } else { throw new SAXNotRecognizedException("Feature " + name + " not recognized"); } } /** * Sets the state of a feature. * *

Recognizes http://xml.org/sax/features/namespaces * and the http://xml.org/sax/features/namespace-prefixes * feature names.

* * @param name the feature name, which is a fully-qualified URI. * @param value the requested state of the feature (true or false). * @throws org.xml.sax.SAXNotRecognizedException when the XMLReader does not * recognize the feature name. * @throws org.xml.sax.SAXNotSupportedException when the XMLReader * recognizes the feature name but cannot set the requested value. * @see #getFeature */ public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { if (name.equals("http://xml.org/sax/features/namespaces") || name .equals("http://xml.org/sax/features/namespace-prefixes")) { return; // Ignores, these features are always set. } else { throw new SAXNotRecognizedException("Feature " + name + " not recognized"); } } /** * Looks up the value of a property. * * @param name the property name, which is a fully-qualified URI. * @return the current value of the property. * @throws org.xml.sax.SAXNotRecognizedException when the * XMLReader does not recognize the property name. * @throws org.xml.sax.SAXNotSupportedException when the * XMLReader recognizes the property name but * cannot determine its value at this time. * @see #setProperty */ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { throw new SAXNotRecognizedException("Property " + name + " not recognized"); } /** * Sets the value of a property. * * @param name the property name, which is a fully-qualified URI. * @param value the requested value for the property. * @throws org.xml.sax.SAXNotRecognizedException when the * XMLReader does not recognize the property name. * @throws org.xml.sax.SAXNotSupportedException when the * XMLReader recognizes the property name but * cannot set the requested value. */ public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { throw new SAXNotRecognizedException("Property " + name + " not recognized"); } /** * Allows an application to register an entity resolver (ignored by this * parser). * * @param resolver the entity resolver. */ public void setEntityResolver(EntityResolver resolver) { _entityResolver = resolver; } private EntityResolver _entityResolver; /** * Returns the current entity resolver. * * @return the current entity resolver, or null if none * has been registered. * @see #setEntityResolver */ public EntityResolver getEntityResolver() { return _entityResolver; } /** * Allows an application to register a DTD handler (ignored by this parser). * * @param handler the DTD handler. */ public void setDTDHandler(DTDHandler handler) { _dtdHandler = handler; } private DTDHandler _dtdHandler; /** * Returns the current DTD handler. * * @return the current DTD handler, or null if none * has been registered. * @see #setDTDHandler */ public DTDHandler getDTDHandler() { return _dtdHandler; } // Implements Reusable. public void reset() { setContentHandler(DEFAULT_HANDLER); setErrorHandler(DEFAULT_HANDLER); _pullParser.reset(); } /** * Parses the whole document using the real-time pull parser. * * @throws SAXException any SAX exception, possibly wrapping another * exception. * @throws IOException an IO exception from the parser, possibly from * a byte stream or character stream supplied by the application. */ private void parseAll() throws IOException, SAXException { try { int eventType = _pullParser.getEventType(); if (eventType != XmlPullParser.START_DOCUMENT) throw new SAXException("Currently parsing"); _contentHandler.startDocument(); while (true) { eventType = _pullParser.nextToken(); if (eventType == XmlPullParser.START_TAG) { // Start prefix mapping. final int depth = _pullParser.getDepth(); final int nsStart = _pullParser .getNamespaceCount(depth - 1); final int nsEnd = _pullParser.getNamespaceCount(depth); for (int i = nsStart; i < nsEnd; i++) { CharSequence prefix = _pullParser.getNamespacePrefix(i); prefix = prefix == null ? NO_CHAR : prefix; // Default namespace is "" CharSequence uri = _pullParser.getNamespaceUri(i); _contentHandler.startPrefixMapping(prefix, uri); } // Start element. CharSequence localName = _pullParser.getName(); CharSequence uri = _pullParser.getNamespace(); CharSequence qName = _pullParser.getQName(); Attributes atts = _pullParser.getSaxAttributes(); _contentHandler.startElement(uri, localName, qName, atts); } else if (eventType == XmlPullParser.END_TAG) { // End element. CharSequence localName = _pullParser.getName(); CharSequence uri = _pullParser.getNamespace(); CharSequence qName = _pullParser.getQName(); _contentHandler.endElement(uri, localName, qName); // Prefix unmapping. final int depth = _pullParser.getDepth(); final int nsStart = _pullParser.getNamespaceCount(depth); final int nsEnd = _pullParser.getNamespaceCount(depth + 1); for (int i = nsStart; i < nsEnd; i++) { CharSequence prefix = _pullParser.getNamespacePrefix(i); prefix = prefix == null ? NO_CHAR : prefix; // Default namespace is "" _contentHandler.endPrefixMapping(prefix); } } else if ((eventType == XmlPullParser.TEXT) || (eventType == XmlPullParser.CDSECT)) { if (_pullParser.isWhitespace()) { char ch[] = _pullParser.getTextCharacters(_startLength); _contentHandler.ignorableWhitespace(ch, _startLength[0], _startLength[1]); } else { char ch[] = _pullParser.getTextCharacters(_startLength); _contentHandler.characters(ch, _startLength[0], _startLength[1]); } } else if (eventType == XmlPullParser.END_DOCUMENT) { break; } else { // Ignores. } } } catch (XmlPullParserException e) { SAXParseException error = new SAXParseException(e.getMessage(), _locator); _errorHandler.fatalError(error); } finally { // Always executed. _contentHandler.endDocument(); reset(); } } int[] _startLength = new int[2]; /** * Inner class implements Locator interface. */ private class LocatorImpl implements Locator { public String getPublicId() { return null; } public String getSystemId() { return null; } public int getLineNumber() { return _pullParser.getLineNumber(); } public int getColumnNumber() { return _pullParser.getColumnNumber(); } } private static final CharSequence NO_CHAR = new CharSequence() { public int length() { return 0; } public char charAt(int index) { throw new IndexOutOfBoundsException(); } public CharSequence subSequence(int start, int end) { throw new IndexOutOfBoundsException(); } }; }

Ads help maintain this website.