package com.nominanuda.xml;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
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;
/**
* A class that can record SAX events and replay them later.
*
* <p>
* Use this class if you need to frequently generate smaller amounts of SAX
* events, or replay a set of recorded start events immediately.
* </p>
*
* <p>
* Both {@link ContentHandler} and {@link LexicalHandler} are supported, the
* only exception is that the setDocumentLocator event is not recorded.
* </p>
*
* @version $Id: SaxBuffer.java 696943 2008-09-19 06:56:50Z reinhard $
*/
public class SaxBuffer implements ContentHandler, LexicalHandler, Serializable,
SAXEmitter {
private static final long serialVersionUID = 535933679567154884L;
/**
* Stores list of {@link SaxBit} objects.
*/
protected List<SaxBit> saxbits;
/**
* Creates empty SaxBuffer
*/
public SaxBuffer() {
this.saxbits = new ArrayList<SaxBit>();
}
/**
* Creates SaxBuffer based on the provided bits list.
*/
public SaxBuffer(List<SaxBit> bits) {
this.saxbits = bits;
}
/**
* Creates copy of another SaxBuffer
*/
public SaxBuffer(SaxBuffer saxBuffer) {
this.saxbits = new ArrayList<SaxBit>(saxBuffer.saxbits);
}
//
// ContentHandler Interface
//
public void skippedEntity(String name) throws SAXException {
this.saxbits.add(new SkippedEntity(name));
}
public void setDocumentLocator(Locator locator) {
// Don't record this event
}
public void ignorableWhitespace(char ch[], int start, int length)
throws SAXException {
this.saxbits.add(new IgnorableWhitespace(ch, start, length));
}
public void processingInstruction(String target, String data)
throws SAXException {
this.saxbits.add(new PI(target, data));
}
public void startDocument() throws SAXException {
this.saxbits.add(StartDocument.SINGLETON);
}
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {
this.saxbits
.add(new StartElement(namespaceURI, localName, qName, atts));
}
public void endPrefixMapping(String prefix) throws SAXException {
this.saxbits.add(new EndPrefixMapping(prefix));
}
public void characters(char ch[], int start, int length)
throws SAXException {
this.saxbits.add(new Characters(ch, start, length));
}
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
this.saxbits.add(new EndElement(namespaceURI, localName, qName));
}
public void endDocument() throws SAXException {
this.saxbits.add(EndDocument.SINGLETON);
}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
this.saxbits.add(new StartPrefixMapping(prefix, uri));
}
//
// LexicalHandler Interface
//
public void endCDATA() throws SAXException {
this.saxbits.add(EndCDATA.SINGLETON);
}
public void comment(char ch[], int start, int length) throws SAXException {
this.saxbits.add(new Comment(ch, start, length));
}
public void startEntity(String name) throws SAXException {
this.saxbits.add(new StartEntity(name));
}
public void endDTD() throws SAXException {
this.saxbits.add(EndDTD.SINGLETON);
}
public void startDTD(String name, String publicId, String systemId)
throws SAXException {
this.saxbits.add(new StartDTD(name, publicId, systemId));
}
public void startCDATA() throws SAXException {
this.saxbits.add(StartCDATA.SINGLETON);
}
public void endEntity(String name) throws SAXException {
this.saxbits.add(new EndEntity(name));
}
//
// Public Methods
//
/**
* @return true if buffer is empty
*/
public boolean isEmpty() {
return this.saxbits.isEmpty();
}
/**
* @return unmodifiable list of SAX bits
*/
public List<SaxBit> getBits() {
return Collections.unmodifiableList(this.saxbits);
}
/**
* Stream this buffer into the provided content handler. If contentHandler
* object implements LexicalHandler, it will get lexical events as well.
*/
public void toSAX(ContentHandler contentHandler) throws SAXException {
for (SaxBit saxbit : this.saxbits) {
saxbit.send(contentHandler);
}
}
/**
* Dump buffer contents into the provided writer.
*/
public void dump(Writer writer) throws IOException {
Iterator<SaxBit> i = this.saxbits.iterator();
while (i.hasNext()) {
final SaxBit saxbit = i.next();
saxbit.dump(writer);
}
writer.flush();
}
//
// Implementation Methods
//
/**
* Adds a SaxBit to the bits list
*/
protected final void addBit(SaxBit bit) {
this.saxbits.add(bit);
}
/**
* Iterates through the bits list
*/
protected final Iterator<SaxBit> bits() {
return this.saxbits.iterator();
}
/**
* SaxBit is a representation of the SAX event. Every SaxBit is immutable
* object.
*/
public interface SaxBit {
public void send(ContentHandler contentHandler) throws SAXException;
public void dump(Writer writer) throws IOException;
}
public final static class StartDocument implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343331395228L;
public static final StartDocument SINGLETON = new StartDocument();
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.startDocument();
}
public void dump(Writer writer) throws IOException {
writer.write("[StartDocument]\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndDocument implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343331395123L;
public static final EndDocument SINGLETON = new EndDocument();
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.endDocument();
}
public void dump(Writer writer) throws IOException {
writer.write("[EndDocument]\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class PI implements SaxBit, Serializable {
private static final long serialVersionUID = -443116534333139556L;
public final String target;
public final String data;
public PI(String target, String data) {
this.target = target;
this.data = data;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.processingInstruction(this.target, this.data);
}
public void dump(Writer writer) throws IOException {
writer.write("[ProcessingInstruction] target=" + this.target
+ ",data=" + this.data + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class StartDTD implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343331567228L;
public final String name;
public final String publicId;
public final String systemId;
public StartDTD(String name, String publicId, String systemId) {
this.name = name;
this.publicId = publicId;
this.systemId = systemId;
}
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).startDTD(this.name,
this.publicId, this.systemId);
}
}
public void dump(Writer writer) throws IOException {
writer.write("[StartDTD] name=" + this.name + ",publicId="
+ this.publicId + ",systemId=" + this.systemId + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndDTD implements SaxBit, Serializable {
private static final long serialVersionUID = -4144565343331395228L;
public static final EndDTD SINGLETON = new EndDTD();
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).endDTD();
}
}
public void dump(Writer writer) throws IOException {
writer.write("[EndDTD]\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class StartEntity implements SaxBit, Serializable {
private static final long serialVersionUID = -4431169193331395228L;
public final String name;
public StartEntity(String name) {
this.name = name;
}
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).startEntity(this.name);
}
}
public void dump(Writer writer) throws IOException {
writer.write("[StartEntity] name=" + this.name + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndEntity implements SaxBit, Serializable {
private static final long serialVersionUID = -4431167863331395228L;
public final String name;
public EndEntity(String name) {
this.name = name;
}
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).endEntity(this.name);
}
}
public void dump(Writer writer) throws IOException {
writer.write("[EndEntity] name=" + this.name + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class SkippedEntity implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343555395228L;
public final String name;
public SkippedEntity(String name) {
this.name = name;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.skippedEntity(this.name);
}
public void dump(Writer writer) throws IOException {
writer.write("[SkippedEntity] name=" + this.name + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class StartPrefixMapping implements SaxBit,
Serializable {
private static final long serialVersionUID = -4436665343331395228L;
public final String prefix;
public final String uri;
public StartPrefixMapping(String prefix, String uri) {
this.prefix = prefix;
this.uri = uri;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.startPrefixMapping(this.prefix, this.uri);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartPrefixMapping] prefix=" + this.prefix + ",uri="
+ this.uri + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndPrefixMapping implements SaxBit, Serializable {
private static final long serialVersionUID = -4499965343331395228L;
public final String prefix;
public EndPrefixMapping(String prefix) {
this.prefix = prefix;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.endPrefixMapping(this.prefix);
}
public void dump(Writer writer) throws IOException {
writer.write("[EndPrefixMapping] prefix=" + this.prefix + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class StartElement implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343377795228L;
public final String namespaceURI;
public final String localName;
public final String qName;
public final Attributes attrs;
public StartElement(String namespaceURI, String localName,
String qName, Attributes attrs) {
this.namespaceURI = namespaceURI;
this.localName = localName;
this.qName = qName;
this.attrs = new org.xml.sax.helpers.AttributesImpl(attrs);
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.startElement(this.namespaceURI, this.localName,
this.qName, this.attrs);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartElement] namespaceURI=" + this.namespaceURI
+ ",localName=" + this.localName + ",qName=" + this.qName
+ "\n");
for (int i = 0; i < this.attrs.getLength(); i++) {
writer.write(" [Attribute] namespaceURI="
+ this.attrs.getURI(i) + ",localName="
+ this.attrs.getLocalName(i) + ",qName="
+ this.attrs.getQName(i) + ",type="
+ this.attrs.getType(i) + ",value="
+ this.attrs.getValue(i) + "\n");
}
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndElement implements SaxBit, Serializable {
private static final long serialVersionUID = -4439845343331395228L;
public final String namespaceURI;
public final String localName;
public final String qName;
public EndElement(String namespaceURI, String localName, String qName) {
this.namespaceURI = namespaceURI;
this.localName = localName;
this.qName = qName;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.endElement(this.namespaceURI, this.localName,
this.qName);
}
public void dump(Writer writer) throws IOException {
writer.write("[EndElement] namespaceURI=" + this.namespaceURI
+ ",localName=" + this.localName + ",qName=" + this.qName
+ "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class Characters implements SaxBit, Serializable {
private static final long serialVersionUID = -4438945343331395228L;
public final char[] ch;
public Characters(char[] ch, int start, int length) {
// make a copy so that we don't hold references to a potentially
// large array we don't control
this.ch = new char[length];
System.arraycopy(ch, start, this.ch, 0, length);
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.characters(this.ch, 0, this.ch.length);
}
public void toString(StringBuffer value) {
value.append(this.ch);
}
public void dump(Writer writer) throws IOException {
writer.write("[Characters] ch=" + new String(this.ch) + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class Comment implements SaxBit, Serializable {
private static final long serialVersionUID = -4491265343331395228L;
public final char[] ch;
public Comment(char[] ch, int start, int length) {
// make a copy so that we don't hold references to a potentially
// large array we don't control
this.ch = new char[length];
System.arraycopy(ch, start, this.ch, 0, length);
}
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).comment(this.ch, 0,
this.ch.length);
}
}
public void dump(Writer writer) throws IOException {
writer.write("[Comment] ch=" + new String(this.ch) + "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class StartCDATA implements SaxBit, Serializable {
private static final long serialVersionUID = -4431165343331347829L;
public static final StartCDATA SINGLETON = new StartCDATA();
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).startCDATA();
}
}
public void dump(Writer writer) throws IOException {
writer.write("[StartCDATA]\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class EndCDATA implements SaxBit, Serializable {
private static final long serialVersionUID = -1111165343331395228L;
public static final EndCDATA SINGLETON = new EndCDATA();
public void send(ContentHandler contentHandler) throws SAXException {
if (contentHandler instanceof LexicalHandler) {
((LexicalHandler) contentHandler).endCDATA();
}
}
public void dump(Writer writer) throws IOException {
writer.write("[EndCDATA]\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public final static class IgnorableWhitespace implements SaxBit,
Serializable {
private static final long serialVersionUID = -4431194143331395228L;
public final char[] ch;
public IgnorableWhitespace(char[] ch, int start, int length) {
// make a copy so that we don't hold references to a potentially
// large array we don't control
this.ch = new char[length];
System.arraycopy(ch, start, this.ch, 0, length);
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.ignorableWhitespace(this.ch, 0, this.ch.length);
}
public void dump(Writer writer) throws IOException {
writer.write("[IgnorableWhitespace] ch=" + new String(this.ch)
+ "\n");
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
dump(sw);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
}
public static boolean isStartElement(SaxBit bit) {
return bit instanceof StartElement;
}
public static boolean isEndElement(SaxBit bit) {
return bit instanceof EndElement;
}
public static boolean isStartDocument(SaxBit bit) {
return bit instanceof StartDocument;
}
public static boolean isEndDocument(SaxBit bit) {
return bit instanceof EndDocument;
}
}