blob: 55ec22636763e7835140594a286c80ffc24bf5d3 [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES{} LOSS OF
* USE, DATA, OR PROFITS{} OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Santiago Pericas-Geertsen
* @author G. Todd Miller
*
*/
package org.apache.xalan.xsltc.runtime.output;
import java.util.Stack;
import java.io.IOException;
import org.xml.sax.ContentHandler;
import org.xml.sax.ext.LexicalHandler;
import org.apache.xalan.xsltc.TransletException;
import org.apache.xalan.xsltc.runtime.Hashtable;
import org.xml.sax.SAXException;
import org.apache.xalan.xsltc.runtime.BasisLibrary;
public class SAXXMLOutput extends SAXOutput {
private static final char[] BEGCDATA = "<![CDATA[".toCharArray();
private static final char[] ENDCDATA = "]]>".toCharArray();
private static final char[] CNTCDATA = "]]]]><![CDATA[>".toCharArray();
public SAXXMLOutput(ContentHandler handler, String encoding)
throws IOException
{
super(handler, encoding);
initCDATA();
initNamespaces();
}
public SAXXMLOutput(ContentHandler handler, LexicalHandler lex,
String encoding) throws IOException
{
super(handler, lex, encoding);
initCDATA();
initNamespaces();
}
public void endDocument() throws TransletException {
try {
// Close any open start tag
if (_startTagOpen) {
closeStartTag();
}
else if (_cdataTagOpen) {
closeCDATA();
}
// Close output document
_saxHandler.endDocument();
}
catch (SAXException e) {
throw new TransletException(e);
}
}
/**
* Start an element in the output document. This might be an XML
* element (<elem>data</elem> type) or a CDATA section.
*/
public void startElement(String elementName) throws TransletException {
// System.out.println("SAXXMLOutput.startElement name = " + elementName);
try {
// Close any open start tag
if (_startTagOpen) {
closeStartTag();
}
else if (_cdataTagOpen) {
closeCDATA();
}
// Handle document type declaration (for first element only)
if (_firstElement) {
if (_doctypeSystem != null) {
_lexHandler.startDTD(elementName, _doctypePublic,
_doctypeSystem);
}
_firstElement = false;
}
_depth++;
_elementName = elementName;
_attributes.clear();
_startTagOpen = true;
}
catch (SAXException e) {
throw new TransletException(e);
}
}
/**
* Put an attribute and its value in the start tag of an element.
* Signal an exception if this is attempted done outside a start tag.
*/
public void attribute(String name, final String value)
throws TransletException
{
final String patchedName = patchName(name);
final String localName = getLocalName(patchedName);
final String uri = getNamespaceURI(patchedName, false);
final int index = (localName == null) ?
_attributes.getIndex(name) : /* don't use patchedName */
_attributes.getIndex(uri, localName);
if (!_startTagOpen) {
BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR,
patchedName);
}
if (index >= 0) { // Duplicate attribute?
_attributes.setAttribute(index, uri, localName,
patchedName, "CDATA", value);
}
else {
_attributes.addAttribute(uri, localName, patchedName,
"CDATA", value);
}
}
public void characters(char[] ch, int off, int len)
throws TransletException
{
// System.out.println("SAXXMLOutput.characters ch = " + new String(ch, off, len));
try {
// Close any open start tag
if (_startTagOpen) {
closeStartTag();
}
Integer I = (Integer)_cdataStack.peek();
if ((I.intValue() == _depth) && (!_cdataTagOpen)) {
startCDATA(ch, off, len);
}
else {
_saxHandler.characters(ch, off, len);
}
}
catch (SAXException e) {
throw new TransletException(e);
}
}
public void endElement(String elementName) throws TransletException {
// System.out.println("SAXXMLOutput.endElement name = " + elementName);
try {
// Close any open element
if (_startTagOpen) {
closeStartTag();
}
else if (_cdataTagOpen) {
closeCDATA();
}
_saxHandler.endElement(getNamespaceURI(elementName, true),
getLocalName(elementName), elementName);
popNamespaces();
if (((Integer)_cdataStack.peek()).intValue() == _depth){
_cdataStack.pop();
}
_depth--;
}
catch (SAXException e) {
throw new TransletException(e);
}
}
/**
* Send a namespace declaration in the output document. The namespace
* declaration will not be include if the namespace is already in scope
* with the same prefix.
*/
public void namespace(final String prefix, final String uri)
throws TransletException
{
if (_startTagOpen) {
pushNamespace(prefix, uri);
}
else {
if ((prefix == EMPTYSTRING) && (uri == EMPTYSTRING)) return;
BasisLibrary.runTimeError(BasisLibrary.STRAY_NAMESPACE_ERR,
prefix, uri);
}
}
/**
* Send a processing instruction to the output document
*/
public void processingInstruction(String target, String data)
throws TransletException {
try {
// Close any open element
if (_startTagOpen) {
closeStartTag();
}
else if (_cdataTagOpen) {
closeCDATA();
}
// Pass the processing instruction to the SAX handler
_saxHandler.processingInstruction(target, data);
}
catch (SAXException e) {
throw new TransletException(e);
}
}
/**
* Declare a prefix to point to a namespace URI. Inform SAX handler
* if this is a new prefix mapping.
*/
protected boolean pushNamespace(String prefix, String uri) {
try {
if (super.pushNamespace(prefix, uri)) {
_saxHandler.startPrefixMapping(prefix, uri);
return true;
}
}
catch (SAXException e) {
// falls through
}
return false;
}
/**
* Undeclare the namespace that is currently pointed to by a given
* prefix. Inform SAX handler if prefix was previously mapped.
*/
protected boolean popNamespace(String prefix) {
try {
if (super.popNamespace(prefix)) {
_saxHandler.endPrefixMapping(prefix);
return true;
}
}
catch (SAXException e) {
// falls through
}
return false;
}
/**
* This method is called when all the data needed for a call to the
* SAX handler's startElement() method has been gathered.
*/
protected void closeStartTag() throws TransletException {
try {
_startTagOpen = false;
final String localName = getLocalName(_elementName);
final String uri = getNamespaceURI(_elementName, true);
// Now is time to send the startElement event
_saxHandler.startElement(uri, localName, _elementName,
_attributes);
if (_cdata != null) {
final StringBuffer expandedName = (uri == EMPTYSTRING) ?
new StringBuffer(_elementName) :
new StringBuffer(uri).append(':').append(localName);
if (_cdata.containsKey(expandedName.toString())) {
_cdataStack.push(new Integer(_depth));
}
}
}
catch (SAXException e) {
throw new TransletException(e);
}
}
protected void closeCDATA() throws SAXException {
// Output closing bracket - "]]>"
_saxHandler.characters(ENDCDATA, 0, ENDCDATA.length);
_cdataTagOpen = false;
}
/**
* Utility method - pass a whole charactes as CDATA to SAX handler
*/
private void startCDATA(char[] ch, int off, int len) throws SAXException {
final int limit = off + len;
int offset = off;
// Output start bracket - "<![CDATA["
_saxHandler.characters(BEGCDATA, 0, BEGCDATA.length);
// Detect any occurence of "]]>" in the character array
for (int i = offset; i < limit - 2; i++) {
if (ch[i] == ']' && ch[i + 1] == ']' && ch[i + 2] == '>') {
_saxHandler.characters(ch, offset, i - offset);
_saxHandler.characters(CNTCDATA, 0, CNTCDATA.length);
offset = i+3;
i += 2; // Skip next chars ']' and '>'
}
}
// Output the remaining characters
if (offset < limit) {
_saxHandler.characters(ch, offset, limit - offset);
}
_cdataTagOpen = true;
}
}