/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */
#include "XalanTransformer.hpp"

#include <algorithm>
#include <iostream>
#include <cstring>


#include <xercesc/sax/SAXParseException.hpp>



#include <xalanc/XalanDOM/XalanDOMException.hpp>



#include <xalanc/PlatformSupport/DOMStringHelper.hpp>
#include <xalanc/PlatformSupport/DOMStringPrintWriter.hpp>
#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>
#include <xalanc/PlatformSupport/XalanOutputStreamPrintWriter.hpp>



#include <xalanc/XPath/XObjectFactoryDefault.hpp>
#include <xalanc/XPath/XPathFactoryBlock.hpp>



#include <xalanc/XSLT/StylesheetConstructionContextDefault.hpp>
#include <xalanc/XSLT/StylesheetExecutionContextDefault.hpp>
#include <xalanc/XSLT/StylesheetRoot.hpp>
#include <xalanc/XSLT/XSLTEngineImpl.hpp>
#include <xalanc/XSLT/XSLTInit.hpp>
#include <xalanc/XSLT/XSLTProcessorEnvSupportDefault.hpp>



#include <xalanc/XalanSourceTree/XalanSourceTreeDOMSupport.hpp>
#include <xalanc/XalanSourceTree/XalanSourceTreeParserLiaison.hpp>



#include <xalanc/XalanExtensions/XalanExtensions.hpp>



#include <xalanc/XalanEXSLT/XalanEXSLTCommon.hpp>
#include <xalanc/XalanEXSLT/XalanEXSLTDynamic.hpp>
#include <xalanc/XalanEXSLT/XalanEXSLTMath.hpp>
#include <xalanc/XalanEXSLT/XalanEXSLTSet.hpp>
#include <xalanc/XalanEXSLT/XalanEXSLTString.hpp>
#include <xalanc/XalanEXSLT/XalanEXSLTDateTime.hpp>



//#define XALAN_USE_ICU
#if defined(XALAN_USE_ICU)
#include <xalanc/ICUBridge/ICUBridgeCleanup.hpp>
#include <xalanc/ICUBridge/ICUBridgeCollationCompareFunctor.hpp>
#include <xalanc/ICUBridge/ICUFormatNumberFunctor.hpp>
#endif



#include "XalanCompiledStylesheetDefault.hpp"
#include "XalanDefaultDocumentBuilder.hpp"
#include "XalanDefaultParsedSource.hpp"
#include "XalanTransformerOutputStream.hpp"
#include "XalanTransformerProblemListener.hpp"
#include "XercesDOMParsedSource.hpp"



