blob: 1ee08db9c5c08ac261aa49d9bbcc2632f97cfd85 [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.components.sax;
import java.util.HashMap;
import org.apache.cocoon.xml.XMLConsumer;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* This a simple xml compiler which outputs a byte array.
* If you want to reuse this instance, make sure to call {@link #recycle()}
* inbetween two compilation tasks.
*
* @version $Id$
*/
public final class XMLByteStreamCompiler implements XMLConsumer {
private HashMap map;
private int mapCount;
/** The buffer for the compile xml byte stream. */
private byte buf[];
/** The number of valid bytes in the buffer. */
private int bufCount;
private int bufCountAverage;
public XMLByteStreamCompiler() {
this.map = new HashMap();
this.bufCountAverage = 2000;
this.initOutput();
}
private void initOutput() {
this.mapCount = 0;
this.map.clear();
this.buf = new byte[bufCountAverage];
this.buf[0] = (byte)'C';
this.buf[1] = (byte)'X';
this.buf[2] = (byte)'M';
this.buf[3] = (byte)'L';
this.buf[4] = (byte)1;
this.buf[5] = (byte)0;
this.bufCount = 6;
}
public void recycle() {
bufCountAverage = (bufCountAverage + bufCount) / 2;
this.initOutput();
}
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;
public Object getSAXFragment() {
if (this.bufCount == 6) { // no event arrived yet
return null;
}
byte newbuf[] = new byte[this.bufCount];
System.arraycopy(this.buf, 0, newbuf, 0, this.bufCount);
return newbuf;
}
public void startDocument() throws SAXException {
this.writeEvent(START_DOCUMENT);
}
public void endDocument() throws SAXException {
this.writeEvent(END_DOCUMENT);
}
public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) throws SAXException {
this.writeEvent(START_PREFIX_MAPPING);
this.writeString(prefix);
this.writeString(uri);
}
public void endPrefixMapping(String prefix) throws SAXException {
this.writeEvent(END_PREFIX_MAPPING);
this.writeString(prefix);
}
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
int length = atts.getLength();
this.writeEvent(START_ELEMENT);
this.writeAttributes(length);
for (int i = 0; i < length; i++) {
this.writeString(atts.getURI(i));
this.writeString(atts.getLocalName(i));
this.writeString(atts.getQName(i));
this.writeString(atts.getType(i));
this.writeString(atts.getValue(i));
}
this.writeString((namespaceURI == null ? "" : namespaceURI));
this.writeString(localName);
this.writeString(qName);
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
this.writeEvent(END_ELEMENT);
this.writeString((namespaceURI == null ? "" : namespaceURI));
this.writeString(localName);
this.writeString(qName);
}
public void characters(char[] ch, int start, int length) throws SAXException {
this.writeEvent(CHARACTERS);
this.writeChars(ch, start, length);
}
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
this.writeEvent(IGNORABLE_WHITESPACE);
this.writeChars(ch, start, length);
}
public void processingInstruction(String target, String data) throws SAXException {
this.writeEvent(PROCESSING_INSTRUCTION);
this.writeString(target);
this.writeString(data);
}
public void setDocumentLocator(Locator locator) {
try {
this.writeEvent(LOCATOR);
String publicId = locator.getPublicId();
String systemId = locator.getSystemId();
this.writeString(publicId!=null?publicId:"");
this.writeString(systemId!=null?systemId:"");
this.write(locator.getLineNumber());
this.write(locator.getColumnNumber());
} catch (Exception e) {
throw new DocumentLocatorException("Error while handling locator", e);
}
}
public void skippedEntity(java.lang.String name) throws SAXException {
this.writeEvent(SKIPPED_ENTITY);
this.writeString(name);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void startDTD(String name, String publicId, String systemId) throws SAXException {
this.writeEvent(START_DTD);
this.writeString(name);
this.writeString(publicId!=null?publicId:"");
this.writeString(systemId!=null?systemId:"");
}
/**
* SAX Event Handling: LexicalHandler
*/
public void endDTD() throws SAXException {
this.writeEvent(END_DTD);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void startEntity(String name) throws SAXException {
this.writeEvent(START_ENTITY);
this.writeString(name);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void endEntity(String name) throws SAXException {
this.writeEvent(END_ENTITY);
this.writeString(name);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void startCDATA() throws SAXException {
this.writeEvent(START_CDATA);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void endCDATA() throws SAXException {
this.writeEvent(END_CDATA);
}
/**
* SAX Event Handling: LexicalHandler
*/
public void comment(char ary[], int start, int length) throws SAXException {
try {
this.writeEvent(COMMENT);
this.writeChars(ary, start, length);
} catch (Exception e) {
throw new SAXException(e);
}
}
public final void writeEvent( final int event) {
this.write(event);
}
public final void writeAttributes( final int attributes) throws SAXException {
if (attributes > 0xFFFF) throw new SAXException("Too many attributes");
this.write((attributes >>> 8) & 0xFF);
this.write((attributes >>> 0) & 0xFF);
}
public final void writeString( final String str) throws SAXException {
Integer index = (Integer) map.get(str);
if (index == null) {
map.put(str, new Integer(mapCount++));
int length = str.length();
this.writeChars(str.toCharArray(), 0, length);
}
else {
int i = index.intValue();
if (i > 0xFFFF) throw new SAXException("Index too large");
this.write(((i >>> 8) & 0xFF) | 0x80);
this.write((i >>> 0) & 0xFF);
}
}
public final void writeChars( final char[] ch, final int start, final int length) {
int utflen = 0;
int c;
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;
}
}
if (utflen >= 0x00007FFF) {
assure(bufCount + utflen + 6);
buf[bufCount++] = (byte)0x7F;
buf[bufCount++] = (byte)0xFF;
buf[bufCount++] = (byte) ((utflen >>> 24) & 0xFF);
buf[bufCount++] = (byte) ((utflen >>> 16) & 0xFF);
buf[bufCount++] = (byte) ((utflen >>> 8) & 0xFF);
buf[bufCount++] = (byte) ((utflen >>> 0) & 0xFF);
}
else {
assure(bufCount + utflen + 2);
buf[bufCount++] = (byte) ((utflen >>> 8) & 0xFF);
buf[bufCount++] = (byte) ((utflen >>> 0) & 0xFF);
}
for (int i = 0; i < length; i++) {
c = ch[i + start];
if ((c >= 0x0001) && (c <= 0x007F)) {
buf[bufCount++] = (byte) c;
}
else if (c > 0x07FF) {
buf[bufCount++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
buf[bufCount++] = (byte) (0x80 | ((c >> 6) & 0x3F));
buf[bufCount++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
else {
buf[bufCount++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
buf[bufCount++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
/*
if (length == 0) return;
assure( (int) (buf.length + length * utfRatioAverage) );
int utflentotal = 0;
bufCount += 2;
int bufStart = bufCount;
for (int i = 0; i < length; i++) {
int c = ch[i + start];
int l = bufCount-bufStart;
if (l+3 >= 0x7FFF) {
buf[bufStart-2] = (byte) ((l >>> 8) & 0xFF);
buf[bufStart-1] = (byte) ((l >>> 0) & 0xFF);
utflentotal += l;
bufCount += 2;
bufStart = bufCount;
}
if ((c >= 0x0001) && (c <= 0x007F)) {
assure(bufCount+1);
buf[bufCount++] = (byte)c;
}
else if (c > 0x07FF) {
assure(bufCount+3);
buf[bufCount++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
buf[bufCount++] = (byte) (0x80 | ((c >> 6) & 0x3F));
buf[bufCount++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
else {
assure(bufCount+2);
buf[bufCount++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
buf[bufCount++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
int l = bufCount-bufStart;
buf[bufStart-2] = (byte) ((l >>> 8) & 0xFF);
buf[bufStart-1] = (byte) ((l >>> 0) & 0xFF);
utflentotal += l;
utfRatioAverage = (utfRatioAverage + (utflentotal / length) / 2);
*/
}
/* JH (2003-11-20): seems to be never used
private void write( final byte[] b ) {
int newcount = this.bufCount + b.length;
assure(newcount);
System.arraycopy(b, 0, this.buf, this.bufCount, b.length);
this.bufCount = newcount;
}
*/
private void write( final int b ) {
int newcount = this.bufCount + 1;
assure(newcount);
this.buf[this.bufCount] = (byte)b;
this.bufCount = newcount;
}
private void assure( final int size ) {
if (size > this.buf.length) {
byte newbuf[] = new byte[Math.max(this.buf.length << 1, size)];
System.arraycopy(this.buf, 0, newbuf, 0, this.bufCount);
this.buf = newbuf;
}
}
}