/*
 * Copyright 1999-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.
 */

// Class header file
#include "StylesheetRoot.hpp"



#include <algorithm>
#include <memory>



#include <xalanc/XalanDOM/XalanDocumentFragment.hpp>



#include <xalanc/PlatformSupport/AttributeListImpl.hpp>
#include <xalanc/PlatformSupport/PrintWriter.hpp>
#include <xalanc/PlatformSupport/StringTokenizer.hpp>
#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>



#include <xalanc/DOMSupport/DOMServices.hpp>



#include <xalanc/XPath/XalanQNameByReference.hpp>
#include <xalanc/XPath/XPathFactory.hpp>
#include <xalanc/XPath/XPathProcessor.hpp>



#include <xalanc/XMLSupport/FormatterToHTML.hpp>
#include <xalanc/XMLSupport/FormatterToText.hpp>
#include <xalanc/XMLSupport/FormatterToXML.hpp>



#include "Constants.hpp"
#include "ElemApplyTemplates.hpp"
#include "ElemAttributeSet.hpp"
#include "ElemTemplate.hpp"
#include "ElemValueOf.hpp"
#include "KeyTable.hpp"
#include "StylesheetConstructionContext.hpp"
#include "StylesheetExecutionContext.hpp"
#include "TraceListener.hpp"
#include "XSLTResultTarget.hpp"



//#define XALAN_VQ_SPECIAL_TRACE
#if defined(XALAN_VQ_SPECIAL_TRACE)
#include "C:/Program Files/Rational/Quantify/pure.h"
#endif



XALAN_CPP_NAMESPACE_BEGIN



StylesheetRoot::StylesheetRoot(
        const XalanDOMString&           baseIdentifier,
        StylesheetConstructionContext&  constructionContext) :
    Stylesheet(*this,
               baseIdentifier,
               constructionContext),    
    m_version(constructionContext.getMemoryManager()),
    m_indentResult(eIndentNoImplicit),
    m_encoding(constructionContext.getMemoryManager()),
    m_mediatype(constructionContext.getMemoryManager()),
    m_doctypeSystem(constructionContext.getMemoryManager()),
    m_doctypePublic(constructionContext.getMemoryManager()),
    m_omitxmlDecl(false),
    m_standalone(constructionContext.getMemoryManager()),
    m_resultNameSpaceURL(constructionContext.getMemoryManager()),
    m_outputMethod(FormatterListener::OUTPUT_METHOD_NONE),
    m_cdataSectionElems(constructionContext.getMemoryManager()),
    m_hasCDATASectionElems(false),
    m_importStack(constructionContext.getMemoryManager()),
    m_defaultTextRule(0),
    m_defaultRule(0),
    m_defaultRootRule(0),
    m_needToBuildKeysTable(false),
    m_outputEscapeURLs(true),
    m_indentAmount(-1),
    m_omitMETATag(false),
    m_elemNumberNextID(0),
    m_attributeSetsMap(constructionContext.getMemoryManager())
{
    // Our base class has already resolved the URI and pushed it on
    // the back of the include stack, so get it from there...
    assert(getIncludeStack().empty() == false);

    m_importStack.push_back(getIncludeStack().back());
}               


StylesheetRoot*
StylesheetRoot::create(
                           MemoryManagerType&              theManager,
                           const XalanDOMString&            baseIdentifier,
                           StylesheetConstructionContext&   constructionContext)
{
    typedef StylesheetRoot ThisType;

    XalanMemMgrAutoPtr<ThisType, false> theGuard( theManager , (ThisType*)theManager.allocate(sizeof(ThisType)));

    ThisType* theResult = theGuard.get();

    new (theResult) ThisType(baseIdentifier, constructionContext);
                             
    theGuard.release();

    return theResult;
}

StylesheetRoot::~StylesheetRoot()
{
}



