/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2004 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * $ Id: $
 *
 */

// 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 <xalanc/XMLSupport/FormatterToDOM.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(),
	m_indentResult(false),
	m_encoding(),
	m_mediatype(),
	m_doctypeSystem(),
	m_doctypePublic(),
	m_omitxmlDecl(false),
	m_standalone(),
	m_resultNameSpaceURL(),
	m_outputMethod(FormatterListener::OUTPUT_METHOD_NONE),
	m_cdataSectionElems(),
	m_hasCDATASectionElems(false),
	m_importStack(),
	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_whitespacePreservingElements(),
	m_whitespaceStrippingElements(),
	m_attributeSetsMap()
{
	// 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()
{
}



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;
	}
}



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(executionContext.doDiagnosticsOutput())
	{
		executionContext.diag(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING(" =============================")));
		executionContext.diag(XalanMessageLoader::getMessage(XalanMessages::Transforming));
		executionContext.pushTime(&sourceTree);
	}

	typedef StylesheetExecutionContext::PushAndPopContextMarker	PushAndPopContextMarker;
	typedef StylesheetExecutionContext::PushAndPopElementFrame	PushAndPopElementFrame;
	typedef StylesheetExecutionContext::ResolveAndClearTopLevelParams	ResolveAndClearTopLevelParams;

	PushAndPopContextMarker		thePushAndPopContextMarker(executionContext);

	PushAndPopElementFrame		thePushAndPopElementFrame(
				executionContext,
				0);

	ResolveAndClearTopLevelParams	theResolveAndClearTopLevelParams(executionContext);

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

	executionContext.startDocument();

	executionContext.setCurrentMode(&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(executionContext.doDiagnosticsOutput())
	{
		executionContext.diag(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("")));
		executionContext.displayDuration(XalanMessageLoader::getMessage(XalanMessages::Transform), &sourceTree);
		executionContext.diag(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("")));
	}
}



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()))
			{
				pw = executionContext.createPrintWriter(
							outputTarget.getFileName(),
							XalanDOMString());
			}
			else
			{
				executionContext.error(
					XalanMessageLoader::getMessage(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 : m_indentResult;

		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
	{
		executionContext.error(
					XalanMessageLoader::getMessage(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();

	bool					didSpecifyIndent = false;

	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
			{
				constructionContext.warn(XalanMessageLoader::getMessage(XalanMessages::HasAnUnknownMethod_2Param,XalanDOMString(aname),XalanDOMString(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);

			didSpecifyIndent = true;
		}
		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);

				XalanDOMString	theToken;

				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(), 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
				{
					constructionContext.warn(
						XalanMessageLoader::getMessage(XalanMessages::UnsupportedXalanSpecificAttribute_1Param,theAttributeName.getLocalPart()),
						0,
						theLocator);
				}
			}
			else if (isAttrOK(aname, atts, i, constructionContext) == false)
			{
				constructionContext.error(
						XalanMessageLoader::getMessage(XalanMessages::HasIllegalAttribute_2Param, XalanDOMString(name) ,XalanDOMString(aname)),
						0,
						theLocator);
			}
		}
	}

	if(FormatterListener::OUTPUT_METHOD_HTML == m_outputMethod &&
	   false == didSpecifyIndent)
	{
		m_indentResult = true;
	}
}



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

		AttributeListImpl		attrs;

		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
		{
			XalanAutoPtr<KeyTable>	kt(
				new KeyTable(
							 theKeyNode,
							 resolver,
							 m_keyDeclarations,
							 executionContext));

			theKeysTable[theKeyNode] = kt.get();

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

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

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



bool
StylesheetRoot::shouldStripSourceNode(
			StylesheetExecutionContext&		executionContext,
			const XalanText&				textNode) const
{
	bool	strip = false;

	XalanNode*	parent = DOMServices::getParentOfNode(textNode);

	while(0 != parent)
	{
		if(parent->getNodeType() == XalanNode::ELEMENT_NODE)
		{
			XPath::eMatchScore	highPreserveScore = XPath::eMatchScoreNone;
			XPath::eMatchScore	highStripScore = XPath::eMatchScoreNone;

			{
				const XPathVectorType&	theElements =
						m_whitespacePreservingElements;

				const XPathVectorType::size_type	nTests =
						theElements.size();

				for(XPathVectorType::size_type i = 0; i < nTests; i++)
				{
					const XPath* const	matchPat = theElements[i];
					assert(matchPat != 0);

					const XPath::eMatchScore	score = matchPat->getMatchScore(parent, executionContext);

					if(score > highPreserveScore)
						highPreserveScore = score;
				}
			}

			{
				const XPathVectorType&	theElements =
						m_whitespaceStrippingElements;

				const XPathVectorType::size_type	nTests =
					theElements.size();

				for(XPathVectorType::size_type i = 0; i < nTests; i++)
				{
					const XPath* const	matchPat =
									theElements[i];
					assert(matchPat != 0);

					const XPath::eMatchScore	score = matchPat->getMatchScore(parent, executionContext);

					if(score > highStripScore)
						highStripScore = score;
				}
			}

			if(highPreserveScore > XPath::eMatchScoreNone ||
			   highStripScore > XPath::eMatchScoreNone)
			{
				if(highPreserveScore > highStripScore)
				{
					strip = false;
				}
				else if(highStripScore > highPreserveScore)
				{
					strip = true;
				}
				else
				{
					executionContext.warn(
						XalanMessageLoader::getMessage(XalanMessages::MatchConflictBetween_strip_space_preserve_space)); 
				}
				break;
			}
		}

		parent = parent->getParentNode();
	}

	return strip;
}



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



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())
	{
		theExecutionContext.error(
			XalanMessageLoader::getMessage(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);
		}
	}
}



XALAN_CPP_NAMESPACE_END