namespace XALAN_CPP_NAMESPACE {


const XSLTInputSource*  XalanTransformer::s_emptyInputSource = 0;

const XSLTInit*         XalanTransformer::s_xsltInit = 0;


static MemoryManager*   s_initMemoryManager = 0;



XalanTransformer::XalanTransformer(MemoryManager&   theManager):
    m_memoryManager(theManager),
    m_compiledStylesheets(m_memoryManager),
    m_parsedSources(m_memoryManager),
    m_params(m_memoryManager),
    m_functions(m_memoryManager),
    m_traceListeners(m_memoryManager),
    m_errorMessage(1, '\0', m_memoryManager),
    m_useValidation(false),
    m_entityResolver(0),
    m_xmlEntityResolver(0),
    m_errorHandler(0),
    m_externalSchemaLocation(m_memoryManager),
    m_externalNoNamespaceSchemaLocation(m_memoryManager),
    m_problemListener(0),
    m_errorStream(0),
    m_warningStream(&std::cerr),
    m_outputEncoding(m_memoryManager),
    m_topXObjectFactory(XObjectFactoryDefault::create(m_memoryManager)),
    m_stylesheetExecutionContext(StylesheetExecutionContextDefault::create(m_memoryManager))
{
#if defined(XALAN_USE_ICU)
    // Create a collation function for the ICU, and have it
    // cache collators...
    XalanMemMgrAutoPtr<ICUBridgeCollationCompareFunctor>  theICUFunctor(m_memoryManager, ICUBridgeCollationCompareFunctor::create(m_memoryManager, true));

    m_stylesheetExecutionContext->installCollationCompareFunctor(theICUFunctor.get());

    XalanMemMgrAutoPtr<ICUFormatNumberFunctor>  theFormatNumberFunctor(m_memoryManager, ICUFormatNumberFunctor::create(m_memoryManager));
    m_stylesheetExecutionContext->installFormatNumberFunctor(theFormatNumberFunctor.get());
    theICUFunctor.release();
    theFormatNumberFunctor.release();

#endif
}



XalanTransformer::~XalanTransformer()
{
    // Clean up Top-Level Parameters
    clearStylesheetParams();

    using std::for_each;

    // Clean up the XalanCompiledStylesheet vector.
    for_each(
        m_compiledStylesheets.begin(),
        m_compiledStylesheets.end(),
        DeleteFunctor<XalanCompiledStylesheet>(m_memoryManager));

    // Clean up the XalanParsedSource vector.
    for_each(
        m_parsedSources.begin(),
        m_parsedSources.end(),
        DeleteFunctor<XalanParsedSource>(m_memoryManager));

    // Clean up the Function map.
    for_each(
        m_functions.begin(),
        m_functions.end(),
        MapValueDeleteFunctor<FunctionMapType>(m_memoryManager));

#if defined(XALAN_USE_ICU)
    // Uninstall the ICU collation compare functor, and destroy it...
    XalanDestroy(
        m_memoryManager,
        m_stylesheetExecutionContext->uninstallCollationCompareFunctor());

    // Uninstall the ICU format number functor, and destroy it...
    XalanDestroy(
        m_memoryManager,
        m_stylesheetExecutionContext->uninstallFormatNumberFunctor());
#endif

    XalanDestroy(
        m_memoryManager,
        *m_topXObjectFactory);

    XalanDestroy(
        m_memoryManager,
        *m_stylesheetExecutionContext);
}



void
XalanTransformer::initialize(MemoryManager&     theManager)
{
    // Initialize Xalan. 
    XalanMemMgrAutoPtr<XSLTInit>    initGuard(theManager, XSLTInit::create(theManager));
    XalanAutoPtr<XSLTInputSource>   inputSourceGuard(new (&theManager) XSLTInputSource(theManager));
    EnsureFunctionsInstallation     instalGuard(theManager); 

    instalGuard.install();

    instalGuard.release();
    s_xsltInit = initGuard.releasePtr();
    s_emptyInputSource = inputSourceGuard.release();

    s_initMemoryManager = &theManager;
}



void
XalanTransformer::terminate()
{
    assert(s_initMemoryManager != 0);

    {
        const EnsureFunctionsInstallation   uninstallGuard(*s_initMemoryManager);
    }

    delete s_emptyInputSource;

    XalanDestroy(
        *s_initMemoryManager,
        const_cast<XSLTInit*>(s_xsltInit));

    s_emptyInputSource = 0;
    s_xsltInit = 0;
    s_initMemoryManager = 0;
}



void
XalanTransformer::ICUCleanUp()
{
#if defined(XALAN_USE_ICU)
    ICUBridgeCleanup::cleanup();
#endif
}



#if defined(XALAN_USE_ICU)
void
ICUStartUp(MemoryManager&   theManager)
{
    ICUBridgeCleanup::startup(theManager);
}
#else
void
ICUStartUp(MemoryManager&   /*theManager*/)
{
}
#endif



bool
XalanTransformer::getICUAvailable() const
{
#if defined(XALAN_USE_ICU)
    return true;
#else
    return false;
#endif
}



static void
addTraceListeners(
             const XalanTransformer::TraceListenerVectorType&   theTraceListeners,
             XSLTEngineImpl&                                    theEngine)
{
    if (theTraceListeners.empty() == false)
    {
        typedef XalanTransformer::TraceListenerVectorType   TraceListenerVectorType;

        TraceListenerVectorType::const_iterator     theEnd = theTraceListeners.end();

        for(TraceListenerVectorType::const_iterator i = theTraceListeners.begin(); i != theEnd; ++i)
        {
            theEngine.addTraceListener(*i);
        }

        theEngine.setTraceSelects(true);
    }
}



int
XalanTransformer::transform(
            const XSLTInputSource&          theInputSource, 
            const XalanCompiledStylesheet*  theCompiledStylesheet,
            const XSLTResultTarget&         theResultTarget)
{
    const XalanParsedSource*    theParsedSource = 0;
 
    const int   theResult = parseSource(theInputSource, theParsedSource);
 
    if (theResult != 0)
    {
        return theResult;
    }
    else
    {
        assert(theParsedSource != 0);
 
        // Make sure the parsed source is destroyed when
        // the transformation is finished...
        EnsureDestroyParsedSource   theGuard(*this, theParsedSource);

        // Do the transformation...
        return transform(
                        *theParsedSource,
                        theCompiledStylesheet,
                        theResultTarget);
    }
}



int
XalanTransformer::transform(
            const XSLTInputSource&      theInputSource, 
            const XSLTInputSource&      theStylesheetSource,
            const XSLTResultTarget&     theResultTarget)
{
    // Parse the source document.
    const XalanParsedSource*    theParsedSource = 0;

    const int   theResult = parseSource(theInputSource, theParsedSource);

    if (theResult != 0)
    {
        return theResult;
    }
    else
    {
        assert(theParsedSource != 0);

        // Make sure the parsed source is destroyed when
        // the transformation is finished...
        EnsureDestroyParsedSource   theGuard(*this, theParsedSource);

        // Do the transformation...
        return transform(
                        *theParsedSource,
                        theStylesheetSource,
                        theResultTarget);
    }
}



int
XalanTransformer::transform(
            const XSLTInputSource&  theInputSource, 
            const XSLTInputSource&  theStylesheetSource,
            void*                   theOutputHandle, 
            XalanOutputHandlerType  theOutputHandler,
            XalanFlushHandlerType   theFlushHandler)
{
    // Set to output target to the callback 
    XalanTransformerOutputStream    theOutputStream(m_memoryManager, theOutputHandle, theOutputHandler, theFlushHandler);

    XalanOutputStreamPrintWriter    thePrintWriter(theOutputStream);

    XSLTResultTarget                theResultTarget(&thePrintWriter, m_memoryManager);

    // Do the transformation...
    return transform(
                    theInputSource, 
                    theStylesheetSource,
                    theResultTarget);
}



int
XalanTransformer::transform(
            const XalanParsedSource&        theParsedSource, 
            const XalanCompiledStylesheet*  theCompiledStylesheet,
            void*                           theOutputHandle, 
            XalanOutputHandlerType          theOutputHandler,
            XalanFlushHandlerType           theFlushHandler)
{
    // Set to output target to the callback 
    XalanTransformerOutputStream    theOutputStream(m_memoryManager, theOutputHandle, theOutputHandler, theFlushHandler);

    XalanOutputStreamPrintWriter    thePrintWriter(theOutputStream);

    XSLTResultTarget                theResultTarget(&thePrintWriter, m_memoryManager);

    // Do the transformation...
    return transform(
                    theParsedSource,
                    theCompiledStylesheet,
                    theResultTarget);
}



int
XalanTransformer::transform(
            const XSLTInputSource&      theInputSource,         
            void*                       theOutputHandle, 
            XalanOutputHandlerType      theOutputHandler,
            XalanFlushHandlerType       theFlushHandler)
{
    assert(s_emptyInputSource != 0);

    // Set to output target to the callback 
    XalanTransformerOutputStream    theOutputStream(m_memoryManager, theOutputHandle, theOutputHandler, theFlushHandler);

    XalanOutputStreamPrintWriter    thePrintWriter(theOutputStream);

    XSLTResultTarget                theResultTarget(&thePrintWriter, m_memoryManager);

    // Do the transformation...
    return transform(
                    theInputSource, 
                    *s_emptyInputSource,
                    theResultTarget);
}



using xercesc::XMLException;
using xercesc::SAXParseException;
using xercesc::SAXException;



static const XalanDOMString&
FormatSAXParseException(
            XPathExecutionContext&      theExecutionContext,
            const SAXParseException&    theException,
            XalanDOMString&             theErrorMessage)
{
    assert(theException.getMessage() != 0);

    const XalanDOMChar  theDummy = 0;

    const XalanDOMChar* const   theSystemID = theException.getSystemId();

    typedef XPathExecutionContext::GetCachedString  GetCachedString;

    const GetCachedString   theGuard(theExecutionContext);

    XalanDOMString&     theExceptionType = theGuard.get();

    theExceptionType.assign("SAXParseException");

    XSLException::defaultFormat(
        theException.getMessage(),
        theSystemID == 0 ? &theDummy : theSystemID,
        theException.getLineNumber(),
        theException.getColumnNumber(),
        theExceptionType.c_str(),
        theErrorMessage);

    return theErrorMessage;
}



static void
FormatSAXParseException(
            XPathExecutionContext&      theExecutionContext,
            const SAXParseException&    theException,
            CharVectorType&             theErrorMessage)
{
    typedef XPathExecutionContext::GetCachedString  GetCachedString;

    const GetCachedString   theGuard(theExecutionContext);

    TranscodeToLocalCodePage(
        FormatSAXParseException(
            theExecutionContext,
            theException,
            theGuard.get()),
        theErrorMessage,
        true);
}



static void
FormatXalanDOMException(
            XPathExecutionContext&      theExecutionContext,
            const XalanDOMException&    theException,
            CharVectorType&             theErrorMessage)
{
    typedef XPathExecutionContext::GetCachedString  GetCachedString;

    const GetCachedString   theGuard1(theExecutionContext);
    const GetCachedString   theGuard2(theExecutionContext);

    const XalanDOMString&   theMessage =
        XalanMessageLoader::getMessage(
            theGuard1.get(),
            XalanMessages::XalanDOMExceptionCaught_1Param,
            NumberToDOMString(
                theException.getExceptionCode(),
                theGuard2.get()));

    TranscodeToLocalCodePage(theMessage, theErrorMessage, true);
}



static void
LoadErrorMessage(
            XPathExecutionContext&      theExecutionContext,
            XalanMessages::Codes        theCode,
            CharVectorType&             theMessage)
{
    typedef XPathExecutionContext::GetCachedString  GetCachedString;

    const GetCachedString   theGuard(theExecutionContext);

    const XalanDOMString&   theMessageString =
                    XalanMessageLoader::getMessage(
                        theGuard.get(),
                        theCode);

    try
    {
        theMessageString.transcode(theMessage);
    }
    catch(...)
    {
        const char* const   theAlternateErrorMessage =
                "An error occurred transcoding an error message.";

        const XalanDOMString::size_type     theLength =
                length(theAlternateErrorMessage);

        theMessage.resize(theLength + 1);

            using std::strncpy;;

            strncpy(
                &*theMessage.begin(),
                theAlternateErrorMessage,
                theLength);
    }
}



int
XalanTransformer::compileStylesheet(
            const XSLTInputSource&              theStylesheetSource,
            const XalanCompiledStylesheet*&     theCompiledStylesheet)
{
    // Clear the error message.
    m_errorMessage.resize(1, '\0');

    // Store error messages from problem listener.
    XalanDOMString  theErrorMessage(m_memoryManager);

    int     theResult = 0;

    try
    {
        // Create some support objects that are necessary for running the processor...
        XalanSourceTreeDOMSupport       theDOMSupport;

        XalanSourceTreeParserLiaison    theParserLiaison(theDOMSupport, m_memoryManager);

        theParserLiaison.setEntityResolver(m_entityResolver);
        theParserLiaison.setXMLEntityResolver(m_xmlEntityResolver);
        theParserLiaison.setErrorHandler(m_errorHandler);

        // Hook the two together...
        theDOMSupport.setParserLiaison(&theParserLiaison);

        // Create some more support objects...
        XSLTProcessorEnvSupportDefault  theXSLTProcessorEnvSupport(m_memoryManager);

        XObjectFactoryDefault   theXObjectFactory(m_memoryManager);

        XPathFactoryBlock       theXPathFactory(m_memoryManager);

        // Create a processor...
        XSLTEngineImpl  theProcessor(
                m_memoryManager,
                theParserLiaison,
                theXSLTProcessorEnvSupport,
                theDOMSupport,
                theXObjectFactory,
                theXPathFactory);

        // Create a problem listener and send output to a XalanDOMString.
        DOMStringPrintWriter    thePrintWriter(theErrorMessage);

        XalanTransformerProblemListener     theProblemListener( m_memoryManager, m_warningStream, &thePrintWriter);

        if (m_problemListener == 0)
        {
            theProcessor.setProblemListener(&theProblemListener);
        }
        else
        {
            theProcessor.setProblemListener(m_problemListener);
        }

        // Allocate the memory now, to avoid leaking if push_back() fails.
        m_compiledStylesheets.reserve(m_compiledStylesheets.size() + 1);

        // Create a new XalanCompiledStylesheet.
        theCompiledStylesheet =
            XalanCompiledStylesheetDefault::create(
                        m_memoryManager,
                        theStylesheetSource,
                        theProcessor,
                        m_errorHandler,
                        m_entityResolver);

        // Store it in a vector.
        m_compiledStylesheets.push_back(theCompiledStylesheet);
    }
    catch(const XSLException&   e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);
        }