void
StylesheetRoot::postConstruction(StylesheetConstructionContext&     constructionContext)
{
    // Chain-up first...
    Stylesheet::postConstruction(constructionContext);

    initDefaultRule(constructionContext);

    {
        AttributeSetMapType::iterator           theCurrentMap = m_attributeSetsMap.begin();
        const AttributeSetMapType::iterator     theEndMap = m_attributeSetsMap.end();

        while(theCurrentMap != theEndMap)
        {
            AttributeSetVectorType::iterator        theCurrentVector = (*theCurrentMap).second.begin();
            const AttributeSetVectorType::iterator  theEndVector = (*theCurrentMap).second.end();

            while(theCurrentVector != theEndVector)
            {
                (*theCurrentVector)->postConstruction(constructionContext, getNamespacesHandler());

                ++theCurrentVector;
            }

            ++theCurrentMap;
        }   
    }

    // We may need to build keys, since we may have inherited them from
    // our imports.
    if (m_needToBuildKeysTable == false && m_keyDeclarations.empty() == false)
    {
        m_needToBuildKeysTable = true;
    }

    // cdata-section-elements only applies to the XML output method...
    if (m_outputMethod != FormatterListener::OUTPUT_METHOD_XML &&
        m_outputMethod != FormatterListener::OUTPUT_METHOD_NONE)
    {
        m_cdataSectionElems.clear();
    }
    else if (m_cdataSectionElems.empty() == false)
    {
        XALAN_USING_STD(sort)

        sort(
            m_cdataSectionElems.begin(),
            m_cdataSectionElems.end(),
            pointer_less<XalanQName>());

        m_hasCDATASectionElems = true;
    }
}



typedef StylesheetExecutionContext::GetCachedString     GetCachedString;



void
StylesheetRoot::process(
            XalanNode*                      sourceTree, 
            XSLTResultTarget&               outputTarget,
            StylesheetExecutionContext&     executionContext) const
{
    assert(m_defaultRule != 0);
    assert(m_defaultTextRule != 0);
    assert(m_defaultRootRule != 0);

    // Find the root pattern in the XSL.
    const ElemTemplateElement*      rootRule =
            findTemplate(executionContext, sourceTree);

    if(0 == rootRule)
    {
        rootRule = m_defaultRootRule;
    }
    assert(rootRule != 0);

    executionContext.setStylesheetRoot(this);

    setupFormatterListener(outputTarget, executionContext);

    executionContext.setRootDocument(sourceTree);

#if defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
    typedef StylesheetExecutionContext::PushAndPopContextMarker PushAndPopContextMarker;
    typedef StylesheetExecutionContext::PushAndPopElementFrame  PushAndPopElementFrame;
    typedef StylesheetExecutionContext::ResolveAndClearTopLevelParams   ResolveAndClearTopLevelParams;

    PushAndPopContextMarker     thePushAndPopContextMarker(executionContext);

    PushAndPopElementFrame      thePushAndPopElementFrame(
                executionContext,
                0);

    ResolveAndClearTopLevelParams   theResolveAndClearTopLevelParams(executionContext);
#else
    executionContext.pushContextMarker();
    executionContext.pushElementFrame(0);
    executionContext.resolveTopLevelParams();
#endif

#if defined(XALAN_VQ_SPECIAL_TRACE)
    QuantifyStartRecordingData();
#endif

    executionContext.startDocument();

    executionContext.pushCurrentMode(&s_emptyQName);

    const XPathExecutionContext::CurrentNodePushAndPop  theCurrentNodePushAndPop(executionContext, sourceTree);

    // Output the action of the found root rule.  All processing
    // occurs from here.
    
    rootRule->execute(executionContext);

    // At this point, anything transient during the tranformation
    // may have been deleted, so we may not refer to anything the
    // execution context may have created for us.
    executionContext.endDocument();

#if defined(XALAN_VQ_SPECIAL_TRACE)
    QuantifyStopRecordingData();
#endif

#if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
    executionContext.clearTopLevelParams();
    executionContext.popElementFrame();
    executionContext.popContextMarker();
#endif
}



