blob: 3422e84d3f3ae011311ca03f073e894b754f1bc9 [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 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 Morten Jorgensen
* @author G. Todd Miller
* @author Santiago Pericas-Geertsen
*
*/
package org.apache.xalan.xsltc.trax;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownServiceException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.DOMCache;
import org.apache.xalan.xsltc.StripFilter;
import org.apache.xalan.xsltc.Translet;
import org.apache.xalan.xsltc.TransletException;
import org.apache.xalan.xsltc.TransletOutputHandler;
import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
import org.apache.xalan.xsltc.dom.DOMBuilder;
import org.apache.xalan.xsltc.dom.DOMWSFilter;
import org.apache.xalan.xsltc.dom.SAXImpl;
import org.apache.xalan.xsltc.dom.XSLTCDTMManager;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import org.apache.xalan.xsltc.runtime.Hashtable;
import org.apache.xalan.xsltc.runtime.output.TransletOutputHandlerFactory;
import org.apache.xml.dtm.DTMWSFilter;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
public final class TransformerImpl extends Transformer
implements DOMCache, ErrorListener
{
private final static String EMPTY_STRING = "";
private final static String NO_STRING = "no";
private final static String YES_STRING = "yes";
private final static String XML_STRING = "xml";
private final static String LEXICAL_HANDLER_PROPERTY =
"http://xml.org/sax/properties/lexical-handler";
private static final String NAMESPACE_FEATURE =
"http://xml.org/sax/features/namespaces";
/**
* A reference to the translet or null if the identity transform.
*/
private AbstractTranslet _translet = null;
/**
* The output method of this transformation.
*/
private String _method = null;
/**
* The output encoding of this transformation.
*/
private String _encoding = null;
/**
* The systemId set in input source.
*/
private String _sourceSystemId = null;
/**
* An error listener for runtime errors.
*/
private ErrorListener _errorListener = this;
/**
* A reference to a URI resolver for calls to document().
*/
private URIResolver _uriResolver = null;
/**
* Output properties of this transformer instance.
*/
private Properties _properties, _propertiesClone;
/**
* A reference to an output handler factory.
*/
private TransletOutputHandlerFactory _tohFactory = null;
/**
* A reference to a internal DOM represenation of the input.
*/
private DOM _dom = null;
/**
* Number of indent spaces to add when indentation is on.
*/
private int _indentNumber;
/**
* A reference to the transformer factory that this templates
* object belongs to.
*/
private TransformerFactoryImpl _tfactory = null;
/**
* A flag indicating whether we use incremental building of the DTM.
*/
//private boolean _isIncremental = false;
/**
* A flag indicating whether this transformer implements the identity
* transform.
*/
private boolean _isIdentity = false;
/**
* A hashtable to store parameters for the identity transform. These
* are not needed during the transformation, but we must keep track of
* them to be fully complaint with the JAXP API.
*/
private Hashtable _parameters = null;
/**
* This class wraps an ErrorListener into a MessageHandler in order to
* capture messages reported via xsl:message.
*/
static class MessageHandler
extends org.apache.xalan.xsltc.runtime.MessageHandler
{
private ErrorListener _errorListener;
public MessageHandler(ErrorListener errorListener) {
_errorListener = errorListener;
}
public void displayMessage(String msg) {
if(_errorListener == null) {
System.err.println(msg);
}
else {
try {
_errorListener.warning(new TransformerException(msg));
}
catch (TransformerException e) {
// ignored
}
}
}
}
protected TransformerImpl(Properties outputProperties, int indentNumber,
TransformerFactoryImpl tfactory)
{
this(null, outputProperties, indentNumber, tfactory);
_isIdentity = true;
// _properties.put(OutputKeys.METHOD, "xml");
}
protected TransformerImpl(Translet translet, Properties outputProperties,
int indentNumber, TransformerFactoryImpl tfactory)
{
_translet = (AbstractTranslet) translet;
_properties = createOutputProperties(outputProperties);
_propertiesClone = (Properties) _properties.clone();
_indentNumber = indentNumber;
_tfactory = tfactory;
//_isIncremental = tfactory._incremental;
}
/**
* Returns the translet wrapped inside this Transformer or
* null if this is the identity transform.
*/
protected AbstractTranslet getTranslet() {
return _translet;
}
public boolean isIdentity() {
return _isIdentity;
}
/**
* Implements JAXP's Transformer.transform()
*
* @param source Contains the input XML document
* @param result Will contain the output from the transformation
* @throws TransformerException
*/
public void transform(Source source, Result result)
throws TransformerException
{
if (!_isIdentity) {
if (_translet == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_TRANSLET_ERR);
throw new TransformerException(err.toString());
}
// Pass output properties to the translet
transferOutputProperties(_translet);
}
final TransletOutputHandler toHandler = getOutputHandler(result);
if (toHandler == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_HANDLER_ERR);
throw new TransformerException(err.toString());
}
if (_uriResolver != null && !_isIdentity) {
_translet.setDOMCache(this);
}
// Pass output properties to handler if identity
if (_isIdentity) {
transferOutputProperties(toHandler);
}
transform(source, toHandler, _encoding);
if (result instanceof DOMResult) {
((DOMResult)result).setNode(_tohFactory.getNode());
}
}
/**
* Create an output handler for the transformation output based on
* the type and contents of the TrAX Result object passed to the
* transform() method.
*/
public TransletOutputHandler getOutputHandler(Result result)
throws TransformerException
{
// Get output method using get() to ignore defaults
_method = (String) _properties.get(OutputKeys.METHOD);
// Get encoding using getProperty() to use defaults
_encoding = (String) _properties.getProperty(OutputKeys.ENCODING);
_tohFactory = TransletOutputHandlerFactory.newInstance();
_tohFactory.setEncoding(_encoding);
if (_method != null) {
_tohFactory.setOutputMethod(_method);
}
// Set indentation number in the factory
if (_indentNumber >= 0) {
_tohFactory.setIndentNumber(_indentNumber);
}
// Return the content handler for this Result object
try {
// Result object could be SAXResult, DOMResult, or StreamResult
if (result instanceof SAXResult) {
final SAXResult target = (SAXResult)result;
final ContentHandler handler = target.getHandler();
_tohFactory.setHandler(handler);
if (handler instanceof LexicalHandler) {
_tohFactory.setLexicalHandler((LexicalHandler) handler);
}
_tohFactory.setOutputType(TransletOutputHandlerFactory.SAX);
return _tohFactory.getTransletOutputHandler();
}
else if (result instanceof DOMResult) {
_tohFactory.setNode(((DOMResult) result).getNode());
_tohFactory.setOutputType(TransletOutputHandlerFactory.DOM);
return _tohFactory.getTransletOutputHandler();
}
else if (result instanceof StreamResult) {
// Get StreamResult
final StreamResult target = (StreamResult) result;
// StreamResult may have been created with a java.io.File,
// java.io.Writer, java.io.OutputStream or just a String
// systemId.
_tohFactory.setOutputType(TransletOutputHandlerFactory.STREAM);
// try to get a Writer from Result object
final Writer writer = target.getWriter();
if (writer != null) {
_tohFactory.setWriter(writer);
return _tohFactory.getTransletOutputHandler();
}
// or try to get an OutputStream from Result object
final OutputStream ostream = target.getOutputStream();
if (ostream != null) {
_tohFactory.setOutputStream(ostream);
return _tohFactory.getTransletOutputHandler();
}
// or try to get just a systemId string from Result object
String systemId = result.getSystemId();
if (systemId == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_RESULT_ERR);
throw new TransformerException(err.toString());
}
// System Id may be in one of several forms, (1) a uri
// that starts with 'file:', (2) uri that starts with 'http:'
// or (3) just a filename on the local system.
URL url = null;
if (systemId.startsWith("file:")) {
url = new URL(systemId);
_tohFactory.setOutputStream(
new FileOutputStream(url.getFile()));
return _tohFactory.getTransletOutputHandler();
}
else if (systemId.startsWith("http:")) {
url = new URL(systemId);
final URLConnection connection = url.openConnection();
_tohFactory.setOutputStream(connection.getOutputStream());
return _tohFactory.getTransletOutputHandler();
}
else {
// system id is just a filename
url = new File(systemId).toURL();
_tohFactory.setOutputStream(
new FileOutputStream(url.getFile()));
return _tohFactory.getTransletOutputHandler();
}
}
}
// If we cannot write to the location specified by the SystemId
catch (UnknownServiceException e) {
throw new TransformerException(e);
}
catch (ParserConfigurationException e) {
throw new TransformerException(e);
}
// If we cannot create the file specified by the SystemId
catch (IOException e) {
throw new TransformerException(e);
}
return null;
}
/**
* Set the internal DOM that will be used for the next transformation
*/
protected void setDOM(DOM dom) {
_dom = dom;
}
/**
* Builds an internal DOM from a TrAX Source object
*/
private DOM getDOM(Source source, int mask)
throws TransformerException {
try {
DOM dom = null;
SAXImpl saxImpl = null;
DTMWSFilter wsfilter;
if (_translet != null && _translet instanceof StripFilter) {
wsfilter = new DOMWSFilter(_translet);
} else {
wsfilter = null;
}
boolean hasIdCall = (_translet != null) ? _translet.hasIdCall()
: false;
// Get systemId from source
if (source != null) {
_sourceSystemId = source.getSystemId();
}
if (source instanceof SAXSource) {
// Get all info from the input SAXSource object
final SAXSource sax = (SAXSource)source;
XMLReader reader = sax.getXMLReader();
final InputSource input = sax.getInputSource();
final boolean hasUserReader = reader != null;
// Create a reader if not set by user
if (reader == null) {
reader = _tfactory.getXMLReader();
}
// Create a new internal DOM and set up its builder to trap
// all content/lexical events
XSLTCDTMManager dtmManager = XSLTCDTMManager.newInstance();
//dtmManager.setIncremental(_isIncremental);
saxImpl = (SAXImpl)dtmManager.getDTM(sax, false, wsfilter, true, false,
hasUserReader, 0, hasIdCall);
//final DOMBuilder builder = ((SAXImpl)dom).getBuilder();
try {
reader.setProperty(LEXICAL_HANDLER_PROPERTY, saxImpl);
}
catch (SAXException e) {
// quitely ignored
}
reader.setContentHandler(saxImpl);
reader.setDTDHandler(saxImpl);
saxImpl.setDocumentURI(_sourceSystemId);
}
else if (source instanceof DOMSource) {
// Create a new internal DTM and build it directly from DOM
XSLTCDTMManager dtmManager = XSLTCDTMManager.newInstance();
//dtmManager.setIncremental(_isIncremental);
saxImpl = (SAXImpl)dtmManager.getDTM(source, false, wsfilter, true,
false, hasIdCall);
saxImpl.setDocumentURI(_sourceSystemId);
}
// Handle StreamSource input
else if (source instanceof StreamSource) {
// Get all info from the input StreamSource object
final StreamSource stream = (StreamSource)source;
final InputStream streamInput = stream.getInputStream();
final Reader streamReader = stream.getReader();
final XMLReader reader = _tfactory.getXMLReader();
// Create a new internal DOM and set up its builder to trap
// all content/lexical events
XSLTCDTMManager dtmManager = XSLTCDTMManager.newInstance();
//dtmManager.setIncremental(_isIncremental);
InputSource input;
if (streamInput != null) {
input = new InputSource(streamInput);
input.setSystemId(_sourceSystemId);
}
else if (streamReader != null) {
input = new InputSource(streamReader);
input.setSystemId(_sourceSystemId);
}
else if (_sourceSystemId != null)
input = new InputSource(_sourceSystemId);
else {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_SOURCE_ERR);
throw new TransformerException(err.toString());
}
saxImpl = (SAXImpl)dtmManager.getDTM(new SAXSource(reader, input),
false, wsfilter, true,
false, hasIdCall);
saxImpl.setDocumentURI(_sourceSystemId);
}
else if (source instanceof XSLTCSource) {
final XSLTCSource xsltcsrc = (XSLTCSource)source;
dom = xsltcsrc.getDOM();
}
// DOM already set via a call to setDOM()
else if (_dom != null) {
dom = _dom; _dom = null; // use only once, so reset to 'null'
}
else {
return null;
}
if (saxImpl != null) {
dom = saxImpl;
}
if (!_isIdentity) {
// Give the translet the opportunity to make a prepass of
// the document, in case it can extract useful information early
_translet.prepassDocument(dom);
}
return dom;
}
//catch (FileNotFoundException e) {
// if (_errorListener != null) postErrorToListener(e.getMessage());
// throw new TransformerException(e);
// }
//catch (MalformedURLException e) {
// if (_errorListener != null) postErrorToListener(e.getMessage());
// throw new TransformerException(e);
// }
// catch (UnknownHostException e) {
// if (_errorListener != null) postErrorToListener(e.getMessage());
// throw new TransformerException(e);
// }
catch (Exception e) {
if (_errorListener != null) postErrorToListener(e.getMessage());
throw new TransformerException(e);
}
}
private void transformIdentity(Source source, TransletOutputHandler handler)
throws Exception
{
// Get systemId from source
if (source != null) {
_sourceSystemId = source.getSystemId();
}
if (source instanceof StreamSource) {
final StreamSource stream = (StreamSource) source;
final InputStream streamInput = stream.getInputStream();
final Reader streamReader = stream.getReader();
final XMLReader reader = _tfactory.getXMLReader();
// Hook up reader and output handler
try {
reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
}
catch (SAXException e) {
// Falls through
}
reader.setContentHandler(new SAX2TO(handler));
// Create input source from source
InputSource input;
if (streamInput != null) {
input = new InputSource(streamInput);
input.setSystemId(_sourceSystemId);
}
else if (streamReader != null) {
input = new InputSource(streamReader);
input.setSystemId(_sourceSystemId);
}
else if (_sourceSystemId != null) {
input = new InputSource(_sourceSystemId);
}
else {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_SOURCE_ERR);
throw new TransformerException(err.toString());
}
// Start pushing SAX events
reader.parse(input);
}
else if (source instanceof SAXSource) {
final SAXSource sax = (SAXSource) source;
XMLReader reader = sax.getXMLReader();
final InputSource input = sax.getInputSource();
// Create a reader if not set by user
if (reader == null) {
reader = _tfactory.getXMLReader();
}
// Hook up reader and output handler
try {
reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
}
catch (SAXException e) {
// Falls through
}
reader.setContentHandler(new SAX2TO(handler));
// Start pushing SAX events
reader.parse(input);
}
else if (source instanceof DOMSource) {
final DOMSource domsrc = (DOMSource) source;
new DOM2TO(domsrc.getNode(), handler).parse();
}
else if (source instanceof XSLTCSource) {
final DOM dom = ((XSLTCSource) source).getDOM();
((SAXImpl)dom).copy(handler);
}
else {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_SOURCE_ERR);
throw new TransformerException(err.toString());
}
}
/**
* Internal transformation method - uses the internal APIs of XSLTC
*/
private void transform(Source source, TransletOutputHandler handler,
String encoding) throws TransformerException
{
try {
if (_isIdentity) {
transformIdentity(source, handler);
}
else {
_translet.transform(getDOM(source, 0), handler);
}
}
catch (TransletException e) {
if (_errorListener != null) postErrorToListener(e.getMessage());
throw new TransformerException(e);
}
catch (RuntimeException e) {
if (_errorListener != null) postErrorToListener(e.getMessage());
throw new TransformerException(e);
}
catch (Exception e) {
if (_errorListener != null) postErrorToListener(e.getMessage());
throw new TransformerException(e);
}
}
/**
* Implements JAXP's Transformer.getErrorListener()
* Get the error event handler in effect for the transformation.
*
* @return The error event handler currently in effect
*/
public ErrorListener getErrorListener() {
return _errorListener;
}
/**
* Implements JAXP's Transformer.setErrorListener()
* Set the error event listener in effect for the transformation.
* Register a message handler in the translet in order to forward
* xsl:messages to error listener.
*
* @param listener The error event listener to use
* @throws IllegalArgumentException
*/
public void setErrorListener(ErrorListener listener)
throws IllegalArgumentException {
if (listener == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR,
"Transformer");
throw new IllegalArgumentException(err.toString());
}
_errorListener = listener;
// Register a message handler to report xsl:messages
if (_translet != null)
_translet.setMessageHandler(new MessageHandler(_errorListener));
}
/**
* Inform TrAX error listener of an error
*/
private void postErrorToListener(String message) {
try {
_errorListener.error(new TransformerException(message));
}
catch (TransformerException e) {
// ignored - transformation cannot be continued
}
}
/**
* Inform TrAX error listener of a warning
*/
private void postWarningToListener(String message) {
try {
_errorListener.warning(new TransformerException(message));
}
catch (TransformerException e) {
// ignored - transformation cannot be continued
}
}
/**
* The translet stores all CDATA sections set in the <xsl:output> element
* in a Hashtable. This method will re-construct the whitespace separated
* list of elements given in the <xsl:output> element.
*/
private String makeCDATAString(Hashtable cdata) {
// Return a 'null' string if no CDATA section elements were specified
if (cdata == null) return null;
StringBuffer result = new StringBuffer();
// Get an enumeration of all the elements in the hashtable
Enumeration elements = cdata.keys();
if (elements.hasMoreElements()) {
result.append((String)elements.nextElement());
while (elements.hasMoreElements()) {
String element = (String)elements.nextElement();
result.append(' ');
result.append(element);
}
}
return(result.toString());
}
/**
* Implements JAXP's Transformer.getOutputProperties().
* Returns a copy of the output properties for the transformation. This is
* a set of layered properties. The first layer contains properties set by
* calls to setOutputProperty() and setOutputProperties() on this class,
* and the output settings defined in the stylesheet's <xsl:output>
* element makes up the second level, while the default XSLT output
* settings are returned on the third level.
*
* @return Properties in effect for this Transformer
*/
public Properties getOutputProperties() {
return (Properties) _properties.clone();
}
/**
* Implements JAXP's Transformer.getOutputProperty().
* Get an output property that is in effect for the transformation. The
* property specified may be a property that was set with setOutputProperty,
* or it may be a property specified in the stylesheet.
*
* @param name A non-null string that contains the name of the property
* @throws IllegalArgumentException if the property name is not known
*/
public String getOutputProperty(String name)
throws IllegalArgumentException
{
if (!validOutputProperty(name)) {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
throw new IllegalArgumentException(err.toString());
}
return _properties.getProperty(name);
}
/**
* Implements JAXP's Transformer.setOutputProperties().
* Set the output properties for the transformation. These properties
* will override properties set in the Templates with xsl:output.
* Unrecognised properties will be quitely ignored.
*
* @param properties The properties to use for the Transformer
* @throws IllegalArgumentException Never, errors are ignored
*/
public void setOutputProperties(Properties properties)
throws IllegalArgumentException
{
if (properties != null) {
final Enumeration names = properties.propertyNames();
while (names.hasMoreElements()) {
final String name = (String) names.nextElement();
// Ignore lower layer properties
if (isDefaultProperty(name, properties)) continue;
if (validOutputProperty(name)) {
_properties.setProperty(name, properties.getProperty(name));
}
else {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
throw new IllegalArgumentException(err.toString());
}
}
}
else {
_properties = _propertiesClone;
}
}
/**
* Implements JAXP's Transformer.setOutputProperty().
* Get an output property that is in effect for the transformation. The
* property specified may be a property that was set with
* setOutputProperty(), or it may be a property specified in the stylesheet.
*
* @param name The name of the property to set
* @param value The value to assign to the property
* @throws IllegalArgumentException Never, errors are ignored
*/
public void setOutputProperty(String name, String value)
throws IllegalArgumentException
{
if (!validOutputProperty(name)) {
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
throw new IllegalArgumentException(err.toString());
}
_properties.setProperty(name, value);
}
/**
* Internal method to pass any properties to the translet prior to
* initiating the transformation
*/
private void transferOutputProperties(AbstractTranslet translet)
{
// Return right now if no properties are set
if (_properties == null) return;
// Get a list of all the defined properties
Enumeration names = _properties.propertyNames();
while (names.hasMoreElements()) {
// Note the use of get() instead of getProperty()
String name = (String) names.nextElement();
String value = (String) _properties.get(name);
// Ignore default properties
if (value == null) continue;
// Pass property value to translet - override previous setting
if (name.equals(OutputKeys.ENCODING)) {
translet._encoding = value;
}
else if (name.equals(OutputKeys.METHOD)) {
translet._method = value;
}
else if (name.equals(OutputKeys.DOCTYPE_PUBLIC)) {
translet._doctypePublic = value;
}
else if (name.equals(OutputKeys.DOCTYPE_SYSTEM)) {
translet._doctypeSystem = value;
}
else if (name.equals(OutputKeys.MEDIA_TYPE)) {
translet._mediaType = value;
}
else if (name.equals(OutputKeys.STANDALONE)) {
translet._standalone = value;
}
else if (name.equals(OutputKeys.VERSION)) {
translet._version = value;
}
else if (name.equals(OutputKeys.OMIT_XML_DECLARATION)) {
translet._omitHeader =
(value != null && value.toLowerCase().equals("yes"));
}
else if (name.equals(OutputKeys.INDENT)) {
translet._indent =
(value != null && value.toLowerCase().equals("yes"));
}
else if (name.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
if (value != null) {
translet._cdata = null; // clear previous setting
StringTokenizer e = new StringTokenizer(value);
while (e.hasMoreTokens()) {
translet.addCdataElement(e.nextToken());
}
}
}
}
}
/**
* This method is used to pass any properties to the output handler
* when running the identity transform.
*/
public void transferOutputProperties(TransletOutputHandler handler)
{
// Return right now if no properties are set
if (_properties == null) return;
String doctypePublic = null;
String doctypeSystem = null;
// Get a list of all the defined properties
Enumeration names = _properties.propertyNames();
while (names.hasMoreElements()) {
// Note the use of get() instead of getProperty()
String name = (String) names.nextElement();
String value = (String) _properties.get(name);
// Ignore default properties
if (value == null) continue;
// Pass property value to translet - override previous setting
if (name.equals(OutputKeys.DOCTYPE_PUBLIC)) {
doctypePublic = value;
}
else if (name.equals(OutputKeys.DOCTYPE_SYSTEM)) {
doctypeSystem = value;
}
else if (name.equals(OutputKeys.MEDIA_TYPE)) {
handler.setMediaType(value);
}
else if (name.equals(OutputKeys.STANDALONE)) {
handler.setStandalone(value);
}
else if (name.equals(OutputKeys.VERSION)) {
handler.setVersion(value);
}
else if (name.equals(OutputKeys.OMIT_XML_DECLARATION)) {
handler.omitHeader(
value != null && value.toLowerCase().equals("yes"));
}
else if (name.equals(OutputKeys.INDENT)) {
handler.setIndent(
value != null && value.toLowerCase().equals("yes"));
}
else if (name.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
if (value != null) {
Hashtable table = new Hashtable();
StringTokenizer e = new StringTokenizer(value);
while (e.hasMoreTokens()) {
final String token = e.nextToken();
table.put(token, token);
}
handler.setCdataElements(table);
}
}
}
// Call setDoctype() if needed
if (doctypePublic != null || doctypeSystem != null) {
handler.setDoctype(doctypeSystem, doctypePublic);
}
}
/**
* Internal method to create the initial set of properties. There
* are two layers of properties: the default layer and the base layer.
* The latter contains properties defined in the stylesheet or by
* the user using this API.
*/
private Properties createOutputProperties(Properties outputProperties) {
final Properties defaults = new Properties();
defaults.setProperty(OutputKeys.ENCODING, "UTF-8");
defaults.setProperty(OutputKeys.METHOD, XML_STRING);
defaults.setProperty(OutputKeys.INDENT, NO_STRING);
defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/xml");
defaults.setProperty(OutputKeys.OMIT_XML_DECLARATION, NO_STRING);
defaults.setProperty(OutputKeys.STANDALONE, NO_STRING);
defaults.setProperty(OutputKeys.VERSION, "1.0");
// Copy propeties set in stylesheet to base
final Properties base = new Properties(defaults);
if (outputProperties != null) {
final Enumeration names = outputProperties.propertyNames();
while (names.hasMoreElements()) {
final String name = (String) names.nextElement();
base.setProperty(name, outputProperties.getProperty(name));
}
}
else {
base.setProperty(OutputKeys.ENCODING, _translet._encoding);
if (_translet._method != null)
base.setProperty(OutputKeys.METHOD, _translet._method);
}
// Update defaults based on output method
final String method = base.getProperty(OutputKeys.METHOD);
if (method != null) {
if (method.equals("html")) {
defaults.setProperty(OutputKeys.INDENT, "yes");
defaults.setProperty(OutputKeys.VERSION, "4.0");
defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/html");
}
else if (method.equals("text")) {
defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/plain");
}
}
return base;
}
/**
* Verifies if a given output property name is a property defined in
* the JAXP 1.1 / TrAX spec
*/
private boolean validOutputProperty(String name) {
return (name.equals(OutputKeys.ENCODING) ||
name.equals(OutputKeys.METHOD) ||
name.equals(OutputKeys.INDENT) ||
name.equals(OutputKeys.DOCTYPE_PUBLIC) ||
name.equals(OutputKeys.DOCTYPE_SYSTEM) ||
name.equals(OutputKeys.CDATA_SECTION_ELEMENTS) ||
name.equals(OutputKeys.MEDIA_TYPE) ||
name.equals(OutputKeys.OMIT_XML_DECLARATION) ||
name.equals(OutputKeys.STANDALONE) ||
name.equals(OutputKeys.VERSION) ||
name.charAt(0) == '{');
}
/**
* Checks if a given output property is default (2nd layer only)
*/
private boolean isDefaultProperty(String name, Properties properties) {
return (properties.get(name) == null);
}
/**
* Implements JAXP's Transformer.setParameter()
* Add a parameter for the transformation. The parameter is simply passed
* on to the translet - no validation is performed - so any unused
* parameters are quitely ignored by the translet.
*
* @param name The name of the parameter
* @param value The value to assign to the parameter
*/
public void setParameter(String name, Object value) {
if (_isIdentity) {
if (_parameters == null) {
_parameters = new Hashtable();
}
_parameters.put(name, value);
}
else {
_translet.addParameter(name, value);
}
}
/**
* Implements JAXP's Transformer.clearParameters()
* Clear all parameters set with setParameter. Clears the translet's
* parameter stack.
*/
public void clearParameters() {
if (_isIdentity && _parameters != null) {
_parameters.clear();
}
else {
_translet.clearParameters();
}
}
/**
* Implements JAXP's Transformer.getParameter()
* Returns the value of a given parameter. Note that the translet will not
* keep values for parameters that were not defined in the stylesheet.
*
* @param name The name of the parameter
* @return An object that contains the value assigned to the parameter
*/
public final Object getParameter(String name) {
if (_isIdentity) {
return (_parameters != null) ? _parameters.get(name) : null;
}
else {
return _translet.getParameter(name);
}
}
/**
* Implements JAXP's Transformer.getURIResolver()
* Set the object currently used to resolve URIs used in document().
*
* @returns The URLResolver object currently in use
*/
public URIResolver getURIResolver() {
return _uriResolver;
}
/**
* Implements JAXP's Transformer.setURIResolver()
* Set an object that will be used to resolve URIs used in document().
*
* @param resolver The URIResolver to use in document()
*/
public void setURIResolver(URIResolver resolver) {
_uriResolver = resolver;
}
/**
* This class should only be used as a DOMCache for the translet if the
* URIResolver has been set.
*
* The method implements XSLTC's DOMCache interface, which is used to
* plug in an external document loader into a translet. This method acts
* as an adapter between TrAX's URIResolver interface and XSLTC's
* DOMCache interface. This approach is simple, but removes the
* possibility of using external document caches with XSLTC.
*
* @param uri An URI pointing to the document location
* @param mask Contains a document ID (passed from the translet)
* @param translet A reference to the translet requesting the document
*/
public DOM retrieveDocument(String uri, int mask, Translet translet) {
try {
return getDOM(_uriResolver.resolve(uri, _sourceSystemId), mask);
}
catch (TransformerException e) {
if (_errorListener != null)
postErrorToListener("File not found: " + e.getMessage());
return(null);
}
}
/**
* Receive notification of a recoverable error.
* The transformer must continue to provide normal parsing events after
* invoking this method. It should still be possible for the application
* to process the document through to the end.
*
* @param exception The warning information encapsulated in a transformer
* exception.
* @throws TransformerException if the application chooses to discontinue
* the transformation (always does in our case).
*/
public void error(TransformerException e)
throws TransformerException
{
Throwable wrapped = e.getException();
if (wrapped != null) {
System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG,
e.getMessageAndLocation(),
wrapped.getMessage()));
} else {
System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG,
e.getMessageAndLocation()));
}
throw e;
}
/**
* Receive notification of a non-recoverable error.
* The application must assume that the transformation cannot continue
* after the Transformer has invoked this method, and should continue
* (if at all) only to collect addition error messages. In fact,
* Transformers are free to stop reporting events once this method has
* been invoked.
*
* @param exception The warning information encapsulated in a transformer
* exception.
* @throws TransformerException if the application chooses to discontinue
* the transformation (always does in our case).
*/
public void fatalError(TransformerException e)
throws TransformerException
{
Throwable wrapped = e.getException();
if (wrapped != null) {
System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG,
e.getMessageAndLocation(),
wrapped.getMessage()));
} else {
System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG,
e.getMessageAndLocation()));
}
throw e;
}
/**
* Receive notification of a warning.
* Transformers can use this method to report conditions that are not
* errors or fatal errors. The default behaviour is to take no action.
* After invoking this method, the Transformer must continue with the
* transformation. It should still be possible for the application to
* process the document through to the end.
*
* @param exception The warning information encapsulated in a transformer
* exception.
* @throws TransformerException if the application chooses to discontinue
* the transformation (never does in our case).
*/
public void warning(TransformerException e)
throws TransformerException
{
Throwable wrapped = e.getException();
if (wrapped != null) {
System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG,
e.getMessageAndLocation(),
wrapped.getMessage()));
} else {
System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG,
e.getMessageAndLocation()));
}
}
}