        theResult = -1;
    }
    catch(const SAXParseException&  e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            FormatSAXParseException(
                *m_stylesheetExecutionContext,
                e,
                m_errorMessage);
        }

        theResult = -2;
    }
    catch(const SAXException&   e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);
        }

        theResult = -2;
    }
    catch(const XMLException&   e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);
        }

        theResult = -3;
    }
    catch(const XalanDOMException&  e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            FormatXalanDOMException(
                *m_stylesheetExecutionContext,
                e,
                m_errorMessage);
        }

        theResult = -4;
    }

    return theResult;
}



int
XalanTransformer::destroyStylesheet(const XalanCompiledStylesheet*  theStylesheet)
{
    using std::find;

    const CompiledStylesheetPtrVectorType::iterator     i =
        find(
            m_compiledStylesheets.begin(),
            m_compiledStylesheets.end(),
            theStylesheet);

    if (i == m_compiledStylesheets.end())
    {
        LoadErrorMessage(
            *m_stylesheetExecutionContext,
            XalanMessages::InvalidCompiledStylesheetProvided,
            m_errorMessage);

        return -1;
    }
    else
    {
        m_compiledStylesheets.erase(i);

        XalanDestroy(
            m_memoryManager,
            const_cast<XalanCompiledStylesheet*>(theStylesheet));

        return 0;
    }
}



