blob: 737f57f3882d522afb2a2e7ef455d6cda6f54ac7 [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 Jacek Ambroziak
* @author Santiago Pericas-Geertsen
* @author Morten Jorgensen
* @author G. Todd Miller
*/
package org.apache.xalan.xsltc.runtime;
import java.util.Vector;
import java.util.Enumeration;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import org.apache.xalan.xsltc.*;
import org.apache.xalan.xsltc.util.IntegerArray;
import org.apache.xalan.xsltc.dom.DOMAdapter;
import org.apache.xalan.xsltc.dom.DOMImpl;
import org.apache.xalan.xsltc.dom.StripWhitespaceFilter;
import org.apache.xalan.xsltc.dom.KeyIndex;
// GTM added all these
import org.apache.xalan.xsltc.runtime.DefaultSAXOutputHandler;
import javax.xml.transform.Transformer;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.ErrorListener;
import java.lang.IllegalArgumentException;
import java.util.Properties;
import java.lang.IllegalArgumentException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import org.xml.sax.XMLReader;
import org.apache.xalan.xsltc.dom.DTDMonitor;
import java.io.File;
import java.io.Writer;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
// END.
public abstract class AbstractTranslet extends Transformer implements Translet {
protected String _encoding = "utf-8";
//!!! to be added only as needed
protected StringValueHandler stringValueHandler = new StringValueHandler();
//!! different handshake needed
protected String[] namesArray;
protected String[] namespaceArray;
//!!! Temporary approach. To be added to translet only when needed. !!!
public Hashtable _formatSymbols = new Hashtable();
public Hashtable _unparsedEntities = null;
// Container for all indexes for xsl:key elements
private Hashtable _keyIndexes = new Hashtable();
private KeyIndex _emptyKeyIndex = new KeyIndex(1);
private int _indexSize = 0;
private MessageHandler _msgHandler = null;
private DOMCache _domCache = null;
private final static String EMPTYSTRING = "";
public final DOMAdapter makeDOMAdapter(DOM dom)
throws TransletException {
if (dom instanceof DOMImpl) {
return new DOMAdapter((DOMImpl)dom, namesArray, namespaceArray);
}
else {
throw new TransletException("wrong type of source DOM");
}
}
public final void setMessageHandler(MessageHandler handler) {
_msgHandler = handler;
}
public final void displayMessage(String msg) {
if (_msgHandler == null) {
System.err.println(msg);
}
else {
_msgHandler.displayMessage(msg);
}
}
/**
* Variable's stack: <tt>vbase</tt> and <tt>vframe</tt> are used
* to denote the current variable frame.
*/
protected int vbase = 0, vframe = 0;
protected Vector varsStack = new Vector();
/**
* Parameter's stack: <tt>pbase</tt> and <tt>pframe</tt> are used
* to denote the current parameter frame.
*/
protected int pbase = 0, pframe = 0;
protected Vector paramsStack = new Vector();
/**
* Push a new parameter frame.
*/
public final void pushParamFrame() {
paramsStack.insertElementAt(new Integer(pbase), pframe);
pbase = ++pframe;
}
/**
* Pop the topmost parameter frame.
*/
public final void popParamFrame() {
if (pbase > 0) {
pframe = pbase - 1;
pbase = ((Integer) paramsStack.elementAt(pframe)).intValue();
}
}
/**
* Add a new parameter if not already in the current frame.
* Returns the old value if already defined and the new value
* if added.
*/
public final Object addParameter(String name, Object value) {
for (int i = pframe - 1; i >= pbase; i--) {
final Parameter param = (Parameter) paramsStack.elementAt(i);
if (param.name.equals(name)) {
return param.value;
}
}
paramsStack.insertElementAt(new Parameter(name, value), pframe++);
return value;
}
/**
* Get the value of a parameter from the current frame or
* <tt>null</tt> if undefined.
*/
public final Object getParameter(String name) {
for (int i = pframe - 1; i >= pbase; i--) {
final Parameter param = (Parameter)paramsStack.elementAt(i);
if (param.name.equals(name)) {
return param.value;
}
}
return null;
}
/**
* Push a new variable frame.
*/
public final void pushVarFrame(int frameSize) {
varsStack.insertElementAt(new Integer(vbase), vframe);
vbase = ++vframe;
vframe += frameSize;
}
/**
* Pop the topmost variable frame.
*/
public final void popVarFrame() {
if (vbase > 0) {
vframe = vbase - 1;
vbase = ((Integer)varsStack.elementAt(vframe)).intValue();
}
}
/**
* Get the value of a variable given its index.
*/
public final Object getVariable(int vindex) {
return varsStack.elementAt(vbase + vindex);
}
/**
* Set the value of a variable in the current frame.
*/
public final void addVariable(int vindex, Object value) {
varsStack.insertElementAt(value, vbase + vindex);
}
/**
* Adds a DecimalFormat object to the _formatSymbols hashtable.
* The entry is created with the input DecimalFormatSymbols.
*/
public void addDecimalFormat(String name, DecimalFormatSymbols symbols) {
// Remove any existing entries with the same name.
if (name == null) name = EMPTYSTRING;
_formatSymbols.remove(name);
// Construct a DecimalFormat object containing the symbols we got
final DecimalFormat df = new DecimalFormat();
if (symbols != null) {
df.setDecimalFormatSymbols(symbols);
}
_formatSymbols.put(name, df);
}
/**
* Retrieves a named DecimalFormat object from _formatSymbols hashtable.
*/
public final DecimalFormat getDecimalFormat(String name) {
if (name == null) name = EMPTYSTRING;
final DecimalFormat df = (DecimalFormat)_formatSymbols.get(name);
if (df == null) {
// This should really result in an error
return((DecimalFormat)_formatSymbols.get(EMPTYSTRING));
}
else {
return df;
}
}
public final void addUnparsedEntity(String name, String uri) {
if (_unparsedEntities == null)
_unparsedEntities = new Hashtable();
if (_unparsedEntities.containsKey(name) == false)
_unparsedEntities.put(name, uri);
}
public final String getUnparsedEntity(String name) {
final String uri = (String)_unparsedEntities.get(name);
return uri == null ? EMPTYSTRING : uri;
}
public final void setDTDMonitor(DTDMonitor monitor) {
setUnparsedEntityURIs(monitor.getUnparsedEntityURIs());
}
public final void setUnparsedEntityURIs(Hashtable table) {
if (_unparsedEntities == null)
_unparsedEntities = table;
else {
Enumeration keys = table.keys();
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
_unparsedEntities.put(name,table.get(name));
}
}
}
public abstract void transform(DOM document, NodeIterator iterator,
TransletOutputHandler handler)
throws TransletException;
protected TransletOutputHandler[] handlers;
protected final TransletOutputHandler getOutputHandler(double port) {
try {
return handlers[(int) port];
}
catch (IndexOutOfBoundsException e) {
BasisLibrary.runTimeError("Output port " + (int)port
+ " out of range.");
return null;
}
}
public final void transform(DOM document, TransletOutputHandler handler)
throws TransletException {
handlers = new TransletOutputHandler[] { handler };
transform(document, document.getIterator(), handler);
}
public final void transform(DOM document,
TransletOutputHandler[] handlers)
throws TransletException {
this.handlers = handlers;
transform(document, document.getIterator(), handlers[0]);
}
private char[] _characterArray = new char[32];
public final void characters(final String string,
TransletOutputHandler handler)
throws TransletException {
if (string == null) return;
final int length = string.length();
if (length > _characterArray.length) {
_characterArray = new char[length];
}
string.getChars(0, length, _characterArray, 0);
handler.characters(_characterArray, 0, length);
}
/**
* Pass the output encoding setting to the output handler.
*/
public String getOutputEncoding() {
return _encoding;
}
public void setIndexSize(int size) {
if (size > _indexSize)
_indexSize = size;
}
public void buildKeyIndex(String name, int node, String value) {
KeyIndex index = (KeyIndex)_keyIndexes.get(name);
if (index == null) {
_keyIndexes.put(name, index = new KeyIndex(_indexSize));
}
index.add(value, node);
}
public KeyIndex createKeyIndex() {
return(new KeyIndex(_indexSize));
}
public KeyIndex getKeyIndex(String name) {
final KeyIndex index = (KeyIndex)_keyIndexes.get(name);
return index != null ? index : _emptyKeyIndex;
}
public void buildKeys(DOM document, NodeIterator iterator,
TransletOutputHandler handler,
int root) throws TransletException {
}
public void setDOMCache(DOMCache cache) {
_domCache = cache;
}
public DOMCache getDOMCache() {
return(_domCache);
}
private String _transletName;
public void setTransletName(String name) {
_transletName = name;
}
public String getTransletName() {
return _transletName;
}
/*********************************************************
* Transformer methods
*********************************************************/
public void transform(Source xmlsrc, Result outputTarget)
throws TransformerException
{
/********************
doTransform( xmlsrc.getSystemId(),
((StreamResult)outputTarget).getOutputStream() );
*******************************/
// try to get the encoding from Translet
final Translet translet = (Translet)this;
String encoding = translet.getOutputEncoding();
if (encoding == null) encoding = "UTF-8";
// create a DefaultSAXOutputHandler
DefaultSAXOutputHandler saxHandler = null;
StreamResult target = (StreamResult)outputTarget;
java.io.Writer writer = target.getWriter();
java.io.OutputStream os = target.getOutputStream();
String systemid = target.getSystemId();
if (writer != null) {
// no constructor that takes encoding yet...
try {
saxHandler = new DefaultSAXOutputHandler(writer);
} catch (java.io.IOException e) {
throw new TransformerException(
"IOException creating DefaultSAXOutputHandler");
}
} else if (os != null) {
try {
saxHandler = new DefaultSAXOutputHandler(os, encoding);
} catch (java.io.IOException e) {
throw new TransformerException(
"IOException creating DefaultSAXOutputHandler");
}
} else if (systemid != null) {
String filePrefix = new String("file:///");
if (systemid.startsWith(filePrefix)) {
systemid = systemid.substring(filePrefix.length());
}
try {
saxHandler = new DefaultSAXOutputHandler(
((OutputStream)new FileOutputStream(systemid)),
encoding);
} catch (java.io.FileNotFoundException e) {
throw new TransformerException(
"Transform output target could not be opened.");
} catch (java.io.IOException e) {
throw new TransformerException(
"Transform output target could not be opened.");
}
}
// finally do the transformation...
doTransform(xmlsrc.getSystemId(), saxHandler, encoding);
}
private void doTransform(String xmlDocName,
DefaultSAXOutputHandler saxHandler, String encoding)
{
try {
final Translet translet = (Translet)this; // GTM added
// Create a SAX parser and get the XMLReader object it uses
final SAXParserFactory factory = SAXParserFactory.newInstance();
final SAXParser parser = factory.newSAXParser();
final XMLReader reader = parser.getXMLReader();
// Set the DOM's DOM builder as the XMLReader's SAX2 content handler
final DOMImpl dom = new DOMImpl();
reader.setContentHandler(dom.getBuilder());
// Create a DTD monitor and pass it to the XMLReader object
final DTDMonitor dtdMonitor = new DTDMonitor();
dtdMonitor.handleDTD(reader);
dom.setDocumentURI(xmlDocName);
/****************
if (_uri)
reader.parse(xmlDocName);
else
*******************/
reader.parse("file:"+(new File(xmlDocName).getAbsolutePath()));
// Set size of key/id indices
setIndexSize(dom.getSize());
// If there are any elements with ID attributes, build an index
dtdMonitor.buildIdIndex(dom, 0, this);
setDTDMonitor(dtdMonitor);
// Transform the document
TextOutput textOutput = new TextOutput(saxHandler, encoding);
translet.transform(dom, textOutput);
textOutput.flush();
}
catch (TransletException e) {
if (_errorListener != null) {
postErrorToListener(e.getMessage());
} else {
System.err.println("\nTranslet Error: " + e.getMessage());
}
System.exit(1);
}
catch (RuntimeException e) {
if (_errorListener != null) {
postErrorToListener("Runtime Error: " + e.getMessage());
} else {
System.err.println("\nRuntime Error: " + e.getMessage());
}
System.exit(1);
}
catch (FileNotFoundException e) {
if (_errorListener != null) {
postErrorToListener("File Not Found: " + e.getMessage());
} else {
//System.err.println("Error:File or URI '"+_fileName+"'
// not found.");
}
System.exit(1);
}
catch (MalformedURLException e) {
if (_errorListener != null) {
postErrorToListener("Malformed URL: " + e.getMessage());
} else {
//System.err.println("Error: Invalid URI '"+_fileName+"'.");
}
System.exit(1);
}
catch (UnknownHostException e) {
if (_errorListener != null) {
postErrorToListener("Can't resolve URI: " + e.getMessage());
} else {
//System.err.println("Error: Can't resolve URI specification '"+
//_fileName+"'.");
}
System.exit(1);
}
catch (Exception e) {
if (_errorListener != null) {
postErrorToListener("Internal error: " + e.getMessage());
} else {
System.err.println("Error: internal error.");
e.printStackTrace();
}
System.exit(1);
}
}
public void clearParameters() {
paramsStack.clear();
}
// TrAX support methods, get/setErrorListener
private ErrorListener _errorListener = null;
public ErrorListener getErrorListener() {
return _errorListener;
}
public void setErrorListener(ErrorListener listener) throws
IllegalArgumentException
{
if (listener == null) {
throw new IllegalArgumentException(
"Error: setErrorListener() call where ErrorListener is null");
}
_errorListener = listener;
}
/**
* inform TrAX error listener of this error
*/
private void postErrorToListener(String msg) {
try {
_errorListener.error(new TransformerException(
"Translet Error: " + msg));
} catch (TransformerException e) {
// TBD
}
}
/**
* inform TrAX error listener of this warning
*/
private void postWarningToListener(String msg) {
try {
_errorListener.warning(new TransformerException(
"Translet Warning: " + msg));
} catch (TransformerException e) {
// TBD
}
}
public Properties getOutputProperties() throws IllegalArgumentException {
/*TBD*/
throw new IllegalArgumentException(
"AbstractTranslet:getOutputProperties() " +
"not implemented yet.");
//return null;
}
public String getOutputProperty(String name) throws
IllegalArgumentException
{
/*TBD*/
throw new IllegalArgumentException(
"AbstractTranslet:getOutputProperty(String) " +
"not implemented yet.");
//return "";
}
public void setOutputProperties(Properties props) throws
IllegalArgumentException
{
/*TBD */
throw new IllegalArgumentException(
"AbstractTranslet:setOutputProperty(Properties) " +
"not implemented yet.");
}
public void setOutputProperty(String name, String value) throws
IllegalArgumentException
{
/*TBD*/
throw new IllegalArgumentException(
"AbstractTranslet:setOutputProperty(String,String) " +
"not implemented yet.");
}
public URIResolver getURIResolver() { /*TBD*/ return null; }
public void setParameter(String name, Object value) {
addParameter(name, value);
}
public void setURIResolver(URIResolver resolver) {
/*TBD*/
}
}