const XalanDOMString&
StylesheetRoot::getEncoding(const XSLTResultTarget&     outputTarget) const
{
    const XalanDOMString&   theEncoding = outputTarget.getEncoding();

    if (theEncoding.length() != 0)
    {
        return theEncoding;
    }
    else
    {
        return m_encoding;
    }
}



FormatterListener*
StylesheetRoot::setupFormatterListener(
            XSLTResultTarget&               outputTarget,
            StylesheetExecutionContext&     executionContext) const
{
    FormatterListener*  flistener = outputTarget.getFormatterListener();

    if (flistener != 0)
    {
        // Do encoding stuff here...
    }
    else if(0 != outputTarget.getCharacterStream() ||
            0 != outputTarget.getByteStream() ||
            0 != outputTarget.getStream() ||
            0 != length(outputTarget.getFileName()))
    {
        /*
         * Output target has a character or byte stream or file
         */
        Writer*     pw = 0;

        if(0 != outputTarget.getCharacterStream())
        {
            pw = outputTarget.getCharacterStream();
        }
        else
        {
            if(0 != outputTarget.getByteStream())
            {
                pw = executionContext.createPrintWriter(*outputTarget.getByteStream());
            }
            else if (0 != outputTarget.getStream())
            {
                pw = executionContext.createPrintWriter(outputTarget.getStream());
            }
            else if(!isEmpty(outputTarget.getFileName()))
            {
                const GetCachedString   theGuard(executionContext);

                pw = executionContext.createPrintWriter(
                            outputTarget.getFileName(),
                            theGuard.get());
            }
            else
            {
                const GetCachedString   theGuard(executionContext);

                executionContext.error(
                    XalanMessageLoader::getMessage(
                        theGuard.get(),
                        XalanMessages::NoValidResultTarget),
                    executionContext.getCurrentNode(),
                    0);
            }
        }

        int     indentAmount = executionContext.getIndent();

        // If the indent amount is less than 0, that means use
        // the value specified in the stylesheet.
        if (indentAmount < 0)
        {
            indentAmount = m_indentAmount;
        }

        const bool  doIndent = (indentAmount > -1) ? true : getOutputIndent();

        const XalanDOMString&   theEncoding = getEncoding(outputTarget);

        switch(m_outputMethod)
        {
        case FormatterListener::OUTPUT_METHOD_HTML:
            {
                if (doIndent == true && indentAmount < 0)
                {
                    indentAmount = FormatterToHTML::eDefaultIndentAmount;
                }

                // Start with the default that was set in the stylesheet...
                bool    outputEscapeURLs = m_outputEscapeURLs;

                {
                    const StylesheetExecutionContext::eEscapeURLs   eEscapeURLs =
                        executionContext.getEscapeURLs();

                    // If it's anything other than StylesheetExecutionContext::eEscapeURLsDefault,
                    // use the property from the execution context...
                    if (eEscapeURLs == StylesheetExecutionContext::eEscapeURLsNo)
                    {
                        outputEscapeURLs = false;
                    }
                    else if (eEscapeURLs == StylesheetExecutionContext::eEscapeURLsYes)
                    {
                        outputEscapeURLs = true;
                    }
                }

                // Start with the default that was set in the stylesheet...
                bool    omitMETATag = m_omitMETATag;

                {
                    const StylesheetExecutionContext::eOmitMETATag  eOmitMETATag =
                        executionContext.getOmitMETATag();

                    // If it's anything other than StylesheetExecutionContext::eOmitMETATagDefault,
                    // use the property from the execution context...
                    if (eOmitMETATag == StylesheetExecutionContext::eOmitMETATagNo)
                    {
                        omitMETATag = false;
                    }
                    else if (eOmitMETATag == StylesheetExecutionContext::eOmitMETATagYes)
                    {
                        omitMETATag = true;
                    }
                }

                flistener = executionContext.createFormatterToHTML(
                                *pw,
                                theEncoding,
                                m_mediatype,
                                m_doctypeSystem,
                                m_doctypePublic,
                                doIndent,
                                indentAmount,
                                outputEscapeURLs,
                                omitMETATag);
            }
            break;

        case FormatterListener::OUTPUT_METHOD_TEXT:
            flistener = executionContext.createFormatterToText(*pw, theEncoding);
            break;

        case FormatterListener::OUTPUT_METHOD_NONE:
        case FormatterListener::OUTPUT_METHOD_XML:
        default:
            // Make sure we don't have a negative indent amount if we're
            // indenting
            if (doIndent == true && indentAmount < 0)
            {
                indentAmount = FormatterToXML::eDefaultIndentAmount;
            }

            flistener = executionContext.createFormatterToXML(
                        *pw, m_version, doIndent, indentAmount, theEncoding, m_mediatype,
                        m_doctypeSystem, m_doctypePublic, !m_omitxmlDecl, m_standalone);
            break;
        }

        executionContext.setFormatterListener(flistener);
    }
    else
    {
        const GetCachedString   theGuard(executionContext);

        executionContext.error(
            XalanMessageLoader::getMessage(
                theGuard.get(),
                XalanMessages::NoValidResultTarget),
            executionContext.getCurrentNode(),
            0);
    }

    executionContext.setFormatterListener(flistener);

    return flistener;
}