int
XalanTransformer::parseSource(
            const XSLTInputSource&      theInputSource,
            const XalanParsedSource*&   theParsedSource,
            bool                        useXercesDOM)
{
    // Clear the error message.
    m_errorMessage.clear();
    m_errorMessage.push_back(0);

    int theResult = 0;

    try
    {
        // Allocate the memory now, to avoid leaking if push_back() fails.
        m_parsedSources.reserve(m_parsedSources.size() + 1);

        if(useXercesDOM == true)
        {
            theParsedSource =
                XercesDOMParsedSource::create(
                        m_memoryManager,
                        theInputSource,
                        m_useValidation,
                        m_errorHandler,
                        m_entityResolver,
                        m_xmlEntityResolver,
                        getExternalSchemaLocation(),
                        getExternalNoNamespaceSchemaLocation());
        }
        else
        {
            theParsedSource =
                XalanDefaultParsedSource::create(
                        m_memoryManager,
                        theInputSource,
                        m_useValidation,
                        m_errorHandler,
                        m_entityResolver,
                        m_xmlEntityResolver,
                        getExternalSchemaLocation(),
                        getExternalNoNamespaceSchemaLocation());
        }

        // Store it in a vector.
        m_parsedSources.push_back(theParsedSource);
    }
    catch(const XSLException&   e)
    {
        XalanDOMString theBuffer(m_memoryManager);

        e.defaultFormat(theBuffer);

        TranscodeToLocalCodePage(theBuffer, m_errorMessage, true);

        theResult = -1;
    }
    catch(const SAXParseException&  e)
    {
        FormatSAXParseException(
            *m_stylesheetExecutionContext,
            e,
            m_errorMessage);

        theResult = -2;
    }
    catch(const SAXException&   e)
    {
        TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);

        theResult = -2;
    }
    catch(const XMLException&   e)
    {
        TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);

        theResult = -3;
    }
    catch(const XalanDOMException&  e)
    {
        FormatXalanDOMException(
            *m_stylesheetExecutionContext,
            e,
            m_errorMessage);

        theResult = -4;
    }

    return theResult;
}



