blob: 160ba93fb5580645b1a79fb9ce6880bc5e115d32 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.xml;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.ext.LexicalHandler;
import org.apache.excalibur.xml.sax.XMLizable;
import org.apache.avalon.excalibur.pool.Recyclable;
import java.io.Serializable;
import java.io.Writer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
/**
* A class that can record SAX events and replay them later.
*
* <p>Compared to {@link org.apache.cocoon.components.sax.XMLByteStreamCompiler},
* this class is many times faster at sending out the recorded SAX events since
* it doesn't need to convert between byte and char representations etc.
* On the other hand, its data structure is more complex then a simple byte array,
* making XMLByteStreamCompiler better in case the recorded SAX should be stored long-term.
*
* <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$
*/
public class SaxBuffer extends AbstractSAXFragment
implements XMLConsumer, Recyclable, Serializable {
/**
* Stores list of {@link SaxBit} objects.
*/
protected List saxbits;
/**
* Creates empty SaxBuffer
*/
public SaxBuffer() {
this.saxbits = new ArrayList();
}
/**
* Creates SaxBuffer based on the provided bits list.
*/
public SaxBuffer(List bits) {
this.saxbits = bits;
}
/**
* Creates copy of another SaxBuffer
*/
public SaxBuffer(SaxBuffer saxBuffer) {
this.saxbits = new ArrayList(saxBuffer.saxbits);
}
//
// ContentHandler Interface
//
public void skippedEntity(String name) throws SAXException {
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 {
saxbits.add(new IgnorableWhitespace(ch, start, length));
}
public void processingInstruction(String target, String data) throws SAXException {
saxbits.add(new PI(target, data));
}
public void startDocument() throws SAXException {
saxbits.add(StartDocument.SINGLETON);
}
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
saxbits.add(new StartElement(namespaceURI, localName, qName, atts));
}
public void endPrefixMapping(String prefix) throws SAXException {
saxbits.add(new EndPrefixMapping(prefix));
}
public void characters(char ch[], int start, int length) throws SAXException {
saxbits.add(new Characters(ch, start, length));
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
saxbits.add(new EndElement(namespaceURI, localName, qName));
}
public void endDocument() throws SAXException {
saxbits.add(EndDocument.SINGLETON);
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
saxbits.add(new StartPrefixMapping(prefix, uri));
}
//
// LexicalHandler Interface
//
public void endCDATA() throws SAXException {
saxbits.add(EndCDATA.SINGLETON);
}
public void comment(char ch[], int start, int length) throws SAXException {
saxbits.add(new Comment(ch, start, length));
}
public void startEntity(String name) throws SAXException {
saxbits.add(new StartEntity(name));
}
public void endDTD() throws SAXException {
saxbits.add(EndDTD.SINGLETON);
}
public void startDTD(String name, String publicId, String systemId) throws SAXException {
saxbits.add(new StartDTD(name, publicId, systemId));
}
public void startCDATA() throws SAXException {
saxbits.add(StartCDATA.SINGLETON);
}
public void endEntity(String name) throws SAXException {
saxbits.add(new EndEntity(name));
}
//
// Public Methods
//
/**
* Add a bit containing XMLizable object
*/
public void xmlizable(XMLizable xml) {
saxbits.add(new XMLizableBit(xml));
}
/**
* @return true if buffer is empty
*/
public boolean isEmpty() {
return saxbits.isEmpty();
}
/**
* @return unmodifiable list of SAX bits
*/
public List getBits() {
return Collections.unmodifiableList(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 (Iterator i = saxbits.iterator(); i.hasNext();) {
SaxBit saxbit = (SaxBit)i.next();
saxbit.send(contentHandler);
}
}
/**
* @return String value of the buffer
*/
public String toString() {
// NOTE: This method is used in i18n XML bundle implementation
final StringBuffer value = new StringBuffer();
for (Iterator i = saxbits.iterator(); i.hasNext();) {
final SaxBit saxbit = (SaxBit) i.next();
if (saxbit instanceof Characters) {
((Characters) saxbit).toString(value);
}
}
return value.toString();
}
/**
* Clear this buffer
*/
public void recycle() {
saxbits.clear();
}
/**
* Dump buffer contents into the provided writer.
*/
public void dump(Writer writer) throws IOException {
Iterator i = saxbits.iterator();
while (i.hasNext()) {
final SaxBit saxbit = (SaxBit) i.next();
saxbit.dump(writer);
}
writer.flush();
}
//
// Implementation Methods
//
/**
* Adds a SaxBit to the bits list
*/
protected final void addBit(SaxBit bit) {
saxbits.add(bit);
}
/**
* Iterates through the bits list
*/
protected final Iterator bits() {
return saxbits.iterator();
}
/**
* SaxBit is a representation of the SAX event. Every SaxBit is immutable object.
*/
interface SaxBit {
public void send(ContentHandler contentHandler) throws SAXException;
public void dump(Writer writer) throws IOException;
}
public final static class StartDocument implements SaxBit, Serializable {
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");
}
}
public final static class EndDocument implements SaxBit, Serializable {
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");
}
}
public final static class PI implements SaxBit, Serializable {
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(target, data);
}
public void dump(Writer writer) throws IOException {
writer.write("[ProcessingInstruction] target=" + target + ",data=" + data + "\n");
}
}
public final static class StartDTD implements SaxBit, Serializable {
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(name, publicId, systemId);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartDTD] name=" + name + ",publicId=" + publicId + ",systemId=" + systemId + "\n");
}
}
public final static class EndDTD implements SaxBit, Serializable {
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");
}
}
public final static class StartEntity implements SaxBit, Serializable {
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(name);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartEntity] name=" + name + "\n");
}
}
public final static class EndEntity implements SaxBit, Serializable {
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(name);
}
public void dump(Writer writer) throws IOException {
writer.write("[EndEntity] name=" + name + "\n");
}
}
public final static class SkippedEntity implements SaxBit, Serializable {
public final String name;
public SkippedEntity(String name) {
this.name = name;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.skippedEntity(name);
}
public void dump(Writer writer) throws IOException {
writer.write("[SkippedEntity] name=" + name + "\n");
}
}
public final static class StartPrefixMapping implements SaxBit, Serializable {
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(prefix, uri);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartPrefixMapping] prefix=" + prefix + ",uri=" + uri + "\n");
}
}
public final static class EndPrefixMapping implements SaxBit, Serializable {
public final String prefix;
public EndPrefixMapping(String prefix) {
this.prefix = prefix;
}
public void send(ContentHandler contentHandler) throws SAXException {
contentHandler.endPrefixMapping(prefix);
}
public void dump(Writer writer) throws IOException {
writer.write("[EndPrefixMapping] prefix=" + prefix + "\n");
}
}
public final static class StartElement implements SaxBit, Serializable {
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(namespaceURI, localName, qName, attrs);
}
public void dump(Writer writer) throws IOException {
writer.write("[StartElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n");
for (int i = 0; i < attrs.getLength(); i++) {
writer.write(" [Attribute] namespaceURI=" + attrs.getURI(i) + ",localName=" + attrs.getLocalName(i) + ",qName=" + attrs.getQName(i) + ",type=" + attrs.getType(i) + ",value=" + attrs.getValue(i) + "\n");
}
}
}
public final static class EndElement implements SaxBit, Serializable {
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(namespaceURI, localName, qName);
}
public void dump(Writer writer) throws IOException {
writer.write("[EndElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n");
}
}
public final static class Characters implements SaxBit, Serializable {
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(ch, 0, ch.length);
}
public void toString(StringBuffer value) {
value.append(ch);
}
public void dump(Writer writer) throws IOException {
writer.write("[Characters] ch=" + new String(ch) + "\n");
}
}
public final static class Comment implements SaxBit, Serializable {
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(ch, 0, ch.length);
}
public void dump(Writer writer) throws IOException {
writer.write("[Comment] ch=" + new String(ch) + "\n");
}
}
public final static class StartCDATA implements SaxBit, Serializable {
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");
}
}
public final static class EndCDATA implements SaxBit, Serializable {
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");
}
}
public final static class IgnorableWhitespace implements SaxBit, Serializable {
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(ch, 0, ch.length);
}
public void dump(Writer writer) throws IOException {
writer.write("[IgnorableWhitespace] ch=" + new String(ch) + "\n");
}
}
public final static class XMLizableBit implements SaxBit, Serializable {
public final XMLizable xml;
public XMLizableBit(XMLizable xml) {
this.xml = xml;
}
public void send(ContentHandler contentHandler) throws SAXException {
this.xml.toSAX(new EmbeddedXMLPipe(contentHandler));
}
public void dump(Writer writer) throws IOException {
if (xml instanceof SaxBuffer) {
writer.write("[XMLizable] Begin nested SaxBuffer\n");
((SaxBuffer)xml).dump(writer);
writer.write("[XMLizable] End nested SaxBuffer\n");
} else {
writer.write("[XMLizable] xml=" + xml + "\n");
}
}
}
}