void 
StylesheetRoot::processOutputSpec(
            const XalanDOMChar*             name, 
            const AttributeListType&        atts,
            StylesheetConstructionContext&  constructionContext)
{
    const unsigned int      nAttrs = atts.getLength();

    const LocatorType* const    theLocator = constructionContext.getLocatorFromStack();

    for(unsigned int i = 0; i < nAttrs; i++)
    {
        const XalanDOMChar* const   aname = atts.getName(i);

        if(equals(aname, Constants::ATTRNAME_OUTPUT_METHOD))
        {
            const XalanDOMChar* const   method = atts.getValue(i);

            if(equals(method, Constants::ATTRVAL_OUTPUT_METHOD_HTML))
            {
                m_outputMethod = FormatterListener::OUTPUT_METHOD_HTML;
            }
            else if(equals(method, Constants::ATTRVAL_OUTPUT_METHOD_XML))
            {
                m_outputMethod = FormatterListener::OUTPUT_METHOD_XML;
            }
            else if(equals(method, Constants::ATTRVAL_OUTPUT_METHOD_TEXT))
            {
                m_outputMethod = FormatterListener::OUTPUT_METHOD_TEXT;
            }
            else
            {
                const StylesheetConstructionContext::GetCachedString    theGuard(constructionContext);

                constructionContext.warn(
                    XalanMessageLoader::getMessage(
                        theGuard.get(),
                        XalanMessages::OutputHasAnUnknownMethod_1Param,
                        method),
                    0,
                    theLocator);
            }
        }
        else if(equals(aname, Constants::ATTRNAME_OUTPUT_VERSION))
        {
            m_version = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_INDENT))
        {
            m_indentResult =
                getYesOrNo(aname, atts.getValue(i), constructionContext) == true ?
                    eIndentYesExplicit :
                    eIndentNoExplicit;
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_ENCODING))
        {
            m_encoding = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_MEDIATYPE))
        {
            m_mediatype = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_DOCTYPE_SYSTEM))
        {
            m_doctypeSystem = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_DOCTYPE_PUBLIC))
        {
            m_doctypePublic = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_OMITXMLDECL))
        {
            m_omitxmlDecl = getYesOrNo(aname, atts.getValue(i), constructionContext);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_STANDALONE))
        {
            m_standalone = atts.getValue(i);
        }
        else if(equals(aname,Constants::ATTRNAME_OUTPUT_CDATA_SECTION_ELEMENTS))
        {
            if (m_outputMethod == FormatterListener::OUTPUT_METHOD_NONE ||
                m_outputMethod == FormatterListener::OUTPUT_METHOD_XML)
            {
                StringTokenizer theTokenizer(atts.getValue(i));

                StringTokenizer::size_type  theTokenCount =
                    theTokenizer.countTokens();

                m_cdataSectionElems.reserve(m_cdataSectionElems.size() + theTokenCount);

                StylesheetConstructionContext::GetAndReleaseCachedString theGuard(constructionContext);

                XalanDOMString& theToken = theGuard.get();

                while(theTokenCount > 0)
                {
                    theTokenizer.nextToken(theToken);

                    --theTokenCount;

                    m_cdataSectionElems.push_back(
                        constructionContext.createXalanQName(theToken, getNamespaces(), theLocator, true));
                }

                assert(theTokenizer.hasMoreTokens() == false);
            }
        }
        else
        {
            const XalanQNameByValue     theAttributeName(aname, getNamespaces(), constructionContext.getMemoryManager(), theLocator );

            if (theAttributeName.getNamespace() == constructionContext.getXalanXSLNameSpaceURL())
            {
                if (theAttributeName.getLocalPart() == Constants::ATTRNAME_ESCAPE_URLS)
                {
                    m_outputEscapeURLs = getYesOrNo(aname, atts.getValue(i), constructionContext);
                }
                else if (theAttributeName.getLocalPart() == Constants::ATTRNAME_INDENTAMOUNT)
                {
                    m_indentAmount = WideStringToInt(atts.getValue(i));

                    if (m_indentAmount < 0)
                    {
                        m_indentAmount = 0;
                    }
                }
                else if (theAttributeName.getLocalPart() == Constants::ATTRNAME_OMIT_META_TAG)
                {
                    m_omitMETATag = getYesOrNo(aname, atts.getValue(i), constructionContext);
                }
                else
                {
                    const StylesheetConstructionContext::GetCachedString    theGuard(constructionContext);

                    constructionContext.warn(
                        XalanMessageLoader::getMessage(
                            theGuard.get(),
                            XalanMessages::UnsupportedXalanSpecificAttribute_1Param,
                            theAttributeName.getLocalPart()),
                        0,
                        theLocator);
                }
            }
            else if (isAttrOK(aname, atts, i, constructionContext) == false)
            {
                const StylesheetConstructionContext::GetCachedString    theGuard(constructionContext);

                constructionContext.error(
                    XalanMessageLoader::getMessage(
                        theGuard.get(),
                        XalanMessages::HasIllegalAttribute_2Param,
                        name,
                        aname),
                    0,
                    theLocator);
            }
        }
    }

    // If HTML output 
    if(m_outputMethod == FormatterListener::OUTPUT_METHOD_HTML &&
       m_indentResult == eIndentNoImplicit)
    {
        m_indentResult = eIndentYesImplicit;
    }
}