int
XalanTransformer::destroyParsedSource(const XalanParsedSource*  theParsedSource)
{
    using std::find;

    const ParsedSourcePtrVectorType::iterator   i =
        find(
            m_parsedSources.begin(),
            m_parsedSources.end(),
            theParsedSource);

    if (i == m_parsedSources.end())
    {
        LoadErrorMessage(
            *m_stylesheetExecutionContext,
            XalanMessages::InvalidParsedSourceProvided,
            m_errorMessage);

        return -1;
    }
    else
    {
        m_parsedSources.erase(i);

        assert(theParsedSource != 0);

        XalanDestroy(
            m_memoryManager,
            const_cast<XalanParsedSource*>(theParsedSource));

        return 0;
    }
}

void
XalanTransformer::setStylesheetParam(
            const XalanDOMString&    qname,
            const XalanDOMString&    expression)
{
    m_params[qname].m_expression = expression;
}

void
XalanTransformer::setStylesheetParam(
            const XalanDOMString&    qname,
            XObjectPtr               object)
{
    m_params[qname].m_value = object;
}


void
XalanTransformer::setStylesheetParam(
            const char*      qname,
            XObjectPtr       object)
{
    setStylesheetParam(
        XalanDOMString(qname, m_memoryManager),
        object);
}

void
XalanTransformer::setStylesheetParam(
            const char*     qname,
            const char*     expression)
{
    setStylesheetParam(
        XalanDOMString(qname, m_memoryManager),
        XalanDOMString(expression, m_memoryManager));
}

void
XalanTransformer::setStylesheetParam(
            const char*     qname,
            double          number)
{
    setStylesheetParam(
        XalanDOMString(qname, m_memoryManager),
        m_topXObjectFactory->createNumber(number));
}

void
XalanTransformer::setStylesheetParam(
            const XalanDOMString&   qname,
            double                  number)
{
    setStylesheetParam(
        qname,
        m_topXObjectFactory->createNumber(number));
}

void
XalanTransformer::setStylesheetParam(
            const char*             qname,
            XalanNode*        nodeset)
{
    setStylesheetParam(
        XalanDOMString(qname, m_memoryManager),
        m_topXObjectFactory->createNodeSet(nodeset));
}

void
XalanTransformer::setStylesheetParam(
            const XalanDOMString&   qname,
            XalanNode*        nodeset)
{
    setStylesheetParam(
        qname,
        m_topXObjectFactory->createNodeSet(nodeset));
}

bool
XalanTransformer::removeTraceListener(TraceListener*    theTraceListener)
{
    using std::find;

    const TraceListenerVectorType::iterator     i =
        find(
            m_traceListeners.begin(),
            m_traceListeners.end(),
            theTraceListener);

    if (i == m_traceListeners.end())
    {
        return false;
    }
    else
    {
        m_traceListeners.erase(i);

        return true;
    }
}



