jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java
changeset 12457 c348e06f0e82
parent 12005 a754d69d5e60
child 12458 d601e4bba306
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java	Thu Apr 12 08:38:26 2012 -0700
@@ -0,0 +1,1384 @@
+/*
+ * reserved comment block
+ * DO NOT REMOVE OR ALTER!
+ */
+/*
+ * Copyright 2001-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.
+ */
+/*
+ * $Id: TransformerImpl.java,v 1.10 2007/06/13 01:57:09 joehw Exp $
+ */
+
+package com.sun.org.apache.xalan.internal.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.URI;
+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 java.util.Vector;
+import java.lang.reflect.Constructor;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamReader;
+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.stax.StAXResult;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
+
+import com.sun.org.apache.xalan.internal.xsltc.DOM;
+import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
+import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
+import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
+import com.sun.org.apache.xalan.internal.xsltc.Translet;
+import com.sun.org.apache.xalan.internal.xsltc.TransletException;
+import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory;
+import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
+import com.sun.org.apache.xalan.internal.xsltc.dom.DOMWSFilter;
+import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
+import com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager;
+import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
+import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
+import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
+
+import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
+import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
+
+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;
+
+/**
+ * @author Morten Jorgensen
+ * @author G. Todd Miller
+ * @author Santiago Pericas-Geertsen
+ */
+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";
+
+    /**
+     * Namespace prefixes feature for {@link XMLReader}.
+     */
+    private static final String NAMESPACE_PREFIXES_FEATURE =
+        "http://xml.org/sax/features/namespace-prefixes";
+
+    /**
+     * 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 reference to the output stream, if we create one in our code.
+     */
+    private OutputStream _ostream = null;
+
+    /**
+     * A reference to the XSLTCDTMManager which is used to build the DOM/DTM
+     * for this transformer.
+     */
+    private XSLTCDTMManager _dtmManager = null;
+
+    /**
+     * A reference to an object that creates and caches XMLReader objects.
+     */
+    private XMLReaderManager _readerManager = XMLReaderManager.getInstance();
+
+    /**
+     * 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;
+
+    /**
+     * State of the secure processing feature.
+     */
+    private boolean _isSecureProcessing = 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 com.sun.org.apache.xalan.internal.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;
+    }
+
+    /**
+     * Return the state of the secure processing feature.
+     */
+    public boolean isSecureProcessing() {
+        return _isSecureProcessing;
+    }
+
+    /**
+     * Set the state of the secure processing feature.
+     */
+    public void setSecureProcessing(boolean flag) {
+        _isSecureProcessing = flag;
+    }
+
+    /**
+     * 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 SerializationHandler 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);
+        try{
+            if (result instanceof DOMResult) {
+                ((DOMResult)result).setNode(_tohFactory.getNode());
+            } else if (result instanceof StAXResult) {
+                  if (((StAXResult) result).getXMLEventWriter() != null)
+                {
+                    (_tohFactory.getXMLEventWriter()).flush();
+                }
+                else if (((StAXResult) result).getXMLStreamWriter() != null) {
+                    (_tohFactory.getXMLStreamWriter()).flush();
+                    //result = new StAXResult(_tohFactory.getXMLStreamWriter());
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("Result writing error");
+        }
+    }
+
+    /**
+     * 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 SerializationHandler 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);
+
+                /**
+                 * Fix for bug 24414
+                 * If the lexicalHandler is set then we need to get that
+                 * for obtaining the lexical information
+                 */
+                LexicalHandler lexicalHandler = target.getLexicalHandler();
+
+                if (lexicalHandler != null ) {
+                    _tohFactory.setLexicalHandler(lexicalHandler);
+                }
+
+                _tohFactory.setOutputType(TransletOutputHandlerFactory.SAX);
+                return _tohFactory.getSerializationHandler();
+            }
+            else if (result instanceof StAXResult) {
+                if (((StAXResult) result).getXMLEventWriter() != null)
+                    _tohFactory.setXMLEventWriter(((StAXResult) result).getXMLEventWriter());
+                else if (((StAXResult) result).getXMLStreamWriter() != null)
+                    _tohFactory.setXMLStreamWriter(((StAXResult) result).getXMLStreamWriter());
+                _tohFactory.setOutputType(TransletOutputHandlerFactory.STAX);
+                return _tohFactory.getSerializationHandler();
+            }
+            else if (result instanceof DOMResult) {
+                _tohFactory.setNode(((DOMResult) result).getNode());
+                _tohFactory.setNextSibling(((DOMResult) result).getNextSibling());
+                _tohFactory.setOutputType(TransletOutputHandlerFactory.DOM);
+                return _tohFactory.getSerializationHandler();
+            }
+            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.getSerializationHandler();
+                }
+
+                // or try to get an OutputStream from Result object
+                final OutputStream ostream = target.getOutputStream();
+                if (ostream != null) {
+                    _tohFactory.setOutputStream(ostream);
+                    return _tohFactory.getSerializationHandler();
+                }
+
+                // 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:")) {
+                    // if StreamResult(File) or setSystemID(File) was used,
+                    // the systemId will be URI encoded as a result of File.toURI(),
+                    // it must be decoded for use by URL
+                    try{
+                        Class clazz =   ObjectFactory.findProviderClass("java.net.URI", ObjectFactory.findClassLoader(), true);
+                        Constructor  construct   = clazz.getConstructor(new Class[] {java.lang.String.class} );
+                        URI uri = (URI) construct.newInstance(new Object[]{systemId}) ;
+                        systemId = "file:";
+
+                        String host = uri.getHost(); // decoded String
+                        String path = uri.getPath(); //decoded String
+                        if (path == null) {
+                         path = "";
+                        }
+
+                        // if host (URI authority) then file:// + host + path
+                        // else just path (may be absolute or relative)
+                        if (host != null) {
+                         systemId += "//" + host + path;
+                        } else {
+                         systemId += "//" + path;
+                        }
+                    }
+                    catch(ClassNotFoundException e){
+                        // running on J2SE 1.3 which doesn't have URI Class so OK to ignore
+                        //ClassNotFoundException.
+                    }
+                    catch (Exception  exception) {
+                        // URI exception which means nothing can be done so OK to ignore
+                    }
+
+                    url = new URL(systemId);
+                    _ostream = new FileOutputStream(url.getFile());
+                    _tohFactory.setOutputStream(_ostream);
+                    return _tohFactory.getSerializationHandler();
+                }
+                else if (systemId.startsWith("http:")) {
+                    url = new URL(systemId);
+                    final URLConnection connection = url.openConnection();
+                    _tohFactory.setOutputStream(_ostream = connection.getOutputStream());
+                    return _tohFactory.getSerializationHandler();
+                }
+                else {
+                    // system id is just a filename
+                    _tohFactory.setOutputStream(
+                        _ostream = new FileOutputStream(new File(systemId)));
+                    return _tohFactory.getSerializationHandler();
+                }
+            }
+        }
+        // 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) throws TransformerException {
+        try {
+            DOM dom = null;
+
+            if (source != null) {
+                DTMWSFilter wsfilter;
+                if (_translet != null && _translet instanceof StripFilter) {
+                    wsfilter = new DOMWSFilter(_translet);
+                 } else {
+                    wsfilter = null;
+                 }
+
+                 boolean hasIdCall = (_translet != null) ? _translet.hasIdCall()
+                                                         : false;
+
+                 if (_dtmManager == null) {
+                     _dtmManager =
+                         (XSLTCDTMManager)_tfactory.getDTMManagerClass()
+                                                   .newInstance();
+                 }
+                 dom = (DOM)_dtmManager.getDTM(source, false, wsfilter, true,
+                                              false, false, 0, hasIdCall);
+            } else if (_dom != null) {
+                 dom = _dom;
+                 _dom = null;  // use only once, so reset to 'null'
+            } else {
+                 return null;
+            }
+
+            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 (Exception e) {
+            if (_errorListener != null) {
+                postErrorToListener(e.getMessage());
+            }
+            throw new TransformerException(e);
+        }
+    }
+
+    /**
+     * Returns the {@link com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl}
+     * object that create this <code>Transformer</code>.
+     */
+    protected TransformerFactoryImpl getTransformerFactory() {
+        return _tfactory;
+    }
+
+    /**
+     * Returns the {@link com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory}
+     * object that create the <code>TransletOutputHandler</code>.
+     */
+    protected TransletOutputHandlerFactory getTransletOutputHandlerFactory() {
+        return _tohFactory;
+    }
+
+    private void transformIdentity(Source source, SerializationHandler 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 = _readerManager.getXMLReader();
+
+            try {
+                // Hook up reader and output handler
+                try {
+                    reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
+                    reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
+                } catch (SAXException e) {
+                    // Falls through
+                }
+                reader.setContentHandler(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);
+            } finally {
+                _readerManager.releaseXMLReader(reader);
+            }
+        } else if (source instanceof SAXSource) {
+            final SAXSource sax = (SAXSource) source;
+            XMLReader reader = sax.getXMLReader();
+            final InputSource input = sax.getInputSource();
+            boolean userReader = true;
+
+            try {
+                // Create a reader if not set by user
+                if (reader == null) {
+                    reader = _readerManager.getXMLReader();
+                    userReader = false;
+                }
+
+                // Hook up reader and output handler
+                try {
+                    reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
+                    reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
+                } catch (SAXException e) {
+                    // Falls through
+                }
+                reader.setContentHandler(handler);
+
+                // Start pushing SAX events
+                reader.parse(input);
+            } finally {
+                if (!userReader) {
+                    _readerManager.releaseXMLReader(reader);
+                }
+            }
+        } else if (source instanceof StAXSource) {
+            final StAXSource staxSource = (StAXSource)source;
+            StAXEvent2SAX staxevent2sax = null;
+            StAXStream2SAX staxStream2SAX = null;
+            if (staxSource.getXMLEventReader() != null) {
+                final XMLEventReader xmlEventReader = staxSource.getXMLEventReader();
+                staxevent2sax = new StAXEvent2SAX(xmlEventReader);
+                staxevent2sax.setContentHandler(handler);
+                staxevent2sax.parse();
+                handler.flushPending();
+            } else if (staxSource.getXMLStreamReader() != null) {
+                final XMLStreamReader xmlStreamReader = staxSource.getXMLStreamReader();
+                staxStream2SAX = new StAXStream2SAX(xmlStreamReader);
+                staxStream2SAX.setContentHandler(handler);
+                staxStream2SAX.parse();
+                handler.flushPending();
+            }
+        } 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(null, _translet);
+            ((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, SerializationHandler handler,
+        String encoding) throws TransformerException
+    {
+        try {
+            /*
+             * According to JAXP1.2, new SAXSource()/StreamSource()
+             * should create an empty input tree, with a default root node.
+             * new DOMSource()creates an empty document using DocumentBuilder.
+             * newDocument(); Use DocumentBuilder.newDocument() for all 3
+             * situations, since there is no clear spec. how to create
+             * an empty tree when both SAXSource() and StreamSource() are used.
+             */
+            if ((source instanceof StreamSource && source.getSystemId()==null
+                && ((StreamSource)source).getInputStream()==null &&
+                ((StreamSource)source).getReader()==null)||
+                (source instanceof SAXSource &&
+                ((SAXSource)source).getInputSource()==null &&
+                ((SAXSource)source).getXMLReader()==null )||
+                (source instanceof DOMSource &&
+                ((DOMSource)source).getNode()==null)){
+                        DocumentBuilderFactory builderF =
+                                DocumentBuilderFactory.newInstance();
+                        DocumentBuilder builder =
+                                builderF.newDocumentBuilder();
+                        String systemID = source.getSystemId();
+                        source = new DOMSource(builder.newDocument());
+
+                        // Copy system ID from original, empty Source to new
+                        if (systemID != null) {
+                          source.setSystemId(systemID);
+                        }
+            }
+            if (_isIdentity) {
+                transformIdentity(source, handler);
+            } else {
+                _translet.transform(getDOM(source), 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);
+        } finally {
+            _dtmManager = null;
+        }
+
+        // If we create an output stream for the Result, we need to close it after the transformation.
+        if (_ostream != null) {
+            try {
+                _ostream.close();
+            }
+            catch (IOException e) {}
+            _ostream = null;
+        }
+    }
+
+    /**
+     * 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(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL +"indent-amount")) {
+                 if (value != null) {
+                     translet._indentamount = Integer.parseInt(value);
+                 }
+            }
+            else if (name.equals(OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL +"indent-amount")) {
+                 if (value != null) {
+                     translet._indentamount = Integer.parseInt(value);
+                 }
+            }
+            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(SerializationHandler 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.setOmitXMLDeclaration(
+                    value != null && value.toLowerCase().equals("yes"));
+            }
+            else if (name.equals(OutputKeys.INDENT)) {
+                handler.setIndent(
+                    value != null && value.toLowerCase().equals("yes"));
+            }
+            else if (name.equals(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL +"indent-amount")) {
+                if (value != null) {
+                    handler.setIndentAmount(Integer.parseInt(value));
+                }
+            }
+            else if (name.equals(OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL +"indent-amount")) {
+                if (value != null) {
+                    handler.setIndentAmount(Integer.parseInt(value));
+                }
+            }
+            else if (name.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
+                if (value != null) {
+                    StringTokenizer e = new StringTokenizer(value);
+                    Vector uriAndLocalNames = null;
+                    while (e.hasMoreTokens()) {
+                        final String token = e.nextToken();
+
+                        // look for the last colon, as the String may be
+                        // something like "http://abc.com:local"
+                        int lastcolon = token.lastIndexOf(':');
+                        String uri;
+                        String localName;
+                        if (lastcolon > 0) {
+                            uri = token.substring(0, lastcolon);
+                            localName = token.substring(lastcolon+1);
+                        } else {
+                            // no colon at all, lets hope this is the
+                            // local name itself then
+                            uri = null;
+                            localName = token;
+                        }
+
+                        if (uriAndLocalNames == null) {
+                            uriAndLocalNames = new Vector();
+                        }
+                        // add the uri/localName as a pair, in that order
+                        uriAndLocalNames.addElement(uri);
+                        uriAndLocalNames.addElement(localName);
+                    }
+                    handler.setCdataSectionElements(uriAndLocalNames);
+                }
+            }
+        }
+
+        // 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();
+        setDefaults(defaults, "xml");
+
+        // 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")) {
+                setDefaults(defaults,"html");
+            }
+            else if (method.equals("text")) {
+                setDefaults(defaults,"text");
+            }
+        }
+
+        return base;
+    }
+
+        /**
+         * Internal method to get the default properties from the
+         * serializer factory and set them on the property object.
+         * @param props a java.util.Property object on which the properties are set.
+         * @param method The output method type, one of "xml", "text", "html" ...
+         */
+        private void setDefaults(Properties props, String method)
+        {
+                final Properties method_props =
+                        OutputPropertiesFactory.getDefaultMethodProperties(method);
+                {
+                        final Enumeration names = method_props.propertyNames();
+                        while (names.hasMoreElements())
+                        {
+                                final String name = (String)names.nextElement();
+                                props.setProperty(name, method_props.getProperty(name));
+                        }
+                }
+        }
+    /**
+     * 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 (value == null) {
+            ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_SET_PARAM_VALUE, name);
+            throw new IllegalArgumentException(err.toString());
+        }
+
+        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().
+     *
+     * @return  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 baseURI The base URI used by the document call.
+     * @param href The href argument passed to the document function.
+     * @param translet A reference to the translet requesting the document
+     */
+    public DOM retrieveDocument(String baseURI, String href, Translet translet) {
+        try {
+            // Argument to document function was: document('');
+            if (href.length() == 0) {
+                href = new String(baseURI);
+            }
+
+            /*
+             *  Fix for bug 24188
+             *  Incase the _uriResolver.resolve(href,base) is null
+             *  try to still  retrieve the document before returning null
+             *  and throwing the FileNotFoundException in
+             *  com.sun.org.apache.xalan.internal.xsltc.dom.LoadDocument
+             *
+             */
+            Source resolvedSource = _uriResolver.resolve(href, baseURI);
+            if (resolvedSource == null)  {
+                StreamSource streamSource = new StreamSource(
+                     SystemIDResolver.getAbsoluteURI(href, baseURI));
+                return getDOM(streamSource) ;
+            }
+
+            return getDOM(resolvedSource);
+        }
+        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 e 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 e 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 e 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()));
+        }
+    }
+
+    /**
+     * This method resets  the Transformer to its original configuration
+     * Transformer code is reset to the same state it was when it was
+     * created
+     * @since 1.5
+     */
+    public void reset() {
+
+        _method = null;
+        _encoding = null;
+        _sourceSystemId = null;
+        _errorListener = this;
+        _uriResolver = null;
+        _dom = null;
+        _parameters = null;
+        _indentNumber = 0;
+        setOutputProperties (null);
+        _tohFactory = null;
+        _ostream = null;
+
+    }
+}