void 
StylesheetRoot::initDefaultRule(StylesheetConstructionContext&  constructionContext)
{
    if (m_defaultRule == 0)
    {
        assert(m_defaultTextRule == 0);
        assert(m_defaultRootRule == 0);

        AttributeListImpl       attrs(constructionContext.getMemoryManager());

        attrs.addAttribute(c_wstr(Constants::ATTRNAME_MATCH),
                           c_wstr(Constants::ATTRTYPE_CDATA),
                           XPath::PSEUDONAME_ANY);

        m_defaultRule =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_TEMPLATE,
                *this,
                attrs);
        assert(m_defaultRule != 0);

        attrs.clear();

        ElemTemplateElement*    childrenElement =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_APPLY_TEMPLATES,
                *this,
                attrs);
        assert(childrenElement != 0);

        m_defaultRule->appendChildElem(childrenElement);

        m_defaultRule->setDefaultTemplate(true);

        // -----------------------------

        attrs.clear();
        attrs.addAttribute(c_wstr(Constants::ATTRNAME_MATCH),
                           c_wstr(Constants::ATTRTYPE_CDATA),
                           c_wstr(Constants::ATTRVAL_DEFAULT_TEXT_RULE));

        m_defaultTextRule =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_TEMPLATE,
                *this,
                attrs);
        assert(m_defaultTextRule != 0);

        attrs.clear();
        attrs.addAttribute(c_wstr(Constants::ATTRNAME_SELECT),
                           c_wstr(Constants::ATTRTYPE_CDATA),
                           c_wstr(Constants::ATTRVAL_THIS));

        childrenElement =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_VALUE_OF,
                *this,
                attrs);
        assert(childrenElement != 0);

        m_defaultTextRule->appendChildElem(childrenElement);

        m_defaultTextRule->setDefaultTemplate(true);

        //--------------------------------
    
        attrs.clear();
        attrs.addAttribute(c_wstr(Constants::ATTRNAME_MATCH),
                           c_wstr(Constants::ATTRTYPE_CDATA),
                           XPath::PSEUDONAME_ROOT);

        m_defaultRootRule =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_TEMPLATE,
                *this,
                attrs);
        assert(m_defaultRootRule != 0);

        attrs.clear();

        childrenElement =
            constructionContext.createElement(
                StylesheetConstructionContext::ELEMNAME_APPLY_TEMPLATES,
                *this,
                attrs);
        assert(childrenElement != 0);

        m_defaultRootRule->appendChildElem(childrenElement);

        m_defaultRootRule->setDefaultTemplate(true);
    }

    assert(m_defaultRule != 0);
    assert(m_defaultTextRule != 0);
    assert(m_defaultRootRule != 0);
}