XalanDocumentBuilder*
XalanTransformer::createDocumentBuilder(const XalanDOMString&   theURI)
{

    m_parsedSources.reserve(m_parsedSources.size() + 1);

    XalanDocumentBuilder* const     theNewBuilder = XalanDefaultDocumentBuilder::create( m_memoryManager, theURI);

    m_parsedSources.push_back(theNewBuilder);

    return theNewBuilder;
}



void
XalanTransformer::destroyDocumentBuilder(XalanDocumentBuilder*  theDocumentBuilder)
{
    destroyParsedSource(theDocumentBuilder);
}



void
XalanTransformer::installExternalFunction(
            const XalanDOMString&   theNamespace,
            const XalanDOMString&   functionName,
            const Function&         function)
{
    const XalanQNameByValue     theQName(theNamespace, functionName, m_memoryManager);

    Function*&  theFunction =
        m_functions[theQName];

    Function* const     theOldFunction =
        theFunction;

    theFunction = function.clone(m_memoryManager);

    XalanDestroy<Function>(
        m_memoryManager,
        theOldFunction);
}



void
XalanTransformer::installExternalFunctionGlobal(
            const XalanDOMString&   theNamespace,
            const XalanDOMString&   functionName,
            const Function&         function)
{
    XSLTProcessorEnvSupportDefault::installExternalFunctionGlobal(
            theNamespace,
            functionName,
            function);
}



void
XalanTransformer::uninstallExternalFunction(
            const XalanDOMString&   theNamespace,
            const XalanDOMString&   functionName)
{
    const XalanQNameByValue     theQName(theNamespace, functionName, m_memoryManager);

    FunctionMapType::iterator   i =
        m_functions.find(theQName);

    if (i != m_functions.end())
    {
        Function* const     theFunction = (*i).second;
        assert(theFunction != 0);

        m_functions.erase(i);

        XalanDestroy(
            m_memoryManager,
            *theFunction);
    }
}



void
XalanTransformer::uninstallExternalFunctionGlobal(
            const XalanDOMString&   theNamespace,
            const XalanDOMString&   functionName)
{
    XSLTProcessorEnvSupportDefault::uninstallExternalFunctionGlobal(
            theNamespace,
            functionName);
}



int
XalanTransformer::getIndent() const
{
    return m_stylesheetExecutionContext->getIndent();
}



void
XalanTransformer::setIndent(int indentAmount)
{
    m_stylesheetExecutionContext->setIndent(indentAmount);
}



XalanTransformer::eEscapeURLs
XalanTransformer::getEscapeURLs() const
{
    eEscapeURLs     escapeValue = eEscapeURLsDefault;

    switch(m_stylesheetExecutionContext->getEscapeURLs())
    {
    case StylesheetExecutionContextDefault::eEscapeURLsDefault:
        break;

    case StylesheetExecutionContextDefault::eEscapeURLsNo:
        escapeValue = eEscapeURLsNo;
        break;

    case StylesheetExecutionContextDefault::eEscapeURLsYes:
        escapeValue = eEscapeURLsYes;
        break;

    default:
        assert(false);
        break;
    }

    return escapeValue;
}


void
XalanTransformer::setEscapeURLs(eEscapeURLs     value)
{
    StylesheetExecutionContextDefault::eEscapeURLs  escapeValue =
        StylesheetExecutionContextDefault::eEscapeURLsDefault;

    switch(value)
    {
    case eEscapeURLsDefault:
        break;

    case eEscapeURLsNo:
        escapeValue = StylesheetExecutionContextDefault::eEscapeURLsNo;
        break;

    case eEscapeURLsYes:
        escapeValue = StylesheetExecutionContextDefault::eEscapeURLsYes;
        break;

    default:
        assert(false);
        break;
    }

    m_stylesheetExecutionContext->setEscapeURLs(escapeValue);
}



XalanTransformer::eOmitMETATag
XalanTransformer::getOmitMETATag() const
{
    eOmitMETATag    omitValue = eOmitMETATagDefault;

    switch(m_stylesheetExecutionContext->getOmitMETATag())
    {
    case StylesheetExecutionContextDefault::eOmitMETATagDefault:
        break;

    case StylesheetExecutionContextDefault::eOmitMETATagNo:
        omitValue = eOmitMETATagNo;
        break;

    case StylesheetExecutionContextDefault::eOmitMETATagYes:
        omitValue = eOmitMETATagYes;
        break;

    default:
        assert(false);
        break;
    }

    return omitValue;
}



