Package de.danet.an.util.sax

Source Code of de.danet.an.util.sax.SAXContentBuffer$SAXBufferStreamReader

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: SAXContentBuffer.java 2326 2007-03-27 21:59:44Z mlipp $
*
* $Log$
* Revision 1.3  2006/09/29 12:32:11  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.2  2006/03/08 14:46:43  drmlipp
* Synchronized with 1.3.3p5.
*
* Revision 1.1.1.3.6.2  2006/03/02 15:29:51  drmlipp
* Added toW3cDom().
*
* Revision 1.1.1.3.6.1  2005/12/05 12:12:50  drmlipp
* Added toString() to SAXContentBuffer.
*
* Revision 1.1.1.3  2004/08/18 15:17:35  drmlipp
* Update to 1.2
*
* Revision 1.15  2004/06/30 20:52:37  lipp
* Fixed null pointer exception.
*
* Revision 1.14  2004/06/25 14:51:33  lipp
* Minimal implementation of XMLStreamReader.
*
* Revision 1.13  2004/06/24 20:15:27  lipp
* Started pull API for SAXContentBuffer.
*
* Revision 1.12  2004/05/09 18:41:43  lipp
* Added support for content > 32k.
*
* Revision 1.11  2003/10/18 09:22:23  lipp
* Made emit multi-thread capable.
*
* Revision 1.10  2003/06/27 08:51:46  lipp
* Fixed copyright/license information.
*
* Revision 1.9  2003/04/24 19:47:50  lipp
* Removed dependency between general util and workflow api.
*
* Revision 1.8  2003/04/22 16:35:48  lipp
* Implements SAXEventBuffer now.
*
* Revision 1.7  2003/04/22 11:13:11  lipp
* Improved bufer length handling.
*
* Revision 1.6  2003/04/21 21:26:09  lipp
* Temporary fix for buffer length error.
*
* Revision 1.5  2003/04/18 18:40:17  lipp
* Better performing implementation of SAXContentBuffer.
*
* Revision 1.4  2002/08/26 20:23:13  lipp
* Lots of method renames.
*
* Revision 1.3  2002/08/25 17:56:09  lipp
* Making copy of attributes in SAXContentbuffer anr re-introduced
* attrs.clear(). (Usage of SAXContentBuffer should be transparent.)
*
* Revision 1.2  2002/07/30 08:00:55  huaiyang
* Implements the method of characters.
*
* Revision 1.1  2002/07/29 21:58:53  lipp
* Moved SAXContentBuffer to sax package.
*
* Revision 1.3  2002/07/24 12:32:14  huaiyang
* Implements the defined methods.
*
* Revision 1.2  2002/07/24 05:48:16  huaiyang
* javadocs added.
*
* Revision 1.1  2002/07/17 15:23:51  lipp
* Design definition, to be implemented.
*
*/
package de.danet.an.util.sax;

import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import java.text.ParsePosition;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.LocatorImpl;