bool
StylesheetRoot::isCDATASectionElementName(const XalanQName&     theQName) const
{
    XALAN_USING_STD(find_if)

    return find_if(
            m_cdataSectionElems.begin(),
            m_cdataSectionElems.end(),
            pointer_equals_predicate<XalanQName>(&theQName)) != m_cdataSectionElems.end() ? true : false;
}



inline XalanNode*
getKeyNode(
            XalanNode*                      startNode,
            StylesheetExecutionContext&     executionContext)
{
    // This is a big hack for dealing with result tree fragments coerced to node-sets using the
    // node-set extension function.  For such cases, the "document" is really the
    // XalanDocumentFragment instance that owns the nodes, not the owner document.
    if (startNode->getFirstChild() == 0)
    {
        XalanNode*  currentNode = executionContext.getCurrentNode();
        assert(currentNode != 0);

        if (currentNode->getOwnerDocument() == startNode)
        {
            // OK, the current node belongs to the document, but the document
            // is just a factory for fragments...
            for(;;)
            {
                XalanNode*  parentNode = DOMServices::getParentOfNode(*currentNode);

                if (parentNode == 0)
                {
                    break;
                }
                else if (parentNode->getNodeType() == XalanNode::DOCUMENT_FRAGMENT_NODE)
                {
                    startNode = parentNode;

                    break;
                }
                else
                {
                    currentNode = parentNode;
                }
            }
        }
    }

    return startNode;
}



void
StylesheetRoot::getNodeSetByKey(
            XalanDocument*                  doc,
            const XalanQName&               qname,
            const XalanDOMString&           ref,
            const PrefixResolver&           resolver,
            MutableNodeRefList&             nodelist,
            StylesheetExecutionContext&     executionContext,
            KeyTablesTableType&             theKeysTable) const
{
    assert(nodelist.empty() == true || nodelist.getDocumentOrder() == true);

    XalanNode* const    theKeyNode = getKeyNode(doc, executionContext);
    assert(theKeyNode != 0);

    if(m_needToBuildKeysTable == true)
    {
        assert(m_keyDeclarations.empty() == false);

        const KeyTablesTableType::const_iterator    i =
            theKeysTable.find(theKeyNode);

        if (i != theKeysTable.end())
        {
            const MutableNodeRefList&   nl = (*i).second->getNodeSetByKey(qname, ref);

            if (nodelist.empty() == true)
            {
                nodelist = nl;
            }
            else
            {
                nodelist.addNodesInDocOrder(nl, executionContext);
            }
        }
        else
        {
            XalanMemMgrAutoPtr<KeyTable>    kt(
                        executionContext.getMemoryManager(),
                        KeyTable::create(
                            executionContext.getMemoryManager(),
                             theKeyNode,
                             resolver,
                             m_keyDeclarations,
                             executionContext));

            theKeysTable[theKeyNode] = kt.get();

            const KeyTable* const   theNewTable = kt.releasePtr();

            const MutableNodeRefList&   nl = theNewTable->getNodeSetByKey(qname, ref);

            if (nodelist.empty() == true)
            {
                nodelist = nl;
            }
            else
            {
                nodelist.addNodesInDocOrder(nl, executionContext);
            }
        }
    }
}