void
XalanTransformer::setOmitMETATag(eOmitMETATag       value)
{
    StylesheetExecutionContextDefault::eOmitMETATag     omitValue =
        StylesheetExecutionContextDefault::eOmitMETATagDefault;

    switch(value)
    {
    case eOmitMETATagDefault:
        break;

    case eOmitMETATagNo:
        omitValue = StylesheetExecutionContextDefault::eOmitMETATagNo;
        break;

    case eOmitMETATagYes:
        omitValue = StylesheetExecutionContextDefault::eOmitMETATagYes;
        break;

    default:
        assert(false);
        break;
    }

    m_stylesheetExecutionContext->setOmitMETATag(omitValue);
}



void
XalanTransformer::reset()
{
    try
    {
        // Reset objects.
        m_stylesheetExecutionContext->setXPathEnvSupport(0);

        m_stylesheetExecutionContext->setDOMSupport(0);
        
        m_stylesheetExecutionContext->setXObjectFactory(0);
        
        m_stylesheetExecutionContext->setXSLTProcessor(0);

        m_stylesheetExecutionContext->reset();

// JIRA-451 Preserve top-level parameters across reset()
// The reset() is done to prepare for a new transformation
// by reusing the m_stylesheetExecutionContext.
//
// Absense of clearStylesheetParams() makes the top-level params
// sticky across transformations. The clearStylesheetParams()
// purges the top-level parameters from the XalanTransformer
// instance.
//
// Presence of clearStylesheetParams() will require the application
// to submit a new set of top-level parameters to the XalanTransformer
// if needed for the next doTransform() operation.
//
//      clearStylesheetParams();

    }
    catch(...)
    {
    }
}



XalanTransformer::EnsureReset::~EnsureReset()
{
    m_transformer.m_stylesheetExecutionContext->reset();

    m_transformer.reset();
}