/**
* This class provides a buffer for SAX content events. The events
* reported to an instance of this class are internally
* recorded. After recording, the events may be played back an
* arbitrary number of times.<P>
*
* The class implements {@link Serializable
* <code>Serializable</code>}. An instance of this class may thus be
* transferred to another process and the SAX events may be replayed
* there.<P>
*
* This implementation uses code from Apache's XMLByteStreamCompiler
* and associated interpreter.
*
* @author <a href="mailto:lipp@danet.de"></a>
* @version $Revision: 2326 $
*/
public class SAXContentBuffer
    implements ContentHandler, LexicalHandler, Serializable {

    /** Version id for serialization. */
    static final long serialVersionUID = 8913598909630773323L;

    private static final int START_DOCUMENT         = 0;
    private static final int END_DOCUMENT           = 1;
    private static final int START_PREFIX_MAPPING   = 2;
    private static final int END_PREFIX_MAPPING     = 3;
    private static final int START_ELEMENT          = 4;
    private static final int END_ELEMENT            = 5;
    private static final int CHARACTERS             = 6;
    private static final int IGNORABLE_WHITESPACE   = 7;
    private static final int PROCESSING_INSTRUCTION = 8;
    private static final int COMMENT                = 9;
    private static final int LOCATOR                = 10;
    private static final int START_DTD              = 11;
    private static final int END_DTD                = 12;
    private static final int START_CDATA            = 13;
    private static final int END_CDATA              = 14;
    private static final int SKIPPED_ENTITY         = 15;
    private static final int START_ENTITY           = 16;
    private static final int END_ENTITY             = 17;

    private static Templates toStringTemplates = null;

    private transient Map map = new HashMap ();
    private transient int count = 0;

    /** The buffer for the compile xml byte stream. */
    private byte[] buf = new byte[2000];

    /** The number of valid bytes in the buffer. */
    private int bufPos = 0;

    /** The current depth of the tree being recorded. */
    private int depth = 0;

    /**
     * Creates an instance of <code>SAXContentBuffer</code>
     * with all attributes initialized to default values.
     */
    public SAXContentBuffer () {
    }
   
    /**
     * Receive an object for locating the origin of SAX document events.
     *
     * @param locator An object that can return the location of any
     * SAX document event.
     * @see org.xml.sax.Locator
     */
    public void setDocumentLocator (Locator locator) {
  if (locator == null) {
      return;
  }
        try {
            writeEvent(LOCATOR);
            String publicId = locator.getPublicId();
            String systemId = locator.getSystemId();
            writeString(publicId != null ? publicId : "");
            writeString(systemId != null ? systemId : "");
            write(locator.getLineNumber());
            write(locator.getColumnNumber());
        } catch (Exception e) {
             throw new IllegalStateException
     ("Cannot write locator info: " + e.getMessage());
        }
    }

    /**
     * Receive notification of the beginning of a document.
     *
     * @throws SAXException Any SAX exception, possibly
     * wrapping another exception.
     * @see #endDocument
     */
    public void startDocument() throws SAXException {
        writeEvent(START_DOCUMENT);
    }

    /**
     * Receive notification of the end of a document.
     *
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see #startDocument
     */
    public void endDocument() throws SAXException {
        writeEvent(END_DOCUMENT);
  pack ();
    }

    /**
     * Begin the scope of a prefix-URI Namespace mapping.
     *
     * @param prefix The Namespace prefix being declared.
     * @param uri The Namespace URI the prefix is mapped to.
     * @throws SAXException The client may throw an
     * exception during processing.
     * @see #endPrefixMapping
     * @see #startElement
     */
    public void startPrefixMapping (String prefix, String uri)
  throws SAXException {
        writeEvent(START_PREFIX_MAPPING);
        writeString(prefix);
        writeString(uri);
    }

    /**
     * End the scope of a prefix-URI mapping.
     *
     * @param prefix The prefix that was being mapping.
     * @throws SAXException The client may throw
     *            an exception during processing.
     * @see #startPrefixMapping
     * @see #endElement
     */
    public void endPrefixMapping(String prefix) throws SAXException {
  writeEvent(END_PREFIX_MAPPING);
  writeString(prefix);
    }

    /**
     * Receive notification of the beginning of an element.
     *
     * @param namespaceURI The Namespace URI, or the empty string if the
     *        element has no Namespace URI or if Namespace
     *        processing is not being performed.
     * @param localName The local name (without prefix), or the
     *        empty string if Namespace processing is not being
     *        performed.
     * @param qName The qualified name (with prefix), or the
     *        empty string if qualified names are not available.
     * @param atts The attributes attached to the element.  If
     *        there are no attributes, it shall be an empty
     *        Attributes object.
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see #endElement
     * @see org.xml.sax.Attributes
     */
    public void startElement (String namespaceURI, String localName,
            String qName, Attributes atts)
  throws SAXException {
  depth += 1;
        int length = atts.getLength();
        writeEvent(START_ELEMENT);
        writeAttributes(length);
        for (int i = 0; i < length; i++) {
            writeString(atts.getURI(i));
            writeString(atts.getLocalName(i));
            writeString(atts.getQName(i));
            writeString(atts.getType(i));
            writeString(atts.getValue(i));
         }
         writeString((namespaceURI == null ? "" : namespaceURI));
         writeString(localName);
         writeString(qName);
    }

    /**
     * Receive notification of the end of an element.
     *
     * @param namespaceURI The Namespace URI, or the empty string if the
     *        element has no Namespace URI or if Namespace
     *        processing is not being performed.
     * @param localName The local name (without prefix), or the
     *        empty string if Namespace processing is not being
     *        performed.
     * @param qName The qualified XML 1.0 name (with prefix), or the
     *        empty string if qualified names are not available.
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     */
    public void endElement (String namespaceURI, String localName,
          String qName)
  throws SAXException {
        writeEvent(END_ELEMENT);
        writeString((namespaceURI == null ? "" : namespaceURI));
        writeString(localName);
        writeString(qName);
  depth -= 1;
    }

    /**
     * Receive notification of character data.
     *
     * @param ch The characters from the XML document.
     * @param start The start position in the array.
     * @param length The number of characters to read from the array.
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see #ignorableWhitespace
     * @see org.xml.sax.Locator
     */
    public void characters (char[] ch, int start, int length)
  throws SAXException {
        writeEvent(CHARACTERS);
        writeChars(ch, start, length, false);
    }

    /**
     * Receive notification of ignorable whitespace in element content.
     *
     * @param ch The characters from the XML document.
     * @param start The start position in the array.
     * @param length The number of characters to read from the array.
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see #characters
     */
    public void ignorableWhitespace (char[] ch, int start, int length)
  throws SAXException {
        writeEvent(IGNORABLE_WHITESPACE);
        writeChars(ch, start, length, false);
    }

    /**
     * Receive notification of a processing instruction.
     *
     * @param target The processing instruction target.
     * @param data The processing instruction data, or null if
     *        none was supplied.  The data does not include any
     *        whitespace separating it from the target.
     * @throws SAXException Any SAX exception, possibly
     *            wrapping another exception.
     */
    public void processingInstruction (String target, String data)
  throws SAXException {
        writeEvent(PROCESSING_INSTRUCTION);
        writeString(target);
        writeString(data);
    }

    /**
     * Receive notification of a skipped entity.
     *
     * @param name The name of the skipped entity.  If it is a
     * parameter entity, the name will begin with '%', and if it is
     * the external DTD subset, it will be the string "[dtd]".
     * @throws SAXException Any SAX exception, possibly
     * wrapping another exception.
     */
    public void skippedEntity (String name)
  throws SAXException {
        writeEvent(SKIPPED_ENTITY);
        writeString(name);
    }

    /**
     * Report the start of DTD declarations, if any.
     *
     * @param name The document type name.
     * @param publicId The declared public identifier for the external DTD
     *                 subset, or null if none was declared.
     * @param systemId The declared system identifier for the external DTD
     *                 subset, or null if none was declared.
     * @throws SAXException not thrown.
     */
    public void startDTD(String name, String publicId, String systemId)
  throws SAXException {
        writeEvent(START_DTD);
        writeString(name);
        writeString(publicId!=null?publicId:"");
        writeString(systemId!=null?systemId:"");
    }

    /**
     * Report the end of DTD declarations.
     * @throws SAXException not thrown.
     */
    public void endDTD() throws SAXException {
        writeEvent(END_DTD);
    }

    /**
     * Report the beginning of an entity.
     *
     * @param name The name of the entity. If it is a parameter entity, the
     *             name will begin with '%'.
     * @throws SAXException not thrown.
     */
    public void startEntity(String name) throws SAXException {
        writeEvent(START_ENTITY);
        writeString(name);
    }

    /**
     * Report the end of an entity.
     *
     * @param name The name of the entity that is ending.
     * @throws SAXException not thrown.
     */
    public void endEntity(String name) throws SAXException {
        writeEvent(END_ENTITY);
        writeString(name);
    }

    /**
     * Report the start of a CDATA section.
     * @throws SAXException not thrown.
     */
    public void startCDATA() throws SAXException {
        writeEvent(START_CDATA);
    }

    /**
     * Report the end of a CDATA section.
     * @throws SAXException not thrown.
     */
    public void endCDATA() throws SAXException {
        writeEvent(END_CDATA);
    }

    /**
     * Report an XML comment anywhere in the document.
     *
     * @param ary An array holding the characters in the comment.
     * @param start The starting position in the array.
     * @param length The number of characters to use from the array.
     * @throws SAXException not thrown.
     */
    public void comment(char[] ary, int start, int length)
    throws SAXException {
        try {
            writeEvent(COMMENT);
            writeChars(ary, start, length, false);
       } catch (Exception e) {
            throw new SAXException(e);
       }
    }


    private final void writeEvent(int event) throws SAXException {
        write(event);
    }

    private final void writeAttributes(int attributes) throws SAXException {
        write((attributes >>> 8) & 0xFF);
        write((attributes >>> 0) & 0xFF);
    }

    private final void writeString(String str) throws SAXException {
        Integer index = (Integer) map.get(str);
        if (index == null) {
            int length = str.length();
            map.put(str, new Integer(count++));
            writeChars(str.toCharArray(), 0, length, true);
        } else {
            int i = index.intValue();
            write(((i >>> 8) & 0xFF) | 0x80);
            write((i >>> 0) & 0xFF);
        }
    }

    private final void writeChars(char[] ch, int start, int length,
          boolean limitLength)
  throws SAXException {
        int utflen = 0;
        int c, count = 0;

        for (int i = 0; i < length; i++) {
            c = ch[i + start];
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        byte[] bytearr = null;
  if (utflen > 0x00007FFF) {
      if (limitLength) {
    throw new SAXException
        ("UTFDataFormatException: "
         + "String cannot be longer than 32k.");
      }
      bytearr = new byte[utflen+6];
      bytearr[count++] = (byte) (0xFF);
      bytearr[count++] = (byte) (0xFF);
      bytearr[count++] = (byte) ((utflen >>> 24) & 0xFF);
      bytearr[count++] = (byte) ((utflen >>> 16) & 0xFF);
      bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
      bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
  } else {
      bytearr = new byte[utflen+2];
      bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
      bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
  }
        for (int i = 0; i < length; i++) {
            c = ch[i + start];
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;
            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }

        write(bytearr);
    }

    private void write(byte[] b) {
        int len = b.length;
        if (len == 0) {
      return;
  }
        int newcount = bufPos + len;
        if (newcount > buf.length) {
            byte[] newbuf = new byte
    [Math.max((depth > 0 ? buf.length << 1 : 100), newcount)];
            System.arraycopy(buf, 0, newbuf, 0, bufPos);
            buf = newbuf;
        }
        System.arraycopy(b, 0, buf, bufPos, len);
        bufPos = newcount;
    }

    private void write(int b) {
        int newcount = bufPos + 1;
        if (newcount > buf.length) {
            byte[] newbuf = new byte
    [Math.max((depth > 0 ? buf.length << 1 : 100), newcount)];
            System.arraycopy(buf, 0, newbuf, 0, bufPos);
            buf = newbuf;
        }
        buf[bufPos] = (byte)b;
        bufPos = newcount;
    }

    /**
     * The internal buffer used to record events is enlarged in
     * chunks. So some space may be left after the last event has been
     * recorded. This method resizes the buffer to the minimum.<P>
     *
     * This method is called by {@link #endDocument
     * <code>endDocument</code>} automatically. However, if only an
     * XML fragment is recorded, there may never be an
     * <code>endDocument</code> event and the mothod should be called
     * manually.
     */
    public void pack () {
  if (buf.length == bufPos) {
      return;
  }
  byte[] newbuf = new byte[bufPos];
  System.arraycopy(buf, 0, newbuf, 0, bufPos);
  buf = newbuf;
    }

    /**
     * Emits the events to the given <code>ContentHandler</code> and
     * <code>LexicalHandler</code>.
     *
     * @param contentHandler the content handler that is to receive
     * the events.
     * @param lexicalHandler the lexical handler that is to receive
     * the events (may be <code>null</code>).
     * @throws SAXException Any SAX exception, possibly wrapping
     * another exception.
     */
    public void emit (ContentHandler contentHandler,
          LexicalHandler lexicalHandler)
  throws SAXException {
  List st = new ArrayList ();
  ParsePosition curPos = new ParsePosition(0);
        while (curPos.getIndex() < bufPos) {
            switch (readEvent(curPos)) {
      case START_DOCUMENT:
    contentHandler.startDocument();
    break;
      case END_DOCUMENT:
    contentHandler.endDocument();
    break;
      case START_PREFIX_MAPPING:
    contentHandler.startPrefixMapping
        (readString(curPos, st), readString(curPos, st));
    break;
      case END_PREFIX_MAPPING:
    contentHandler.endPrefixMapping(readString(curPos, st));
    break;
      case START_ELEMENT:
    int attributes = readAttributes(curPos);
    AttributesImpl atts = new AttributesImpl();
    for (int i = 0; i < attributes; i++) {
        atts.addAttribute
      (readString(curPos, st), readString(curPos, st),
       readString(curPos, st), readString(curPos, st),
       readString(curPos, st));
    }
    contentHandler.startElement
        (readString(curPos, st), readString(curPos, st),
         readString(curPos, st), atts);
    break;
      case END_ELEMENT:
    contentHandler.endElement
        (readString(curPos, st), readString(curPos, st),
         readString(curPos, st));
    break;
      case CHARACTERS:
    char[] chars = readChars(curPos);
    int len = chars.length;
    while (len > 0 && chars[len-1]==0) {
        len--;
    }
    if (len > 0) {
        contentHandler.characters(chars, 0, len);
    }
    break;
      case IGNORABLE_WHITESPACE:
    char[] spaces = readChars(curPos);
    len = spaces.length;
    while (len > 0 && spaces[len-1] == 0) {
        len--;
    }
    if (len > 0) {
        contentHandler.characters(spaces, 0, len);
    }
    break;
      case PROCESSING_INSTRUCTION:
    contentHandler.processingInstruction
        (readString(curPos, st), readString(curPos, st));
    break;
      case COMMENT:
    chars = readChars(curPos);
    if (lexicalHandler != null) {
        len = chars.length;
        while (len > 0 && chars[len-1] == 0) {
      len--;
        }
        if (len > 0) {
       lexicalHandler.comment(chars, 0, len);
        }
    }
    break;
      case LOCATOR:
    {
                    String publicId = readString(curPos, st);
                    String systemId = readString(curPos, st);
                    int lineNumber = read(curPos);
                    int columnNumber = read(curPos);
                    LocatorImpl locator = new LocatorImpl();
                    locator.setPublicId(publicId);
                    locator.setSystemId(systemId);
                    locator.setLineNumber(lineNumber);
                    locator.setColumnNumber(columnNumber);
                    contentHandler.setDocumentLocator(locator);
    }
    break;
      case START_DTD:
    String name = readString(curPos, st);
    String publicId = readString(curPos, st);
    String systemId = readString(curPos, st);
    if (lexicalHandler != null) {
        lexicalHandler.startDTD (name, publicId, systemId);
    }
    break;
      case END_DTD:
    if (lexicalHandler != null) {
                    lexicalHandler.endDTD();
    }
    break;
      case START_CDATA:
    if (lexicalHandler != null) {
                    lexicalHandler.startCDATA();
    }
    break;
      case END_CDATA:
    if (lexicalHandler != null) {
                    lexicalHandler.endCDATA();
    }
    break;
      case SKIPPED_ENTITY:
    contentHandler.skippedEntity(readString(curPos, st));
    break;
      case START_ENTITY:
    {
        String entity = readString(curPos, st);
        if (lexicalHandler != null) {
      lexicalHandler.startEntity(entity);
        }
        break;
    }
      case END_ENTITY:
    {
        String entity = readString(curPos, st);
        if (lexicalHandler != null) {
      lexicalHandler.endEntity(entity);
        }
        break;
    }
      default:
    throw new SAXException ("parsing error: event not supported.");
            }
        }
    }

    /**
     * Emits the events to the given <code>ContentHandler</code>.
     *
     * @param contentHandler the content handler that is to receive
     * the events.
     * @throws SAXException Any SAX exception, possibly wrapping
     * another exception.
     */
    public void emit (ContentHandler contentHandler) throws SAXException {
  emit (contentHandler, null);
    }

    private int readEvent(ParsePosition curPos) throws SAXException {
        return read(curPos);
    }

    private int readAttributes(ParsePosition curPos) throws SAXException {
        int ch1 = read(curPos);
        int ch2 = read(curPos);
        return ((ch1 << 8) + (ch2 << 0));
    }

    private String readString(ParsePosition curPos, List stringTab)
  throws SAXException {
        int length = readLength(curPos);
        int index = length & 0x00007FFF;
        if (length >= 0x00008000) {
            return (String) stringTab.get(index);
        } else {
            char[] chars = readChars(curPos, index);
            int len = chars.length;
            if (len > 0) {
                while (chars[len-1]==0) {
        len--;
    }
            }
            String str;
            if (len == 0) {
                str = "";
            } else {
                str = new String(chars, 0, len);
            }
            stringTab.add(str);
            return str;
        }
    }

    /**
     * The returned char array might contain any number of zero bytes
     * at the end
     */
    private char[] readChars(ParsePosition curPos) throws SAXException {
  int length = readLength(curPos);
  if (length == 0xFFFF) {
      int ch1 = read(curPos);
      int ch2 = read(curPos);
      int ch3 = read(curPos);
      int ch4 = read(curPos);
      length = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
  }
        return readChars(curPos, length);
    }

    private int read(ParsePosition curPos) throws SAXException {
        if (curPos.getIndex() >= buf.length) {
            throw new SAXException("Reached end of input.");
  }
        int res = buf[curPos.getIndex()] & 0xff;
  curPos.setIndex(curPos.getIndex() + 1);
  return res;
    }

    /**
     * The returned char array might contain any number of zero bytes
     * at the end
     */
    private char[] readChars(ParsePosition curPos, int len)
  throws SAXException {
        char[] str = new char[len];
        byte[] bytearr = new byte[len];
        int c, char2, char3;
        int count = 0;
        int i = 0;

        readBytes(bytearr, curPos);

        while (count < len) {
            c = (int) bytearr[count] & 0xff;
            switch (c >> 4) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    // 0xxxxxxx
                    count++;
                    str[i++] = (char) c;
                    break;
                case 12: case 13:
                    // 110x xxxx   10xx xxxx
                    count += 2;
                    char2 = (int) bytearr[count-1];
                    str[i++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
                    break;
                case 14:
                    // 1110 xxxx  10xx xxxx  10xx xxxx
                    count += 3;
                    char2 = (int) bytearr[count-2];
                    char3 = (int) bytearr[count-1];
                    str[i++] = ((char)(((c & 0x0F) << 12)
               | ((char2 & 0x3F) << 6)
               | ((char3 & 0x3F) << 0)));
                    break;
                default:
                    // 10xx xxxx,  1111 xxxx
                    throw new SAXException("UTFDataFormatException");
            }
        }

        return str;
    }

    private void readBytes(byte[] b, ParsePosition curPos)
  throws SAXException {
        if (curPos.getIndex() + b.length > buf.length) {
            // TC:
            // >= prevents getting the last byte
            // 0 1 2 3 4   input.length = 5
            //     |_ bufPos = 2
            // b.length = 3
            // 2 + 3 > 5 ok
            // 2 + 3 >= 5 wrong
            // why has this worked before?
            throw new SAXException("End of input reached.");
        }
        System.arraycopy(buf, curPos.getIndex(), b, 0, b.length);
        curPos.setIndex(curPos.getIndex() + b.length);
    }

    private int readLength(ParsePosition curPos) throws SAXException {
        int ch1 = read(curPos);
        int ch2 = read(curPos);
        return ((ch1 << 8) + (ch2 << 0));
    }

    /**
     * Create a new <code>XMLEventReader</code> for this SAX content.
     * Note that the implementation of the <code>XMLEventReader</code>
     * is currently incomplete and provides only very basic functions.
     *
     * @return the event reader
     */
    public XMLStreamReader createXMLStreamReader () {
  return new SAXBufferStreamReader ();
    }

    /**
     * An XML stream reader backed up by the
     * <code>SAXContentBuffer</code>.
     */
    public class SAXBufferStreamReader
  implements Serializable, XMLStreamReader {

  private ParsePosition curPos = new ParsePosition(0);
  private List st = new ArrayList ();
  private AttributesImpl atts = null;
  private int attsIndex = 0;

  private String namespaceURI = null;
  private String localName = null;
  private String qName = null;
 
  // Implementation of javax.xml.stream.XMLStreamReader

  /**
   * Get the value of a feature/property from the underlying
   * implementation.
   *
   * @param string the name of the property, may not be null
   * @return the value of the property
   * @exception IllegalArgumentException if the property is not
   * supported
   */
  public Object getProperty(String string)
      throws IllegalArgumentException {
      throw new IllegalArgumentException ("No such property: " + string);
  }

  /**
   * Returns a <code>QName</code> for the current
   * <codeSTART_ELEMENT</code> or <code>END_ELEMENT</code>
   * event.
   *
   * @return a <code>QName</code> value
   * @throws IllegalStateException if this is not a
   * <code>START_ELEMENT</code> or <code>END_ELEMENT</code>
   */
  public QName getName() throws IllegalStateException {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the next parsing event
   * @return the next parsing event
   * @throws NoSuchElementException if this is called when
   * <code>hasNext()</code> returns false
   * @exception XMLStreamException if there is an error
   * processing the underlying XML source
   */
  public int next() throws XMLStreamException, NoSuchElementException {
      namespaceURI = null;
      localName = null;
      qName = null;

      // Are there pending attribute events?
      if (atts != null) {
    attsIndex += 1;
    if (attsIndex < atts.getLength()) {
        return XMLStreamConstants.ATTRIBUTE;
    }
    atts = null;
      }
      try {
    // Process events until we find something to return
    while (curPos.getIndex() < bufPos) {
        // Read next event from buffer
        switch (readEvent(curPos)) {
        case SAXContentBuffer.START_DOCUMENT: {
      return XMLStreamConstants.START_DOCUMENT;
        }
        case SAXContentBuffer.END_DOCUMENT: {
      curPos.setIndex(bufPos);
      return XMLStreamConstants.END_DOCUMENT;
        }
        case SAXContentBuffer.START_PREFIX_MAPPING: {
      String prefix = readString(curPos, st);
      String uri = readString(curPos, st);
      break;
        }
        case SAXContentBuffer.END_PREFIX_MAPPING: {
      String prefix = readString(curPos, st);
      break;
        }
        case SAXContentBuffer.START_ELEMENT: {
      int attributes = readAttributes(curPos);
      atts = new AttributesImpl ();
      for (int i = 0; i < attributes; i++) {
          atts.addAttribute
        (readString(curPos, st), readString(curPos, st),
         readString(curPos, st), readString(curPos, st),
         readString(curPos, st));
      }
      attsIndex = -1;
      namespaceURI = readString(curPos, st);
      localName = readString(curPos, st);
      qName = readString(curPos, st);
      return XMLStreamConstants.START_ELEMENT;
        }
        case SAXContentBuffer.END_ELEMENT: {
      namespaceURI = readString(curPos, st);
      localName = readString(curPos, st);
      qName = readString(curPos, st);
      return XMLStreamConstants.END_ELEMENT;
        }
        case SAXContentBuffer.CHARACTERS: {
      char[] chars = readChars(curPos);
      int len = chars.length;
      while (len > 0 && chars[len-1]==0) {
          len--;
      }
      if (len > 0) {
          return XMLStreamConstants.CHARACTERS;
      }
      break;
        }
        case SAXContentBuffer.IGNORABLE_WHITESPACE: {
      char[] chars = readChars(curPos);
      int len = chars.length;
      while (len > 0 && chars[len-1]==0) {
          len--;
      }
      if (len > 0) {
      }
      break;
        }
        case SAXContentBuffer.PROCESSING_INSTRUCTION: {
      String target = readString(curPos, st);
      String data = readString(curPos, st);
      return XMLStreamConstants.PROCESSING_INSTRUCTION;
        }
        case SAXContentBuffer.COMMENT: {
      char[] chars = readChars(curPos);
      int len = chars.length;
      while (len > 0 && chars[len-1]==0) {
          len--;
      }
      if (len > 0) {
      }
      return XMLStreamConstants.COMMENT;
        }
        case SAXContentBuffer.LOCATOR: {
      String publicId = readString(curPos, st);
      String systemId = readString(curPos, st);
      int lineNumber = read(curPos);
      int columnNumber = read(curPos);
      break;
        }
        case SAXContentBuffer.START_DTD: {
      String name = readString(curPos, st);
      String publicId = readString(curPos, st);
      String systemId = readString(curPos, st);
      break;
        }
        case SAXContentBuffer.END_DTD: {
      return XMLStreamConstants.DTD;
        }
        case SAXContentBuffer.START_CDATA: {
      break;
        }
        case SAXContentBuffer.END_CDATA: {
      return XMLStreamConstants.CDATA;
        }
        case SAXContentBuffer.SKIPPED_ENTITY: {
      String skippedEntity = readString(curPos, st);
      break;
        }
        case SAXContentBuffer.START_ENTITY: {
      String entity = readString(curPos, st);
      break;
        }
        case SAXContentBuffer.END_ENTITY: {
      String entity = readString(curPos, st);
      return XMLStreamConstants.ENTITY_DECLARATION;
        }
        default: {
      throw new XMLStreamException
          ("parsing error: event not supported.");
        }
        }
    }
    throw new XMLStreamException("No more events");
      } catch (SAXException e) {
    throw (XMLStreamException)
        (new XMLStreamException (e.getMessage ())).initCause (e);
      }
  }

  /**
   * Returns <code>true</code> if there are more parsing events
   * and <code>false</code> if there are no more events. This
   * method will return <code>false</code> if the current state of the
   * XMLStreamReader is <code>END_DOCUMENT</code>
   *
   * @return a <code>boolean</code> value
   * @exception XMLStreamException if an error occurs
   */
  public boolean hasNext() throws XMLStreamException {
      return curPos.getIndex() < bufPos;
  }

  /**
   * Frees any resources associated with this Reader.
   *
   * @exception XMLStreamException if an error occurs
   */
  public void close() throws XMLStreamException {
  }

  /**
   * Returns <code>null</code> as the encoding is unknown.
   *
   * @return a <code>String</code> value
   */
  public String getEncoding() {
      return null;
  }

  /**
   * Return the current location of the processor. If the
   * Location is unknown the processor should return an
   * implementation of Location that returns -1 for the location
   * and null for the publicId and systemId. The location
   * information is only valid until next() is called.
   *
   * @return a <code>Location</code> value
   */
  public Location getLocation() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Get the xml version declared on the xml declaration. Returns
   * <code>null</code> if none was declared.
   *
   * @return a <code>String</code> value
   */
  public String getVersion() {
      return null;
  }

  /**
   * Returns the current value of the parse event as a string,
   * this returns the string value of a <code>CHARACTERS</code>
   * event, returns the value of a <code>COMMENT</code>, the
   * replacement value for an <code>ENTITY_REFERENCE</code>, the
   * string value of a <code>CDATA</code> section, the string
   * value for a <code>SPACE</code> event, or the String value
   * of the internal subset of the DTD. If an
   * <code>ENTITY_REFERENCE</code> has been resolved, any
   * character data will be reported as <code>CHARACTERS</code>
   * events.
   *
   * @return a <code>String</code> value
   */
  public String getText() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the character encoding declared on the xml
   * declaration. Returns <code>null</code> if none was declared.
   *
   * @return a <code>String</code> value
   */
  public String getCharacterEncodingScheme() {
      return null;
  }

  /**
   * Get the standalone declaration from the xml declaration.
   *
   * @return a <code>boolean</code> value
   */
  public boolean isStandalone() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Checks if standalone was set in the document.
   *
   * @return a <code>boolean</code> value
   */
  public boolean standaloneSet() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns an integer code that indicates the type of the
   * event the cursor is pointing to.
   *
   * @return an <code>int</code> value
   */
  public int getEventType() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns <code>true</code> if the cursor points to a start
   * tag (otherwise <code>false</code>).
   *
   * @return a <code>boolean</code> value
   */
  public boolean isStartElement() {
      return getEventType () == START_ELEMENT;
  }

  /**
   * Returns <code>true</code> if the cursor points to an end
   * tag (otherwise <code>false</code>).
   *
   * @return a <code>boolean</code> value
   */
  public boolean isEndElement() {
      return getEventType () == END_ELEMENT;
  }

  /**
   * Returns <code>true</code> if the cursor points to a
   * character data event.
   *
   * @return a <code>boolean</code> value
   */
  public boolean isCharacters() {
      return getEventType () == CHARACTERS;
  }

  /**
   * Returns a read only namespace context for the current
   * position. The context is transient and only valid until a
   * call to <code>next()</code> changes the state of the
   * reader.
   *
   * @return a <code>NamespaceContext</code> value
   */
  public NamespaceContext getNamespaceContext() {
      throw new UnsupportedOperationException ();
  }

  /**
   * If the current event is a <code>START_ELEMENT</code> or
   * <code>END_ELEMENT</code> this method returns the URI of the
   * prefix or the default namespace. Returns <code>null</code>
   * if the event does not have a prefix.
   *
   * @param string a <code>String</code> value
   * @return a <code>String</code> value
   */
  public String getNamespaceURI(String string) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the uri for the namespace declared at the index.
   *
   * @param n the position of the namespace declaration
   * @return a <code>String</code> value
   */
  public String getNamespaceURI(int n) {
      throw new UnsupportedOperationException ();
  }

  /**
   * If the current event is a <code>START_ELEMENT</code> or
   * <code>END_ELEMENT</code> this method returns the URI of the
   * prefix or the default namespace. Returns null if the event
   * does not have a prefix.
   *
   * @return a <code>String</code> value
   */
  public String getNamespaceURI() {
      return namespaceURI;
  }

  /**
   * Returns true if the cursor points to a character data event
   * that consists of all whitespace.
   *
   * @return a <code>boolean</code> value
   */
  public boolean isWhiteSpace() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the prefix of the current event or
   * <code>null</code> if the event does not have a prefix.
   *
   * @return a <code>String</code> value
   */
  public String getPrefix() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Test if the current event is of the given type and if the
   * namespace and name match the current namespace and name of
   * the current event. If the namespaceURI is <code>null</code>
   * it is not checked for equality, if the localName is
   * <code>null</code> it is not checked for equality.
   *
   * @param type an <code>int</code> value
   * @param namespaceURI a <code>String</code> value
   * @param localName a <code>String</code> value
   * @exception XMLStreamException if an error occurs
   */
  public void require(int type, String namespaceURI, String localName)
      throws XMLStreamException {
      throw new UnsupportedOperationException ();
  }

  /**
   * Reads the content of a text-only element, an exception is
   * thrown if this is not a text-only element. Regardless of
   * value of javax.xml.stream.isCoalescing this method always
   * returns coalesced content.
   *
   * @return a <code>String</code> value
   * @exception XMLStreamException if an error occurs
   */
  public String getElementText() throws XMLStreamException {
      throw new UnsupportedOperationException ();
  }

  /**
   * Skips any white space (<code>isWhiteSpace()</code> returns
   * <code>true</code>), <code>COMMENT</code>, or
   * <code>PROCESSING_INSTRUCTION</code>, until a
   * <code>START_ELEMENT</code> or <code>END_ELEMENT</code> is
   * reached. If other than white space characters,
   * <code>COMMENT</code>, <code>PROCESSING_INSTRUCTION</code>,
   * <code>START_ELEMENT</code>, <code>END_ELEMENT</code> are
   * encountered, an exception is thrown. This method should be
   * used when processing element-only content seperated by
   * white space.
   *
   * @return an <code>int</code> value
   * @exception XMLStreamException if an error occurs
   */
  public int nextTag() throws XMLStreamException {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the normalized attribute value of the attribute
   * with the namespace and localName If the namespaceURI is
   * null the namespace is not checked for equality
   *
   * @param namespaceURI a <code>String</code> value
   * @param localName a <code>String</code> value
   * @return a <code>String</code> value
   */
  public String getAttributeValue(String namespaceURI, String localName) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the value of the attribute at the index.
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getAttributeValue(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the count of attributes on this
   * <code>START_ELEMENT</code>, this method is only valid on a
   * <code>START_ELEMENT</code> or <code>ATTRIBUTE</code>. This
   * count excludes namespace definitions. Attribute indices are
   * zero-based.
   *
   * @return an <code>int</code> value
   */
  public int getAttributeCount() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the qname of the attribute at the provided index.
   *
   * @param index an <code>int</code> value
   * @return a <code>QName</code> value
   */
  public QName getAttributeName(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the namespace of the attribute at the provided index.
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getAttributeNamespace(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the localName of the attribute at the provided index.
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getAttributeLocalName(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the prefix of this attribute at the provided index.
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getAttributePrefix(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the XML type of the attribute at the provided index.
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getAttributeType(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns a boolean which indicates if this attribute was
   * created by default.
   *
   * @param index an <code>int</code> value
   * @return a <code>boolean</code> value
   */
  public boolean isAttributeSpecified(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the count of namespaces declared on this
   * <code>START_ELEMENT</code> or <code>END_ELEMENT</code>,
   * this method is only valid on a <code>START_ELEMENT</code>,
   * <code>END_ELEMENT</code> or <code>NAMESPACE</code>. On an
   * <code>END_ELEMENT</code> the count is of the namespaces
   * that are about to go out of scope. This is the equivalent
   * of the information reported by SAX callback for an end
   * element event.
   *
   * @return an <code>int</code> value
   */
  public int getNamespaceCount() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the prefix for the namespace declared at the
   * index. Returns null if this is the default namespace
   * declaration
   *
   * @param index an <code>int</code> value
   * @return a <code>String</code> value
   */
  public String getNamespacePrefix(int index) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns an array which contains the characters from this
   * event. This array should be treated as read-only and
   * transient. I.e. the array will contain the text characters
   * until the XMLStreamReader moves on to the next
   * event. Attempts to hold onto the character array beyond
   * that time or modify the contents of the array are breaches
   * of the contract for this interface.
   *
   * @return a <code>char[]</code> value
   */
  public char[] getTextCharacters() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Gets the the text associated with a
   * <code>CHARACTERS</code>, <code>SPACE</code> or
   * <code>CDATA</code> event.
   *
   * @param sourceStart an <code>int</code> value
   * @param target a <code>char[]</code> value
   * @param targetStart an <code>int</code> value
   * @param length an <code>int</code> value
   * @return an <code>int</code> value
   * @exception XMLStreamException if an error occurs
   */
  public int getTextCharacters
      (int sourceStart, char[] target, int targetStart, int length)
      throws XMLStreamException {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the offset into the text character array where the
   * first character (of this text event) is stored.
   *
   * @return an <code>int</code> value
   */
  public int getTextStart() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Returns the length of the sequence of characters for this
   * Text event within the text character array.
   *
   * @return an <code>int</code> value
   */
  public int getTextLength() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Return <code>true</code> if the current event has text,
   * <code>false</code> otherwise.  The following events have
   * text: <code>CHARACTERS</code>, <code>DTD</code>,
   * <code>ENTITY_REFERENCE</code>, <code>COMMENT</code>,
   * <code>SPACE</code>.
   *
   * @return a <code>boolean</code> value
   */
  public boolean hasText() {
      int t = getEventType ();
      return t == XMLStreamConstants.CHARACTERS
    || t == XMLStreamConstants.DTD
    || t == XMLStreamConstants.ENTITY_REFERENCE
    || t == XMLStreamConstants.COMMENT
    || t == XMLStreamConstants.SPACE;
  }

  /**
   * Returns the (local) name of the current event. For
   * <code>START_ELEMENT</code> or <code>END_ELEMENT</code>
   * returns the (local) name of the current element. For
   * <code>ENTITY_REFERENCE</code> it returns entity name. The
   * current event must be <code>START_ELEMENT</code> or
   * <code>END_ELEMENT</code>, or <code>ENTITY_REFERENCE</code>
   *
   * @return a <code>String</code> value
   * @throws IllegalStateException if this not a
   * <code>START_ELEMENT</code>, <code>END_ELEMENT</code> or
   * <code>ENTITY_REFERENCE</code>
   */
  public String getLocalName() throws IllegalStateException {
      if (localName == null) {
    throw new IllegalStateException ();
      }
      return localName;
  }

  /**
   * returns <code>true</code> if the current event has a name
   * (is a <code>START_ELEMENT</code> or
   * <code>END_ELEMENT<(code>) returns <code>false</code>
   * otherwise.
   *
   * @return a <code>boolean</code> value
   */
  public boolean hasName() {
      int t = getEventType ();
      return t == XMLStreamConstants.START_ELEMENT
    || t == XMLStreamConstants.END_ELEMENT;
  }

  /**
   * Get the target of a processing instruction.
   *
   * @return a <code>String</code> value
   */
  public String getPITarget() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Get the data section of a processing instruction.
   *
   * @return a <code>String</code> value
   */
  public String getPIData() {
      throw new UnsupportedOperationException ();
  }

    }

    /**
     * Returns a string representation of the buffer's content. Note
     * that this method should not be used to generate XML as charcode
     * handling is partially undefined (dependant on the platform
     * configuration).
     * @return a terse (unindented) XML representation
     */
    public String toString() {
  return toString(false);
    }

    /**
     * Returns a string representation of the buffer's content. Note
     * that this method should not be used to generate XML as charcode
     * handling is partially undefined (dependant on the platform
     * configuration).
     * @param pretty if <code>true</code> output will be formated
     * using line breaks and indentation, thus improving readability
     * @return the XML representation
     */
    public String toString(boolean pretty) {
  try {
      SAXTransformerFactory stf = (SAXTransformerFactory)
    TransformerFactory.newInstance();
      TransformerHandler serializer = null;
      if (pretty) {
    serializer = stf.newTransformerHandler();
      } else {
    if (toStringTemplates == null) {
        InputStream xslIn
      = SAXContentBuffer.class.getResourceAsStream
      ("/de/danet/an/util/sax/remove-whitespace.xsl");
        toStringTemplates
      = stf.newTemplates(new StreamSource(xslIn));
    }
    serializer = stf.newTransformerHandler(toStringTemplates);
      }
      serializer.getTransformer().setOutputProperty
    (OutputKeys.INDENT, pretty ? "yes" : "no");
      serializer.getTransformer().setOutputProperty
    (OutputKeys.OMIT_XML_DECLARATION, "yes");
      StringWriter res = new StringWriter();
      serializer.setResult(new StreamResult(res));
      emit(serializer);
      return res.toString();
  } catch (Exception e) {
      return super.toString ();
  }
    }
   
    /**
     * Returns a W3C DOM representation of the buffer's content.
     * @return the DOM representation
     */
    public Node toW3cDom() {
        try {
            SAXTransformerFactory stf = (SAXTransformerFactory)
                TransformerFactory.newInstance();
            TransformerHandler serializer = stf.newTransformerHandler();
            DOMResult res = new DOMResult();
            serializer.setResult(res);
            emit(serializer);
            return res.getNode();
        } catch (Exception e) {
            throw (IllegalStateException)
                (new IllegalStateException ()).initCause(e);
        }
    }
   
}
TOP

Related Classes of de.danet.an.util.sax.SAXContentBuffer$SAXBufferStreamReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.