bool
StylesheetRoot::shouldStripSourceNode(const XalanText&  textNode) const
{
    const XalanNode* const  parent = textNode.getParentNode();
    assert(parent != 0);

    if (textNode.isIgnorableWhitespace() == true &&
        parent->getNodeType() == XalanNode::ELEMENT_NODE)
    {
        const XalanElement* const   theElement =
#if defined(XALAN_OLD_STYLE_CASTS)
                (const XalanElement*)parent;
#else
                static_cast<const XalanElement*>(parent);
#endif

        typedef WhitespaceElementsVectorType::const_iterator    const_iterator;

        for (const_iterator i = m_whitespaceElements.begin();
                i != m_whitespaceElements.end();
                    ++i)
        {
            if ((*i)(*theElement) != XPath::eMatchScoreNone)
            {
                return (*i).getType() == XalanSpaceNodeTester::eStrip;
            }
        }
    }

    return false;
}



void
StylesheetRoot::addAttributeSet(ElemAttributeSet&   theAttributeSet)
{
    m_attributeSetsMap[&theAttributeSet.getQName()].push_back(&theAttributeSet);
}


#if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
const ElemAttributeSet*
StylesheetRoot::getAttributeSet(
            StylesheetExecutionContext&     executionContext,
            const XalanQName&               theQName,
            size_type                       matchingIndex,
            const LocatorType*              theLocator) const
{
    const AttributeSetMapType::const_iterator   i =
        m_attributeSetsMap.find(&theQName);

    if (i == m_attributeSetsMap.end())
    {
        const GetCachedString   theGuard(executionContext);

        executionContext.error(
            XalanMessageLoader::getMessage(
                theGuard.get(),
                XalanMessages::UnknownNodeType_1Param,
                Constants::ELEMNAME_ATTRIBUTESET_WITH_PREFIX_STRING),
            executionContext.getCurrentNode(),
            theLocator);
    }
    else
    {
        const AttributeSetVectorType&                   theAttributeSets = (*i).second;

        if (matchingIndex < theAttributeSets.size())
        {
            return theAttributeSets[matchingIndex];
        }
    }

    return 0;

}
#endif



#if defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
void
StylesheetRoot::executeAttributeSet(
            StylesheetExecutionContext&     theExecutionContext,
            const XalanQName&               theQName,
            const LocatorType*              theLocator) const
{
    const AttributeSetMapType::const_iterator   i =
        m_attributeSetsMap.find(&theQName);

    if (i == m_attributeSetsMap.end())
    {
        const GetCachedString   theGuard(theExecutionContext);

        theExecutionContext.error(
            XalanMessageLoader::getMessage(
                theGuard.get(),
                XalanMessages::UnknownNodeType_1Param,
                Constants::ELEMNAME_ATTRIBUTESET_WITH_PREFIX_STRING),
            theExecutionContext.getCurrentNode(),
            theLocator);
    }
    else
    {
        const AttributeSetVectorType&                   theAttributeSets = (*i).second;
        const AttributeSetVectorType::const_iterator    theEnd = theAttributeSets.end();

        for(AttributeSetVectorType::const_iterator i = theAttributeSets.begin(); i != theEnd; ++i)
        {
            assert(*i != 0);

            (*i)->execute(theExecutionContext);
        }
    }
}
#endif



XALAN_CPP_NAMESPACE_END
