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