int
XalanTransformer::doTransform(
            const XalanParsedSource&        theParsedXML,
            const XalanCompiledStylesheet*  theCompiledStylesheet,
            const XSLTInputSource*          theStylesheetSource,
            const XSLTResultTarget&         theResultTarget)
{
    int     theResult = 0;

    // Clear the error message.
    m_errorMessage.resize(1, '\0');

    // Store error messages from problem listener.
    XalanDOMString  theErrorMessage(m_memoryManager);

    try
    {
        XalanDocument* const    theSourceDocument = theParsedXML.getDocument();
        assert(theSourceDocument != 0);

        // Create the helper object that is necessary for running the processor...
        XalanMemMgrAutoPtr<XalanParsedSourceHelper>   theHelper(m_memoryManager, theParsedXML.createHelper(m_memoryManager));

        assert(theHelper.get() != 0);

        DOMSupport&         theDOMSupport = theHelper->getDOMSupport();

        XMLParserLiaison&   theParserLiaison = theHelper->getParserLiaison();

        theParserLiaison.setExecutionContext(*m_stylesheetExecutionContext);

        theParserLiaison.setEntityResolver(m_entityResolver);
        theParserLiaison.setXMLEntityResolver(m_xmlEntityResolver);
        theParserLiaison.setErrorHandler(m_errorHandler);
        theParserLiaison.setUseValidation(m_useValidation);

        // Create some more support objects...
        XSLTProcessorEnvSupportDefault  theXSLTProcessorEnvSupport(m_memoryManager);

        const XalanDOMString&   theSourceURI = theParsedXML.getURI();

        if (theSourceURI.empty() == false)
        {
            theXSLTProcessorEnvSupport.setSourceDocument(theSourceURI, theSourceDocument);
        }

        // Set the functions if any.
        {
            for (FunctionMapType::const_iterator i = m_functions.begin();
                    i != m_functions.end(); ++i)
            {
                assert((*i).second != 0);

                theXSLTProcessorEnvSupport.installExternalFunctionLocal(
                        (*i).first.getNamespace(),
                        (*i).first.getLocalPart(),
                        *(*i).second);
            }
        }

        XObjectFactoryDefault   theXObjectFactory(m_memoryManager);

        XPathFactoryBlock       theXPathFactory(m_memoryManager);

        // Create a processor...
        XSLTEngineImpl  theProcessor(
                    m_memoryManager,
                    theParserLiaison,
                    theXSLTProcessorEnvSupport,
                    theDOMSupport,
                    theXObjectFactory,
                    theXPathFactory);

        theXSLTProcessorEnvSupport.setProcessor(&theProcessor);

        const EnsureReset   theReset(*this);

        // Set up the stylesheet execution context.
        m_stylesheetExecutionContext->setXPathEnvSupport(&theXSLTProcessorEnvSupport);

        m_stylesheetExecutionContext->setDOMSupport(&theDOMSupport);

        m_stylesheetExecutionContext->setXObjectFactory(&theXObjectFactory);

        m_stylesheetExecutionContext->setXSLTProcessor(&theProcessor);

        // Create a problem listener and send output to a XalanDOMString.  Do this before
        // pushing params, since there could be a problem resolving a QName.
        DOMStringPrintWriter    thePrintWriter(theErrorMessage);

        XalanTransformerProblemListener     theProblemListener(m_memoryManager, m_warningStream, &thePrintWriter);

        if (m_problemListener == 0)
        {
            theProcessor.setProblemListener(&theProblemListener);
        }
        else
        {
            theProcessor.setProblemListener(m_problemListener);
        }

        {
            typedef ParamMapType::const_iterator    const_iterator;

            theProcessor.clearStylesheetParams();

            // Set the parameters if any.
            for (const_iterator i = m_params.begin();
                    i != m_params.end();
                        ++i)
            {
                const XalanDOMString&      theName = (*i).first;
                const XalanParamHolder&    theCurrent= (*i).second;
                const XalanDOMString&      theExpression = theCurrent.m_expression;
                XObjectPtr                 theObject = theCurrent.m_value;

                if (theExpression.length() > 0)
                {
                    theProcessor.setStylesheetParam(theName, theExpression);
                }
                else
                {
                    theProcessor.setStylesheetParam(theName, theObject);
                }
            }
        }

        // Create an input source for the source document...
        XSLTInputSource     theDocumentInputSource(theSourceDocument, m_memoryManager);

        // Set the system ID, so relative URIs are resolved properly...
        theDocumentInputSource.setSystemId(theSourceURI.c_str());

        addTraceListeners(m_traceListeners, theProcessor);

        // We may need to change the output encoding, so
        // we make a copy of the result target.
        XSLTResultTarget    tempResultTarget(theResultTarget, m_memoryManager);

        if (tempResultTarget.getEncoding().length() == 0 && m_outputEncoding.length() != 0)
        {
            tempResultTarget.setEncoding(m_outputEncoding);
        }

        if (theCompiledStylesheet != 0)
        {
            assert(theStylesheetSource == 0 &&
                   theCompiledStylesheet->getStylesheetRoot() != 0);

            m_stylesheetExecutionContext->setStylesheetRoot(theCompiledStylesheet->getStylesheetRoot());

            // Do the transformation...
            theProcessor.process(
                        theDocumentInputSource,
                        tempResultTarget,
                        *m_stylesheetExecutionContext);
        }
        else
        {
            assert(theStylesheetSource != 0);

            // Create a stylesheet construction context, 
            // using the stylesheet's factory support objects.
            StylesheetConstructionContextDefault    theStylesheetConstructionContext(
                            m_memoryManager,
                            theProcessor,
                            theXPathFactory);

            // Do the transformation...
            theProcessor.process(
                            theDocumentInputSource,
                            *theStylesheetSource,
                            tempResultTarget,
                            theStylesheetConstructionContext,
                            *m_stylesheetExecutionContext);
        }
    }
    catch(const XSLException&   e)
    {
        if (theErrorMessage.empty() == true)
        {
            e.defaultFormat(theErrorMessage);
        }

        TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);

        theResult = -1;
    }
    catch(const SAXParseException&  e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            FormatSAXParseException(
                *m_stylesheetExecutionContext,
                e,
                m_errorMessage);
        }

        theResult = -2;
    }
    catch(const SAXException&   e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);
        }

        theResult = -2;
    }
    catch(const XMLException&   e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            TranscodeToLocalCodePage(e.getMessage(), m_errorMessage, true);
        }

        theResult = -3;
    }
    catch(const XalanDOMException&  e)
    {
        if (theErrorMessage.empty() == false)
        {
            TranscodeToLocalCodePage(theErrorMessage, m_errorMessage, true);
        }
        else
        {
            FormatXalanDOMException(
                *m_stylesheetExecutionContext,
                e,
                m_errorMessage);
        }

        theResult = -4;
    }

    return theResult;
}

void
XalanTransformer::EnsureFunctionsInstallation::install()
{
    XalanExtensionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTCommonFunctionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTDynamicFunctionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTMathFunctionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTSetFunctionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTStringFunctionsInstaller::installGlobal(m_memoryManagement);
    XalanEXSLTDateTimeFunctionsInstaller::installGlobal(m_memoryManagement);
}

XalanTransformer::EnsureFunctionsInstallation::~EnsureFunctionsInstallation()
{
    if ( !m_release)
    {
        XalanExtensionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTCommonFunctionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTDynamicFunctionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTMathFunctionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTSetFunctionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTStringFunctionsInstaller::uninstallGlobal(m_memoryManagement);
        XalanEXSLTDateTimeFunctionsInstaller::uninstallGlobal(m_memoryManagement);

#if defined(XALAN_USE_ICU)
        XPath::uninstallFunction(XPathFunctionTable::s_formatNumber);
#endif
    }
}


}
