<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 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 <EMAIL>.
 *
 * 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/>.
 */



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



#include <xercesc/sax/DocumentHandler.hpp>
#include <xercesc/sax/EntityResolver.hpp>
#include <xercesc/sax/Locator.hpp>



#include <xalanc/Include/STLHelper.hpp>



#include <xalanc/XalanDOM/XalanDOMException.hpp>
#include <xalanc/XalanDOM/XalanNode.hpp>
#include <xalanc/XalanDOM/XalanAttr.hpp>
#include <xalanc/XalanDOM/XalanComment.hpp>
#include <xalanc/XalanDOM/XalanCDATASection.hpp>
#include <xalanc/XalanDOM/XalanNodeList.hpp>
#include <xalanc/XalanDOM/XalanNamedNodeMap.hpp>
#include <xalanc/XalanDOM/XalanProcessingInstruction.hpp>
#include <xalanc/XalanDOM/XalanText.hpp>



#include <xalanc/PlatformSupport/PrintWriter.hpp>
#include <xalanc/PlatformSupport/StringTokenizer.hpp>
#include <xalanc/PlatformSupport/XalanLocator.hpp>
#include <xalanc/PlatformSupport/XalanUnicode.hpp>



#include <xalanc/DOMSupport/DOMServices.hpp>
#include <xalanc/DOMSupport/DOMSupport.hpp>



#include <xalanc/XMLSupport/FormatterToHTML.hpp>
#include <xalanc/XMLSupport/FormatterTreeWalker.hpp>
#include <xalanc/XMLSupport/XMLParserLiaison.hpp>
#include <xalanc/XMLSupport/FormatterTreeWalker.hpp>



#include <xalanc/XPath/ElementPrefixResolverProxy.hpp>
#include <xalanc/XPath/XalanQNameByReference.hpp>
#include <xalanc/XPath/ResultTreeFragBase.hpp>
#include <xalanc/XPath/XObject.hpp>
#include <xalanc/XPath/XObjectFactory.hpp>
#include <xalanc/XPath/XPathEnvSupport.hpp>
#include <xalanc/XPath/XPathEnvSupportDefault.hpp>
#include <xalanc/XPath/XPathExecutionContextDefault.hpp>
#include <xalanc/XPath/XPathFactory.hpp>
#include <xalanc/XPath/XPathFunctionTable.hpp>
#include <xalanc/XPath/XPathProcessorImpl.hpp>



#include "Constants.hpp"
#include "ElemWithParam.hpp"
#include "FunctionCurrent.hpp"
#include "FunctionDocument.hpp"
#include "FunctionElementAvailable.hpp"
#include "FunctionFunctionAvailable.hpp"
#include "FunctionFormatNumber.hpp"
#include "FunctionGenerateID.hpp"
#include "FunctionKey.hpp"
#include "FunctionSystemProperty.hpp"
#include "FunctionUnparsedEntityURI.hpp"
#include "GenerateEvent.hpp"
#include "ProblemListener.hpp"
#include "ProblemListenerDefault.hpp"
#include "Stylesheet.hpp"
#include "StylesheetConstructionContext.hpp"
#include "StylesheetExecutionContext.hpp"
#include "StylesheetHandler.hpp"
#include "StylesheetRoot.hpp"
#include "TraceListener.hpp"
#include "XSLTInputSource.hpp"
#include "XSLTProcessorException.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



const XalanDOMString	XSLTEngineImpl::s_emptyString;



//==========================================================
// SECTION: Constructors
//==========================================================

XSLTEngineImpl::XSLTEngineImpl(
			XMLParserLiaison&	parserLiaison,
			XPathEnvSupport&	xpathEnvSupport,
			DOMSupport&			domSupport,
			XObjectFactory&		xobjectFactory,
			XPathFactory&		xpathFactory) :
	XSLTProcessor(),
	PrefixResolver(),
	m_resultNameSpacePrefix(),
	m_resultNameSpaceURL(),
	m_xpathFactory(xpathFactory),
	m_xobjectFactory(xobjectFactory),
	m_xpathProcessor(new XPathProcessorImpl),
	m_cdataStack(),
	m_stylesheetLocatorStack(),
	m_defaultProblemListener(),
	m_problemListener(&m_defaultProblemListener),
	m_stylesheetRoot(0),
	m_traceSelects(false),
	m_quietConflictWarnings(true),
	m_diagnosticsPrintWriter(0),
	m_durationsTable(),
	m_traceListeners(),
	m_uniqueNSValue(0),
	m_topLevelParams(),
	m_parserLiaison(parserLiaison),
	m_xpathEnvSupport(xpathEnvSupport),
	m_domSupport(domSupport),
	m_executionContext(0),
	m_outputContextStack(),
	m_resultNamespacesStack(),
	m_dummyAttributesList(),
	m_scratchString(),
	m_attributeNamesVisited(),
	m_hasStripOrPreserveSpace(false),
	m_hasCDATASectionElements(false),
	m_xpathConstructionContext()
{
	m_outputContextStack.pushContext();
}



void
XSLTEngineImpl::reset()
{
	m_topLevelParams.clear();
	m_durationsTable.clear();
	m_stylesheetLocatorStack.clear();
	m_cdataStack.clear();

	m_stylesheetRoot = 0;

	m_outputContextStack.reset();

	m_outputContextStack.pushContext();

	m_xpathEnvSupport.reset();
	m_xpathFactory.reset();
	m_xobjectFactory.reset();
	m_domSupport.reset();

	m_resultNamespacesStack.clear();

	m_attributeNamesVisited.clear();

	m_hasStripOrPreserveSpace = false;
	m_hasCDATASectionElements = false;

	m_xpathConstructionContext.reset();
}



XSLTEngineImpl::~XSLTEngineImpl()
{
	reset();
}



//==========================================================
// SECTION: Main API Functions
//==========================================================



static const XalanDOMChar	s_dummyString = 0;


void
XSLTEngineImpl::process(
			const XSLTInputSource&			inputSource, 
	        const XSLTInputSource&			stylesheetSource,
	        XSLTResultTarget&				outputTarget,
			StylesheetConstructionContext&	constructionContext,
			StylesheetExecutionContext&		executionContext)
{
	XalanDOMString	xslIdentifier;

	if (0 == stylesheetSource.getSystemId())
	{
		xslIdentifier = XALAN_STATIC_UCODE_STRING("Input XSL");
	}
	else
	{
		xslIdentifier = stylesheetSource.getSystemId();
	}

	bool totalTimeID = true;

	if(m_diagnosticsPrintWriter != 0)
	{
		pushTime(&totalTimeID);
	}

	XalanNode*	sourceTree = getSourceTreeFromInput(inputSource);

	m_stylesheetRoot = processStylesheet(stylesheetSource, constructionContext);

	if(0 != sourceTree && m_stylesheetRoot == 0)
	{
		// Didn't get a stylesheet from the input source, so look for a
		// stylesheet processing instruction...

		// The PI must be a child of the document...
		const XalanNode*	child = sourceTree->getFirstChild();

		XalanDOMString		theCurrentToken;
		XalanDOMString		theStylesheetURI;

		bool			isOK = false;

		while(child != 0 && isOK == false && theStylesheetURI.empty() == true)
		{
			if(XalanNode::PROCESSING_INSTRUCTION_NODE == child->getNodeType())
			{
				const XalanDOMString&	nodeName = child->getNodeName();

				if(equals(nodeName, s_stylesheetNodeName))
				{
					StringTokenizer 	tokenizer(child->getNodeValue(), s_piTokenizerString);

					while(tokenizer.hasMoreTokens() == true && (isOK == false || theStylesheetURI.empty() == true))
					{
						tokenizer.nextToken(theCurrentToken);

						if(equals(theCurrentToken, s_typeString))
						{
							tokenizer.nextToken(theCurrentToken);


							const XalanDOMString::size_type		theLength =
									theCurrentToken.length();

							if (theLength > 2)
							{
								theCurrentToken.erase(theLength - 1, 1);
								theCurrentToken.erase(0, 1);

								if(equals(theCurrentToken, s_typeValueString1) ||
									equals(theCurrentToken, s_typeValueString2) ||
									equals(theCurrentToken, s_typeValueString3) ||
									equals(theCurrentToken, s_typeValueString4))
								{
									isOK = true;
								}
							}
						}
						else if(equals(theCurrentToken, s_hrefString))
						{
							tokenizer.nextToken(theCurrentToken);

							const XalanDOMString::size_type		theLength =
									theCurrentToken.length();

							if (theLength > 2)
							{
								// Trim of the starting and trailing delimiters...
								theStylesheetURI.assign(theCurrentToken, 1, theLength - 2);
							}
						}
					}
				}
			}

			child = child->getNextSibling();
		}

		if (isOK == true && theStylesheetURI.empty() == false)
		{
			const XalanDOMChar* const	pxch = inputSource.getSystemId();

			const XalanDOMString		sysid(pxch == 0 ? &s_dummyString : pxch); 

			getStylesheetFromPIURL(
					theStylesheetURI,
					*sourceTree,
					sysid,
					true,
					constructionContext);
		}
	}

	if(0 == m_stylesheetRoot)
	{
		error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Failed to process stylesheet!")));
	}
	else if(0 != sourceTree)
	{
		executionContext.setStylesheetRoot(m_stylesheetRoot);

		FormatterListener* const	theFormatter =
				outputTarget.getFormatterListener();

		if (theFormatter != 0 && theFormatter->getPrefixResolver() == 0)
		{
			theFormatter->setPrefixResolver(this);
		}

		m_hasStripOrPreserveSpace = m_stylesheetRoot->hasPreserveOrStripSpaceElements();

		m_hasCDATASectionElements = m_stylesheetRoot->hasCDATASectionElements();

		m_stylesheetRoot->process(sourceTree, outputTarget, executionContext);
	}

	if(m_diagnosticsPrintWriter != 0)
	{
		displayDuration(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Total time")), &totalTimeID);
	}
}



void
XSLTEngineImpl::process(
			const XSLTInputSource&			inputSource, 
	        XSLTResultTarget&				outputTarget,
			StylesheetExecutionContext&		executionContext)
{
	bool	totalTimeID = true;

	if(m_diagnosticsPrintWriter != 0)
	{
		pushTime(&totalTimeID);
	}

	XalanNode* const	sourceTree = getSourceTreeFromInput(inputSource);

	if(0 != sourceTree)
	{
		if (m_stylesheetRoot == 0)
		{
			error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("No stylesheet is available to process!")));
		}

		FormatterListener* const	theFormatter =
				outputTarget.getFormatterListener();

		if (theFormatter != 0 && theFormatter->getPrefixResolver() == 0)
		{
			theFormatter->setPrefixResolver(this);
		}

		m_hasStripOrPreserveSpace = m_stylesheetRoot->hasPreserveOrStripSpaceElements();

		m_hasCDATASectionElements = m_stylesheetRoot->hasCDATASectionElements();

		m_stylesheetRoot->process(sourceTree, outputTarget, executionContext);
	}

	if(m_diagnosticsPrintWriter != 0)
	{
		displayDuration(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Total time")), &totalTimeID);
	}
}



StylesheetRoot*
XSLTEngineImpl::processStylesheet(
			const XalanDOMString&			xsldocURLString,
			StylesheetConstructionContext&	constructionContext)
{
	const XSLTInputSource	input(c_wstr(xsldocURLString));

	return processStylesheet(input, constructionContext);
}



StylesheetRoot*
XSLTEngineImpl::processStylesheet(
  			const XSLTInputSource&			stylesheetSource,
			StylesheetConstructionContext&	constructionContext)
{
	StylesheetRoot*		theStylesheet = 0;

	const XalanDOMChar* const	systemID = stylesheetSource.getSystemId();
	XalanNode* const			stylesheetNode = stylesheetSource.getNode();

	if (systemID != 0 || stylesheetNode != 0 || stylesheetSource.getStream() != 0)
	{
		XalanDOMString	xslIdentifier;

		theStylesheet = constructionContext.create(stylesheetSource);

		StylesheetHandler	stylesheetProcessor(*theStylesheet, constructionContext);

		if(stylesheetNode != 0)
		{
			const XalanNode::NodeType	theType = stylesheetNode->getNodeType();

			if (theType != XalanNode::ELEMENT_NODE && theType != XalanNode::DOCUMENT_NODE)
			{
				error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Compiling a stylesheet from a DOM instance requires a Document or Element node")));
			}
			else
			{
				xslIdentifier = XALAN_STATIC_UCODE_STRING("Input XSL");

				FormatterTreeWalker tw(stylesheetProcessor);

				if (theType == XalanNode::DOCUMENT_NODE)
				{
					tw.traverse(stylesheetNode);
				}
				else
				{
					stylesheetProcessor.startDocument();

					tw.traverseSubtree(stylesheetNode);

					stylesheetProcessor.endDocument();
				}
			}
		}
		else
		{
			if (systemID != 0)
			{
				xslIdentifier = systemID;
			}

			if(m_diagnosticsPrintWriter != 0)
			{
				diag(XALAN_STATIC_UCODE_STRING("========= Parsing ") + xslIdentifier + XALAN_STATIC_UCODE_STRING(" =========="));

				pushTime(&xslIdentifier);
			}

			m_parserLiaison.parseXMLStream(stylesheetSource,
										   stylesheetProcessor);

			if(m_diagnosticsPrintWriter != 0)
			{
				displayDuration(XALAN_STATIC_UCODE_STRING("Parse of ") + xslIdentifier, &xslIdentifier);
			}
		}

		theStylesheet->postConstruction(constructionContext);
	}

	return theStylesheet;
}



//==========================================================
// SECTION: XML Parsing Functions
//==========================================================

XalanNode*
XSLTEngineImpl::getSourceTreeFromInput(const XSLTInputSource&	inputSource)
{
	XalanNode*		sourceTree = inputSource.getNode();

	if(0 == sourceTree)
	{
		const XalanDOMString	xmlIdentifier = 0 != inputSource.getSystemId() ?
												XalanDOMString(inputSource.getSystemId()) :
												StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Input XML"));

		if(m_diagnosticsPrintWriter != 0)
		{
			// In case we have a fragment identifier, go ahead and 
			// try to parse the XML here.
			diag(XALAN_STATIC_UCODE_STRING("========= Parsing ") +
						xmlIdentifier +
						XALAN_STATIC_UCODE_STRING(" =========="));

			pushTime(&xmlIdentifier);
		}

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

		XalanDocument* const	theDocument =
						m_parserLiaison.parseXMLStream(inputSource,
													   xmlIdentifier);
		assert(theDocument != 0);

#if defined(XALAN_VQ_SPECIAL_TRACE)
		QuantifyStopRecordingData();
#endif
		if(0 != m_diagnosticsPrintWriter)
		{
			displayDuration(
				XALAN_STATIC_UCODE_STRING("Parse of ") + xmlIdentifier,
				&xmlIdentifier);
		}

		m_xpathEnvSupport.setSourceDocument(xmlIdentifier, theDocument);

		sourceTree = theDocument;
	}

	return sourceTree;
}



const XalanDOMString*
XSLTEngineImpl::getNamespaceForPrefix(const XalanDOMString&		prefix) const
{
	return m_resultNamespacesStack.getNamespaceForPrefix(prefix);
}



const XalanDOMString&
XSLTEngineImpl::getURI() const
{
	return s_emptyString;
}



XalanDocument*
XSLTEngineImpl::parseXML(
			const XalanDOMString&	urlString,
			DocumentHandlerType*	docHandler,
			XalanDocument*			docToRegister)
{
	
	XalanDocument*	doc =
			m_xpathEnvSupport.getSourceDocument(urlString);

	if(doc == 0)
	{
		EntityResolverType* const	theResolver = 
			m_parserLiaison.getEntityResolver();

		if (theResolver == 0)
		{
			const XSLTInputSource	inputSource(c_wstr(urlString));

			doc = parseXML(inputSource, docHandler, docToRegister);
		}
		else
		{
			const XalanAutoPtr<InputSourceType>		resolverInputSource =
				theResolver->resolveEntity(0, c_wstr(urlString));

			if (resolverInputSource.get() != 0)
			{
				doc = parseXML(*resolverInputSource.get(), docHandler, docToRegister);
			}
			else
			{
				const XSLTInputSource	inputSource(c_wstr(urlString));

				doc = parseXML(inputSource, docHandler, docToRegister);
			}
		}

		if (doc != 0)
		{
			m_xpathEnvSupport.setSourceDocument(urlString, doc);
		}
	}

	return doc;
}



XalanDocument*
XSLTEngineImpl::parseXML(
			const InputSourceType&	inputSource,
			DocumentHandlerType*	docHandler,
			XalanDocument*			docToRegister)
{
	if(0 != docHandler)
	{
		m_parserLiaison.parseXMLStream(inputSource, *docHandler);

		return docToRegister;
	}
	else
	{
		return m_parserLiaison.parseXMLStream(inputSource);
	}
}



Stylesheet*
XSLTEngineImpl::getStylesheetFromPIURL(
			const XalanDOMString&			xslURLString,
			XalanNode&						fragBase,
			const XalanDOMString&			xmlBaseIdent,
			bool							isRoot,
			StylesheetConstructionContext&	constructionContext)
{
	Stylesheet*				stylesheet = 0;

	XalanDOMString			stringHolder;

	XalanDOMString			localXSLURLString = trim(xslURLString);

	const XalanDOMString::size_type		fragIndex = indexOf(localXSLURLString, XalanUnicode::charNumberSign);

	const XalanDocument*	stylesheetDoc = 0;

	if(fragIndex == 0)
	{
		const XalanDOMString	fragID(localXSLURLString, 1);

		const XalanElement*		nsNode = 0;

		const XalanNode::NodeType	theType = fragBase.getNodeType();

		if (theType == XalanNode::DOCUMENT_NODE)
		{
			const XalanDocument&	doc =
#if defined(XALAN_OLD_STYLE_CASTS)
				(const XalanDocument&)fragBase;
#else
				static_cast<const XalanDocument&>(fragBase);
#endif

			nsNode = doc.getDocumentElement(); 
		}
		else if	(theType == XalanNode::ELEMENT_NODE)
		{
#if defined(XALAN_OLD_STYLE_CASTS)
			nsNode = (const XalanElement*)&fragBase;
#else
			nsNode = static_cast<const XalanElement*>(&fragBase);
#endif
		}
		else
		{
			XalanNode* const	node = fragBase.getParentNode();

			if	(node->getNodeType() == XalanNode::ELEMENT_NODE) 
			{
#if defined(XALAN_OLD_STYLE_CASTS)
				nsNode = (const XalanElement*)&fragBase;
#else
				nsNode = static_cast<XalanElement*>(node);
#endif
			}
			else
			{
				error("Could not identify fragment: " + fragID);
			}
		}

		// Try a bunch of really ugly stuff to find the fragment.
		// What's the right way to do this?
		XalanDOMString	ds(XALAN_STATIC_UCODE_STRING("id("));

		ds += fragID;
		ds += XALAN_STATIC_UCODE_STRING(")");

		ElementPrefixResolverProxy		theProxy(nsNode, m_xpathEnvSupport, m_domSupport);

		XPathExecutionContextDefault	theExecutionContext(m_xpathEnvSupport,
															m_domSupport,
															m_xobjectFactory,
															&fragBase,
															0,
															&theProxy);

		const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
		assert(xobj.null() == false);

		NodeRefList		nl(xobj->nodeset());

		if(nl.getLength() == 0)
		{
			NodeRefList		theEmptyList;

			ds = XALAN_STATIC_UCODE_STRING("//*[@id='");
			ds += fragID;
			ds += XALAN_STATIC_UCODE_STRING("']");

			theExecutionContext.setContextNodeList(theEmptyList);

			const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
			assert(xobj.null() == false);

			nl = xobj->nodeset();

			if(nl.getLength() == 0)
			{
				ds = XALAN_STATIC_UCODE_STRING("//*[@name='");
				ds += fragID;
				ds += XALAN_STATIC_UCODE_STRING("']");

				theExecutionContext.setContextNodeList(theEmptyList);

				const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
				assert(xobj.null() == false);

				nl = xobj->nodeset();

				if(nl.getLength() == 0)
				{
					// Well, hell, maybe it's an XPath...
					theExecutionContext.setContextNodeList(theEmptyList);

					const XObjectPtr	xobj(evalXPathStr(fragID, theExecutionContext));
					assert(xobj.null() == false);

					nl = xobj->nodeset();
				}
			}
		}

		if(nl.getLength() == 0)
		{
			error("Could not find fragment: " + fragID);
		}

		XalanNode* const	frag = nl.item(0);

		if(XalanNode::ELEMENT_NODE == frag->getNodeType())
		{
			if(m_diagnosticsPrintWriter != 0)
			{
				pushTime(frag);
			}

			XalanAutoPtr<Stylesheet>	theGuard;

			if(isRoot)
			{
				StylesheetRoot* const	theLocalRoot =
					constructionContext.create(stringHolder);

				stylesheet = theLocalRoot;

				m_stylesheetRoot = theLocalRoot;
			}
			else
			{
#if defined(XALAN_OLD_STYLE_CASTS)
				stylesheet = constructionContext.create(*((StylesheetRoot*)m_stylesheetRoot), stringHolder);
#else
				stylesheet = constructionContext.create(*const_cast<StylesheetRoot*>(m_stylesheetRoot), stringHolder);
#endif

				theGuard.reset(stylesheet);
			}

			StylesheetHandler stylesheetProcessor(*stylesheet, constructionContext);

			FormatterTreeWalker tw(stylesheetProcessor);

			stylesheetProcessor.startDocument();

			tw.traverseSubtree(frag);

			stylesheetProcessor.endDocument();

			if(m_diagnosticsPrintWriter != 0)
			{
				displayDuration(
						XalanDOMString(XALAN_STATIC_UCODE_STRING("Setup of ")) + localXSLURLString,
						frag);
			}

			stylesheet->postConstruction(constructionContext);

			theGuard.release();
		}
		else
		{
			stylesheetDoc = 0;

			error("Node pointed to by fragment identifier was not an element: " + fragID);
		}
	}
	else
	{
		if(m_diagnosticsPrintWriter != 0)
		{
			diag(XalanDOMString(XALAN_STATIC_UCODE_STRING("========= Parsing and preparing ")) +
					localXSLURLString +
					XALAN_STATIC_UCODE_STRING(" =========="));
			pushTime(&localXSLURLString);
		}

		XalanAutoPtr<Stylesheet>	theGuard;

		const XalanDocument* const	theOwnerDocument =
				fragBase.getNodeType() == XalanNode::DOCUMENT_NODE ?
#if defined(XALAN_OLD_STYLE_CASTS)
				(const XalanDocument*)&fragBase :
#else
				static_cast<const XalanDocument*>(&fragBase) :
#endif
				fragBase.getOwnerDocument();
		assert(theOwnerDocument != 0);

		// Catch any XMLExceptions thrown, since we may not
		// be able to resolve the URL.  In that case, the
		// parser will throw an error.  We do this because
		// we don't know what sort of EntityResolvers might
		// be active, so we never want to error out here.
		try
		{
			if (length(xmlBaseIdent) == 0)
			{
				URISupport::getURLStringFromString(
							localXSLURLString,
							m_xpathEnvSupport.findURIFromDoc(theOwnerDocument),
							localXSLURLString);
			}
			else
			{
				URISupport::getURLStringFromString(
							localXSLURLString,
							xmlBaseIdent,
							localXSLURLString);
			}
		}
		catch(const XERCES_CPP_NAMESPACE_QUALIFIER XMLException&)
		{
		}

		if(isRoot)
		{
			StylesheetRoot* const	theLocalRoot =
					constructionContext.create(localXSLURLString);

			stylesheet = theLocalRoot;

			m_stylesheetRoot = theLocalRoot;
		}
		else
		{
#if defined(XALAN_OLD_STYLE_CASTS)
			stylesheet = new Stylesheet(*(StylesheetRoot*)m_stylesheetRoot, localXSLURLString, constructionContext);
#else
			stylesheet = new Stylesheet(*const_cast<StylesheetRoot*>(m_stylesheetRoot), localXSLURLString, constructionContext);
#endif

			theGuard.reset(stylesheet);
		}

		StylesheetHandler stylesheetProcessor(*stylesheet, constructionContext);

		typedef StylesheetConstructionContext::URLAutoPtrType	URLAutoPtrType;

		URLAutoPtrType	xslURL(constructionContext.getURLFromString(localXSLURLString));

		XSLTInputSource		inputSource(xslURL->getURLText());

		m_parserLiaison.parseXMLStream(inputSource, stylesheetProcessor);

		stylesheet->postConstruction(constructionContext);

		theGuard.release();

		if(m_diagnosticsPrintWriter != 0)
		{
			displayDuration("Parsing and init of " + localXSLURLString, &localXSLURLString);
		}
	}

	return stylesheet;
}



const StylesheetRoot*
XSLTEngineImpl::getStylesheetRoot() const
{
	return m_stylesheetRoot;
}



void
XSLTEngineImpl::setStylesheetRoot(const StylesheetRoot*		theStylesheet)
{
	m_stylesheetRoot = theStylesheet;
}



void
XSLTEngineImpl::setExecutionContext(StylesheetExecutionContext*		theExecutionContext)
{
	m_executionContext = theExecutionContext;
}



//==========================================================
// SECTION: Diagnostic functions
//==========================================================

XSLTEngineImpl::size_type
XSLTEngineImpl::getTraceListeners() const
{
	return m_traceListeners.size();
}



void
XSLTEngineImpl::addTraceListener(TraceListener* tl)
{
	if (tl != 0)
	{
		m_traceListeners.push_back(tl);
	}
}



void
XSLTEngineImpl::removeTraceListener(TraceListener*	tl)
{
	XALAN_USING_STD(remove)

	const TraceListenerVectorType::iterator		i =
		remove(
			m_traceListeners.begin(),
			m_traceListeners.end(),
			tl);

	m_traceListeners.erase(i);
}



void
XSLTEngineImpl::fireGenerateEvent(const GenerateEvent&	ge)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerGenerateFunctor(ge));
}



void
XSLTEngineImpl::fireSelectEvent(const SelectionEvent&	se)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerSelectFunctor(se));
}



void
XSLTEngineImpl::fireTraceEvent(const TracerEvent& te)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerTraceFunctor(te));
}



bool
XSLTEngineImpl::getTraceSelects() const
{
	return m_traceSelects;
}



void
XSLTEngineImpl::setTraceSelects(bool	b)
{
	m_traceSelects = b;
}



void
XSLTEngineImpl::message(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	problem(msg, ProblemListener::eMESSAGE, sourceNode, styleNode);
}



void
XSLTEngineImpl::message(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eMESSAGE, locator, sourceNode);
}



void
XSLTEngineImpl::problem(
			const XalanDOMString&				msg, 
			ProblemListener::eClassification	classification,
			const XalanNode*					sourceNode,
			const ElemTemplateElement*			styleNode) const
{
	const XalanDOMChar*		id = 0;

	XalanDOMString			uri;

	int						lineNumber = XalanLocator::getUnknownValue();
	int 					columnNumber = XalanLocator::getUnknownValue();

	const LocatorType*		locator = getLocatorFromStack();

	if (locator == 0 && styleNode != 0)
	{
		locator = styleNode->getLocator();
	}

	if (locator != 0)
	{
		id = locator->getPublicId();

		if (id == 0)
		{
			id = locator->getSystemId();
		}

		if (id != 0)
		{
			uri = id;
		}

		lineNumber = locator->getLineNumber();
		columnNumber = locator->getColumnNumber();
	}
	else if (styleNode != 0)
	{
		lineNumber = styleNode->getLineNumber();
		columnNumber = styleNode->getColumnNumber();

		uri = styleNode->getURI();
	}

	if (m_problemListener != 0)
	{
		m_problemListener->problem(
					ProblemListener::eXSLPROCESSOR,
					classification,
					sourceNode,
					styleNode,
					msg,
					id,
					lineNumber,
					columnNumber);
	}

	if (classification == ProblemListener::eERROR)
	{
		throw XSLTProcessorException(msg, uri, lineNumber, columnNumber);
	}
}



static const XalanDOMChar	theDummy = 0;

void
XSLTEngineImpl::problem(
			const XalanDOMString&				msg, 
			ProblemListener::eClassification	classification,
			const LocatorType&					locator,
			const XalanNode*					sourceNode) const
{
	const XalanDOMChar*		id = locator.getSystemId();

	if (id == 0)
	{
		id = &theDummy;
	}

	const XalanLocator::size_type	lineNumber = locator.getLineNumber();
	const XalanLocator::size_type 	columnNumber = locator.getColumnNumber();

	if (m_problemListener != 0)
	{
		m_problemListener->problem(
					ProblemListener::eXSLPROCESSOR,
					classification,
					sourceNode,
					0,
					msg,
					id,
					lineNumber,
					columnNumber);
	}

	if (classification == ProblemListener::eERROR)
	{
		throw XSLTProcessorException(msg, XalanDOMString(id), lineNumber, columnNumber);
	}
}



void
XSLTEngineImpl::warn(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	const LocatorType* const	locator = styleNode == 0 ? 0 : styleNode->getLocator();

	if (locator != 0)
	{
		problem(msg, ProblemListener::eWARNING, *locator, sourceNode);
	}
	else
	{
		problem(msg, ProblemListener::eWARNING, sourceNode, styleNode);
	}
}



void
XSLTEngineImpl::warn(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eWARNING, locator, sourceNode);
}



void
XSLTEngineImpl::warn(
			const char*					msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	warn(TranscodeFromLocalCodePage(msg), sourceNode, styleNode);
}



void
XSLTEngineImpl::error(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	problem(msg, ProblemListener::eERROR, sourceNode, styleNode);
}



void
XSLTEngineImpl::error(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eERROR, locator, sourceNode);
}



void
XSLTEngineImpl::pushTime(const void*	key)
{
	if(0 != key)
	{
#if defined(XALAN_STRICT_ANSI_HEADERS)
		m_durationsTable[key] = std::clock();
#else
		m_durationsTable[key] = clock();
#endif
	}
}



XSLTEngineImpl::ClockType
XSLTEngineImpl::popDuration(const void*		key)
{
	ClockType 	clockTicksDuration = 0;

	if(0 != key)
	{
		const DurationsTableMapType::iterator	i =
				m_durationsTable.find(key);

		if (i != m_durationsTable.end())
		{
#if defined(XALAN_STRICT_ANSI_HEADERS)
			clockTicksDuration = std::clock() - (*i).second;
#else
			clockTicksDuration = clock() - (*i).second;
#endif

			m_durationsTable.erase(i);
		}
	}

	return clockTicksDuration;
}



void
XSLTEngineImpl::displayDuration(
			const XalanDOMString&	info,
			const void*				key)
{
	if(0 != key)
	{
		const ClockType	theDuration = popDuration(key);

		if(m_diagnosticsPrintWriter != 0)
		{
			const double	millis = (double(theDuration) / CLOCKS_PER_SEC) * 1000.0;

			m_diagnosticsPrintWriter->print(info);

			m_diagnosticsPrintWriter->print(" took ");
			m_diagnosticsPrintWriter->print(millis);
			m_diagnosticsPrintWriter->println(" milliseconds.");
		}
	}
}



void
XSLTEngineImpl::setDiagnosticsOutput(PrintWriter*	pw)
{
	m_diagnosticsPrintWriter = pw;

	m_problemListener->setPrintWriter(pw);
}



void
XSLTEngineImpl::diag(const XalanDOMString& 	s) const
{
	if (0 != m_diagnosticsPrintWriter)
	{
		m_diagnosticsPrintWriter->println(s);
	}
}



void
XSLTEngineImpl::diag(const char*	s) const
{
	diag(TranscodeFromLocalCodePage(s));
}



void
XSLTEngineImpl::setQuietConflictWarnings(bool	b)
{
	m_quietConflictWarnings = b;
}



void
XSLTEngineImpl::setDocumentLocator(const LocatorType*	/* locator */)
{
	// Do nothing for now
}



void
XSLTEngineImpl::traceSelect(
			StylesheetExecutionContext& 	executionContext,
			const ElemTemplateElement&		theTemplate,
			const NodeRefListBase&			nl,
			const XPath*					xpath) const
{
	if (0 != m_diagnosticsPrintWriter)
	{
		XalanDOMString	msg = theTemplate.getElementName() + XalanDOMString(XALAN_STATIC_UCODE_STRING(": "));

		if(xpath != 0)
		{
			msg += xpath->getExpression().getCurrentPattern();
			msg += XALAN_STATIC_UCODE_STRING(", ");
		}
		else
		{
			msg += XALAN_STATIC_UCODE_STRING("*|text(), (default select), ");
		}

		msg += UnsignedLongToDOMString(nl.getLength());
		msg += XALAN_STATIC_UCODE_STRING(" selected");

		const XalanQName* const		mode = executionContext.getCurrentMode();

		if(mode != 0 && mode->isEmpty() == false)
		{
			msg += XALAN_STATIC_UCODE_STRING(", mode = {");
			msg += mode->getNamespace();
			msg += XALAN_STATIC_UCODE_STRING("}");
			msg += mode->getLocalPart();
		}

		m_diagnosticsPrintWriter->println(msg);
	}
}



void
XSLTEngineImpl::startDocument()
{
	assert(getFormatterListener() != 0);
	assert(m_executionContext != 0);

	if (getHasPendingStartDocument() == false)
	{
		assert(m_cdataStack.empty() == true || m_outputContextStack.size() != 1);

		m_resultNamespacesStack.pushContext();

		setHasPendingStartDocument(true);

		setMustFlushPendingStartDocument(false);

		if (m_hasCDATASectionElements == true)
		{
			m_cdataStack.push_back(false);
		}
	}
	else if (getMustFlushPendingStartDocument() == true)
	{
		getFormatterListener()->startDocument();

		if(getTraceListeners() > 0)
		{
			const GenerateEvent		ge(GenerateEvent::EVENTTYPE_STARTDOCUMENT);

			fireGenerateEvent(ge);
		}

		// Reset this, but leave getMustFlushPendingStartDocument() alone,
		// since it will still be needed.
		setHasPendingStartDocument(false);
	}
}



void
XSLTEngineImpl::endDocument()
{
	assert(getFormatterListener() != 0);
	assert(m_executionContext != 0);

	setMustFlushPendingStartDocument(true);

	flushPending();

	getFormatterListener()->endDocument();

	if(getTraceListeners() > 0)
	{
		const GenerateEvent		ge(GenerateEvent::EVENTTYPE_ENDDOCUMENT);

		fireGenerateEvent(ge);
	}

	if (m_hasCDATASectionElements == true)
	{
		m_cdataStack.pop_back();
	}
	assert(m_cdataStack.empty() == true);

	m_resultNamespacesStack.popContext();

	assert(m_resultNamespacesStack.empty() == true);
}



void
XSLTEngineImpl::addResultAttribute(
			AttributeListImpl&			attList,
			const XalanDOMString&		aname,
			const XalanDOMChar*			value)
{
	assert(value != 0);

	// Always exclude the implicit XML declaration...
	if (equals(aname, DOMServices::s_XMLNamespacePrefix) == false) 
	{
		bool	fExcludeAttribute = false;

		if (equals(aname, DOMServices::s_XMLNamespace) == true)
		{
			// OK, we're adding a default namespace declaration.  So see if the length
			// of the namespace is 0.  If it's not, go ahead and add the declaration.
			// If it's not, it means we're "turning off" the previous default
			// declaration.

			const XalanDOMString* const		currentDefaultNamespace =
						getNamespaceForPrefix(s_emptyString);

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

			// Note that we use an empty string for the prefix, instead of "xmlns", since the
			// prefix really is "".
			if (theLength != 0)
			{
				if (currentDefaultNamespace != 0 &&
					equals(*currentDefaultNamespace, value, theLength) == true)
				{
					fExcludeAttribute = true;
				}
				else
				{
					addResultNamespaceDecl(s_emptyString, value, theLength);
				}
			}
			else
			{
				// OK, we're turning of the previous default namespace declaration.
				// Check to see if there is one, and if there isn't, don't add
				// the namespace declaration _and_ don't add the attribute.
				if (currentDefaultNamespace != 0 && length(*currentDefaultNamespace) != 0)
				{
					addResultNamespaceDecl(s_emptyString, value, theLength);
				}
				else
				{
					fExcludeAttribute = true;
				}
			}
		}
		else if (startsWith(aname, DOMServices::s_XMLNamespaceWithSeparator) == true)
		{
			assert(m_executionContext != 0);

			StylesheetExecutionContext::GetAndReleaseCachedString	prefixGuard(*m_executionContext);

			XalanDOMString&		prefix = prefixGuard.get();

			substring(aname, prefix, DOMServices::s_XMLNamespaceWithSeparatorLength);

			const XalanDOMString* const	theNamespace = getResultNamespaceForPrefix(prefix);

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

			if (theNamespace == 0 || equals(*theNamespace, value, theLength) == false)
			{
				addResultNamespaceDecl(prefix, value, theLength);
			}
			else
			{
				fExcludeAttribute = true;
			}
		}

		if (fExcludeAttribute == false)
		{
			attList.addAttribute(
				c_wstr(aname),
				c_wstr(Constants::ATTRTYPE_CDATA),
				c_wstr(value));
		}
	}
}



bool
XSLTEngineImpl::pendingAttributesHasDefaultNS() const
{
	const AttributeListImpl&	thePendingAttributes =
		getPendingAttributes();

	const unsigned int	n = thePendingAttributes.getLength();

	for(unsigned int i = 0; i < n; i++)
	{
		if(equals(thePendingAttributes.getName(i),
				  DOMServices::s_XMLNamespace) == true)
		{
			return true;
		}
	}

	return false;
}



void
XSLTEngineImpl::flushPending()
{
	if(getHasPendingStartDocument() == true && 0 != length(getPendingElementName()))
	{
		assert(getFormatterListener() != 0);
		assert(m_executionContext != 0);

		if (m_stylesheetRoot->isOutputMethodSet() == false)
		{
			if (equalsIgnoreCaseASCII(getPendingElementName(),
								 Constants::ELEMNAME_HTML_STRING) == true &&
				pendingAttributesHasDefaultNS() == false)
			{
				if (getFormatterListener()->getOutputFormat() == FormatterListener::OUTPUT_METHOD_XML)
				{
					// Yuck!!! Ugly hack to switch to HTML on-the-fly.
					FormatterListener* const	theFormatter =
							getFormatterListener();
					assert(theFormatter->getWriter() != 0);

					setFormatterListenerImpl(
						m_executionContext->createFormatterToHTML(
							*theFormatter->getWriter(),
							theFormatter->getEncoding(),
							theFormatter->getMediaType(),
							theFormatter->getDoctypeSystem(),
							theFormatter->getDoctypePublic(),
							true,	// indent
							theFormatter->getIndent() > 0 ? theFormatter->getIndent() :
											StylesheetExecutionContext::eDefaultHTMLIndentAmount));

					if (m_hasCDATASectionElements == true)
					{
						m_hasCDATASectionElements = false;
					}
				}
			}
		}
	}

	if(getHasPendingStartDocument() == true && getMustFlushPendingStartDocument() == true)
	{
		startDocument();
	}

	XalanDOMString&		thePendingElementName = getPendingElementNameImpl();

	if(0 != length(thePendingElementName) && getMustFlushPendingStartDocument() == true)
	{
		assert(getFormatterListener() != 0);
		assert(m_executionContext != 0);

		if(m_hasCDATASectionElements == true)
		{
			m_cdataStack.push_back(isCDataResultElem(thePendingElementName));
		}

		AttributeListImpl&	thePendingAttributes =
				getPendingAttributesImpl();

		getFormatterListener()->startElement(c_wstr(thePendingElementName), thePendingAttributes);

		if(getTraceListeners() > 0)
		{
			const GenerateEvent		ge(
				GenerateEvent::EVENTTYPE_STARTELEMENT,
				thePendingElementName,
				&thePendingAttributes);

			fireGenerateEvent(ge);
		}

		thePendingAttributes.clear();

		clear(thePendingElementName);
	}
}



void
XSLTEngineImpl::startElement(const XalanDOMChar*	name)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	m_resultNamespacesStack.pushContext();

	setPendingElementName(name);

	setMustFlushPendingStartDocument(true);
}



void
XSLTEngineImpl::startElement(
			const XalanDOMChar*		name,
			AttributeListType&		atts)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	const unsigned int	nAtts = atts.getLength();

	assert(m_outputContextStack.empty() == false);

	AttributeListImpl&	thePendingAttributes =
		getPendingAttributesImpl();

	thePendingAttributes.clear();

	for(unsigned int i = 0; i < nAtts; i++)
	{
		thePendingAttributes.addAttribute(
			atts.getName(i),
			atts.getType(i),
			atts.getValue(i));
	}

	m_resultNamespacesStack.pushContext();

	setPendingElementName(name);
}



void
XSLTEngineImpl::endElement(const XalanDOMChar*	name)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	getFormatterListener()->endElement(name);

	if(getTraceListeners() > 0)
	{
		const GenerateEvent		ge(GenerateEvent::EVENTTYPE_ENDELEMENT, name);

		fireGenerateEvent(ge);
	}

	m_resultNamespacesStack.popContext();

	if(m_hasCDATASectionElements == true)
	{
		assert(m_cdataStack.empty() == false);

		m_cdataStack.pop_back();
	}
}



void
XSLTEngineImpl::characters(
			const XalanDOMChar*			ch,
			XalanDOMString::size_type 	length)
{
	characters(ch,
			   0,
			   length);<commit_msg>Fixed glitch where GenerateEvent for endElement did not have the element name.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 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 <EMAIL>@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/>.
 */



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



#include <xercesc/sax/DocumentHandler.hpp>
#include <xercesc/sax/EntityResolver.hpp>
#include <xercesc/sax/Locator.hpp>



#include <xalanc/Include/STLHelper.hpp>



#include <xalanc/XalanDOM/XalanDOMException.hpp>
#include <xalanc/XalanDOM/XalanNode.hpp>
#include <xalanc/XalanDOM/XalanAttr.hpp>
#include <xalanc/XalanDOM/XalanComment.hpp>
#include <xalanc/XalanDOM/XalanCDATASection.hpp>
#include <xalanc/XalanDOM/XalanNodeList.hpp>
#include <xalanc/XalanDOM/XalanNamedNodeMap.hpp>
#include <xalanc/XalanDOM/XalanProcessingInstruction.hpp>
#include <xalanc/XalanDOM/XalanText.hpp>



#include <xalanc/PlatformSupport/PrintWriter.hpp>
#include <xalanc/PlatformSupport/StringTokenizer.hpp>
#include <xalanc/PlatformSupport/XalanLocator.hpp>
#include <xalanc/PlatformSupport/XalanUnicode.hpp>



#include <xalanc/DOMSupport/DOMServices.hpp>
#include <xalanc/DOMSupport/DOMSupport.hpp>



#include <xalanc/XMLSupport/FormatterToHTML.hpp>
#include <xalanc/XMLSupport/FormatterTreeWalker.hpp>
#include <xalanc/XMLSupport/XMLParserLiaison.hpp>
#include <xalanc/XMLSupport/FormatterTreeWalker.hpp>



#include <xalanc/XPath/ElementPrefixResolverProxy.hpp>
#include <xalanc/XPath/XalanQNameByReference.hpp>
#include <xalanc/XPath/ResultTreeFragBase.hpp>
#include <xalanc/XPath/XObject.hpp>
#include <xalanc/XPath/XObjectFactory.hpp>
#include <xalanc/XPath/XPathEnvSupport.hpp>
#include <xalanc/XPath/XPathEnvSupportDefault.hpp>
#include <xalanc/XPath/XPathExecutionContextDefault.hpp>
#include <xalanc/XPath/XPathFactory.hpp>
#include <xalanc/XPath/XPathFunctionTable.hpp>
#include <xalanc/XPath/XPathProcessorImpl.hpp>



#include "Constants.hpp"
#include "ElemWithParam.hpp"
#include "FunctionCurrent.hpp"
#include "FunctionDocument.hpp"
#include "FunctionElementAvailable.hpp"
#include "FunctionFunctionAvailable.hpp"
#include "FunctionFormatNumber.hpp"
#include "FunctionGenerateID.hpp"
#include "FunctionKey.hpp"
#include "FunctionSystemProperty.hpp"
#include "FunctionUnparsedEntityURI.hpp"
#include "GenerateEvent.hpp"
#include "ProblemListener.hpp"
#include "ProblemListenerDefault.hpp"
#include "Stylesheet.hpp"
#include "StylesheetConstructionContext.hpp"
#include "StylesheetExecutionContext.hpp"
#include "StylesheetHandler.hpp"
#include "StylesheetRoot.hpp"
#include "TraceListener.hpp"
#include "XSLTInputSource.hpp"
#include "XSLTProcessorException.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



const XalanDOMString	XSLTEngineImpl::s_emptyString;



//==========================================================
// SECTION: Constructors
//==========================================================

XSLTEngineImpl::XSLTEngineImpl(
			XMLParserLiaison&	parserLiaison,
			XPathEnvSupport&	xpathEnvSupport,
			DOMSupport&			domSupport,
			XObjectFactory&		xobjectFactory,
			XPathFactory&		xpathFactory) :
	XSLTProcessor(),
	PrefixResolver(),
	m_resultNameSpacePrefix(),
	m_resultNameSpaceURL(),
	m_xpathFactory(xpathFactory),
	m_xobjectFactory(xobjectFactory),
	m_xpathProcessor(new XPathProcessorImpl),
	m_cdataStack(),
	m_stylesheetLocatorStack(),
	m_defaultProblemListener(),
	m_problemListener(&m_defaultProblemListener),
	m_stylesheetRoot(0),
	m_traceSelects(false),
	m_quietConflictWarnings(true),
	m_diagnosticsPrintWriter(0),
	m_durationsTable(),
	m_traceListeners(),
	m_uniqueNSValue(0),
	m_topLevelParams(),
	m_parserLiaison(parserLiaison),
	m_xpathEnvSupport(xpathEnvSupport),
	m_domSupport(domSupport),
	m_executionContext(0),
	m_outputContextStack(),
	m_resultNamespacesStack(),
	m_dummyAttributesList(),
	m_scratchString(),
	m_attributeNamesVisited(),
	m_hasStripOrPreserveSpace(false),
	m_hasCDATASectionElements(false),
	m_xpathConstructionContext()
{
	m_outputContextStack.pushContext();
}



void
XSLTEngineImpl::reset()
{
	m_topLevelParams.clear();
	m_durationsTable.clear();
	m_stylesheetLocatorStack.clear();
	m_cdataStack.clear();

	m_stylesheetRoot = 0;

	m_outputContextStack.reset();

	m_outputContextStack.pushContext();

	m_xpathEnvSupport.reset();
	m_xpathFactory.reset();
	m_xobjectFactory.reset();
	m_domSupport.reset();

	m_resultNamespacesStack.clear();

	m_attributeNamesVisited.clear();

	m_hasStripOrPreserveSpace = false;
	m_hasCDATASectionElements = false;

	m_xpathConstructionContext.reset();
}



XSLTEngineImpl::~XSLTEngineImpl()
{
	reset();
}



//==========================================================
// SECTION: Main API Functions
//==========================================================



static const XalanDOMChar	s_dummyString = 0;


void
XSLTEngineImpl::process(
			const XSLTInputSource&			inputSource, 
	        const XSLTInputSource&			stylesheetSource,
	        XSLTResultTarget&				outputTarget,
			StylesheetConstructionContext&	constructionContext,
			StylesheetExecutionContext&		executionContext)
{
	XalanDOMString	xslIdentifier;

	if (0 == stylesheetSource.getSystemId())
	{
		xslIdentifier = XALAN_STATIC_UCODE_STRING("Input XSL");
	}
	else
	{
		xslIdentifier = stylesheetSource.getSystemId();
	}

	bool totalTimeID = true;

	if(m_diagnosticsPrintWriter != 0)
	{
		pushTime(&totalTimeID);
	}

	XalanNode*	sourceTree = getSourceTreeFromInput(inputSource);

	m_stylesheetRoot = processStylesheet(stylesheetSource, constructionContext);

	if(0 != sourceTree && m_stylesheetRoot == 0)
	{
		// Didn't get a stylesheet from the input source, so look for a
		// stylesheet processing instruction...

		// The PI must be a child of the document...
		const XalanNode*	child = sourceTree->getFirstChild();

		XalanDOMString		theCurrentToken;
		XalanDOMString		theStylesheetURI;

		bool			isOK = false;

		while(child != 0 && isOK == false && theStylesheetURI.empty() == true)
		{
			if(XalanNode::PROCESSING_INSTRUCTION_NODE == child->getNodeType())
			{
				const XalanDOMString&	nodeName = child->getNodeName();

				if(equals(nodeName, s_stylesheetNodeName))
				{
					StringTokenizer 	tokenizer(child->getNodeValue(), s_piTokenizerString);

					while(tokenizer.hasMoreTokens() == true && (isOK == false || theStylesheetURI.empty() == true))
					{
						tokenizer.nextToken(theCurrentToken);

						if(equals(theCurrentToken, s_typeString))
						{
							tokenizer.nextToken(theCurrentToken);


							const XalanDOMString::size_type		theLength =
									theCurrentToken.length();

							if (theLength > 2)
							{
								theCurrentToken.erase(theLength - 1, 1);
								theCurrentToken.erase(0, 1);

								if(equals(theCurrentToken, s_typeValueString1) ||
									equals(theCurrentToken, s_typeValueString2) ||
									equals(theCurrentToken, s_typeValueString3) ||
									equals(theCurrentToken, s_typeValueString4))
								{
									isOK = true;
								}
							}
						}
						else if(equals(theCurrentToken, s_hrefString))
						{
							tokenizer.nextToken(theCurrentToken);

							const XalanDOMString::size_type		theLength =
									theCurrentToken.length();

							if (theLength > 2)
							{
								// Trim of the starting and trailing delimiters...
								theStylesheetURI.assign(theCurrentToken, 1, theLength - 2);
							}
						}
					}
				}
			}

			child = child->getNextSibling();
		}

		if (isOK == true && theStylesheetURI.empty() == false)
		{
			const XalanDOMChar* const	pxch = inputSource.getSystemId();

			const XalanDOMString		sysid(pxch == 0 ? &s_dummyString : pxch); 

			getStylesheetFromPIURL(
					theStylesheetURI,
					*sourceTree,
					sysid,
					true,
					constructionContext);
		}
	}

	if(0 == m_stylesheetRoot)
	{
		error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Failed to process stylesheet!")));
	}
	else if(0 != sourceTree)
	{
		executionContext.setStylesheetRoot(m_stylesheetRoot);

		FormatterListener* const	theFormatter =
				outputTarget.getFormatterListener();

		if (theFormatter != 0 && theFormatter->getPrefixResolver() == 0)
		{
			theFormatter->setPrefixResolver(this);
		}

		m_hasStripOrPreserveSpace = m_stylesheetRoot->hasPreserveOrStripSpaceElements();

		m_hasCDATASectionElements = m_stylesheetRoot->hasCDATASectionElements();

		m_stylesheetRoot->process(sourceTree, outputTarget, executionContext);
	}

	if(m_diagnosticsPrintWriter != 0)
	{
		displayDuration(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Total time")), &totalTimeID);
	}
}



void
XSLTEngineImpl::process(
			const XSLTInputSource&			inputSource, 
	        XSLTResultTarget&				outputTarget,
			StylesheetExecutionContext&		executionContext)
{
	bool	totalTimeID = true;

	if(m_diagnosticsPrintWriter != 0)
	{
		pushTime(&totalTimeID);
	}

	XalanNode* const	sourceTree = getSourceTreeFromInput(inputSource);

	if(0 != sourceTree)
	{
		if (m_stylesheetRoot == 0)
		{
			error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("No stylesheet is available to process!")));
		}

		FormatterListener* const	theFormatter =
				outputTarget.getFormatterListener();

		if (theFormatter != 0 && theFormatter->getPrefixResolver() == 0)
		{
			theFormatter->setPrefixResolver(this);
		}

		m_hasStripOrPreserveSpace = m_stylesheetRoot->hasPreserveOrStripSpaceElements();

		m_hasCDATASectionElements = m_stylesheetRoot->hasCDATASectionElements();

		m_stylesheetRoot->process(sourceTree, outputTarget, executionContext);
	}

	if(m_diagnosticsPrintWriter != 0)
	{
		displayDuration(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Total time")), &totalTimeID);
	}
}



StylesheetRoot*
XSLTEngineImpl::processStylesheet(
			const XalanDOMString&			xsldocURLString,
			StylesheetConstructionContext&	constructionContext)
{
	const XSLTInputSource	input(c_wstr(xsldocURLString));

	return processStylesheet(input, constructionContext);
}



StylesheetRoot*
XSLTEngineImpl::processStylesheet(
  			const XSLTInputSource&			stylesheetSource,
			StylesheetConstructionContext&	constructionContext)
{
	StylesheetRoot*		theStylesheet = 0;

	const XalanDOMChar* const	systemID = stylesheetSource.getSystemId();
	XalanNode* const			stylesheetNode = stylesheetSource.getNode();

	if (systemID != 0 || stylesheetNode != 0 || stylesheetSource.getStream() != 0)
	{
		XalanDOMString	xslIdentifier;

		theStylesheet = constructionContext.create(stylesheetSource);

		StylesheetHandler	stylesheetProcessor(*theStylesheet, constructionContext);

		if(stylesheetNode != 0)
		{
			const XalanNode::NodeType	theType = stylesheetNode->getNodeType();

			if (theType != XalanNode::ELEMENT_NODE && theType != XalanNode::DOCUMENT_NODE)
			{
				error(StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Compiling a stylesheet from a DOM instance requires a Document or Element node")));
			}
			else
			{
				xslIdentifier = XALAN_STATIC_UCODE_STRING("Input XSL");

				FormatterTreeWalker tw(stylesheetProcessor);

				if (theType == XalanNode::DOCUMENT_NODE)
				{
					tw.traverse(stylesheetNode);
				}
				else
				{
					stylesheetProcessor.startDocument();

					tw.traverseSubtree(stylesheetNode);

					stylesheetProcessor.endDocument();
				}
			}
		}
		else
		{
			if (systemID != 0)
			{
				xslIdentifier = systemID;
			}

			if(m_diagnosticsPrintWriter != 0)
			{
				diag(XALAN_STATIC_UCODE_STRING("========= Parsing ") + xslIdentifier + XALAN_STATIC_UCODE_STRING(" =========="));

				pushTime(&xslIdentifier);
			}

			m_parserLiaison.parseXMLStream(stylesheetSource,
										   stylesheetProcessor);

			if(m_diagnosticsPrintWriter != 0)
			{
				displayDuration(XALAN_STATIC_UCODE_STRING("Parse of ") + xslIdentifier, &xslIdentifier);
			}
		}

		theStylesheet->postConstruction(constructionContext);
	}

	return theStylesheet;
}



//==========================================================
// SECTION: XML Parsing Functions
//==========================================================

XalanNode*
XSLTEngineImpl::getSourceTreeFromInput(const XSLTInputSource&	inputSource)
{
	XalanNode*		sourceTree = inputSource.getNode();

	if(0 == sourceTree)
	{
		const XalanDOMString	xmlIdentifier = 0 != inputSource.getSystemId() ?
												XalanDOMString(inputSource.getSystemId()) :
												StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("Input XML"));

		if(m_diagnosticsPrintWriter != 0)
		{
			// In case we have a fragment identifier, go ahead and 
			// try to parse the XML here.
			diag(XALAN_STATIC_UCODE_STRING("========= Parsing ") +
						xmlIdentifier +
						XALAN_STATIC_UCODE_STRING(" =========="));

			pushTime(&xmlIdentifier);
		}

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

		XalanDocument* const	theDocument =
						m_parserLiaison.parseXMLStream(inputSource,
													   xmlIdentifier);
		assert(theDocument != 0);

#if defined(XALAN_VQ_SPECIAL_TRACE)
		QuantifyStopRecordingData();
#endif
		if(0 != m_diagnosticsPrintWriter)
		{
			displayDuration(
				XALAN_STATIC_UCODE_STRING("Parse of ") + xmlIdentifier,
				&xmlIdentifier);
		}

		m_xpathEnvSupport.setSourceDocument(xmlIdentifier, theDocument);

		sourceTree = theDocument;
	}

	return sourceTree;
}



const XalanDOMString*
XSLTEngineImpl::getNamespaceForPrefix(const XalanDOMString&		prefix) const
{
	return m_resultNamespacesStack.getNamespaceForPrefix(prefix);
}



const XalanDOMString&
XSLTEngineImpl::getURI() const
{
	return s_emptyString;
}



XalanDocument*
XSLTEngineImpl::parseXML(
			const XalanDOMString&	urlString,
			DocumentHandlerType*	docHandler,
			XalanDocument*			docToRegister)
{
	
	XalanDocument*	doc =
			m_xpathEnvSupport.getSourceDocument(urlString);

	if(doc == 0)
	{
		EntityResolverType* const	theResolver = 
			m_parserLiaison.getEntityResolver();

		if (theResolver == 0)
		{
			const XSLTInputSource	inputSource(c_wstr(urlString));

			doc = parseXML(inputSource, docHandler, docToRegister);
		}
		else
		{
			const XalanAutoPtr<InputSourceType>		resolverInputSource =
				theResolver->resolveEntity(0, c_wstr(urlString));

			if (resolverInputSource.get() != 0)
			{
				doc = parseXML(*resolverInputSource.get(), docHandler, docToRegister);
			}
			else
			{
				const XSLTInputSource	inputSource(c_wstr(urlString));

				doc = parseXML(inputSource, docHandler, docToRegister);
			}
		}

		if (doc != 0)
		{
			m_xpathEnvSupport.setSourceDocument(urlString, doc);
		}
	}

	return doc;
}



XalanDocument*
XSLTEngineImpl::parseXML(
			const InputSourceType&	inputSource,
			DocumentHandlerType*	docHandler,
			XalanDocument*			docToRegister)
{
	if(0 != docHandler)
	{
		m_parserLiaison.parseXMLStream(inputSource, *docHandler);

		return docToRegister;
	}
	else
	{
		return m_parserLiaison.parseXMLStream(inputSource);
	}
}



Stylesheet*
XSLTEngineImpl::getStylesheetFromPIURL(
			const XalanDOMString&			xslURLString,
			XalanNode&						fragBase,
			const XalanDOMString&			xmlBaseIdent,
			bool							isRoot,
			StylesheetConstructionContext&	constructionContext)
{
	Stylesheet*				stylesheet = 0;

	XalanDOMString			stringHolder;

	XalanDOMString			localXSLURLString = trim(xslURLString);

	const XalanDOMString::size_type		fragIndex = indexOf(localXSLURLString, XalanUnicode::charNumberSign);

	const XalanDocument*	stylesheetDoc = 0;

	if(fragIndex == 0)
	{
		const XalanDOMString	fragID(localXSLURLString, 1);

		const XalanElement*		nsNode = 0;

		const XalanNode::NodeType	theType = fragBase.getNodeType();

		if (theType == XalanNode::DOCUMENT_NODE)
		{
			const XalanDocument&	doc =
#if defined(XALAN_OLD_STYLE_CASTS)
				(const XalanDocument&)fragBase;
#else
				static_cast<const XalanDocument&>(fragBase);
#endif

			nsNode = doc.getDocumentElement(); 
		}
		else if	(theType == XalanNode::ELEMENT_NODE)
		{
#if defined(XALAN_OLD_STYLE_CASTS)
			nsNode = (const XalanElement*)&fragBase;
#else
			nsNode = static_cast<const XalanElement*>(&fragBase);
#endif
		}
		else
		{
			XalanNode* const	node = fragBase.getParentNode();

			if	(node->getNodeType() == XalanNode::ELEMENT_NODE) 
			{
#if defined(XALAN_OLD_STYLE_CASTS)
				nsNode = (const XalanElement*)&fragBase;
#else
				nsNode = static_cast<XalanElement*>(node);
#endif
			}
			else
			{
				error("Could not identify fragment: " + fragID);
			}
		}

		// Try a bunch of really ugly stuff to find the fragment.
		// What's the right way to do this?
		XalanDOMString	ds(XALAN_STATIC_UCODE_STRING("id("));

		ds += fragID;
		ds += XALAN_STATIC_UCODE_STRING(")");

		ElementPrefixResolverProxy		theProxy(nsNode, m_xpathEnvSupport, m_domSupport);

		XPathExecutionContextDefault	theExecutionContext(m_xpathEnvSupport,
															m_domSupport,
															m_xobjectFactory,
															&fragBase,
															0,
															&theProxy);

		const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
		assert(xobj.null() == false);

		NodeRefList		nl(xobj->nodeset());

		if(nl.getLength() == 0)
		{
			NodeRefList		theEmptyList;

			ds = XALAN_STATIC_UCODE_STRING("//*[@id='");
			ds += fragID;
			ds += XALAN_STATIC_UCODE_STRING("']");

			theExecutionContext.setContextNodeList(theEmptyList);

			const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
			assert(xobj.null() == false);

			nl = xobj->nodeset();

			if(nl.getLength() == 0)
			{
				ds = XALAN_STATIC_UCODE_STRING("//*[@name='");
				ds += fragID;
				ds += XALAN_STATIC_UCODE_STRING("']");

				theExecutionContext.setContextNodeList(theEmptyList);

				const XObjectPtr	xobj(evalXPathStr(ds, theExecutionContext));
				assert(xobj.null() == false);

				nl = xobj->nodeset();

				if(nl.getLength() == 0)
				{
					// Well, hell, maybe it's an XPath...
					theExecutionContext.setContextNodeList(theEmptyList);

					const XObjectPtr	xobj(evalXPathStr(fragID, theExecutionContext));
					assert(xobj.null() == false);

					nl = xobj->nodeset();
				}
			}
		}

		if(nl.getLength() == 0)
		{
			error("Could not find fragment: " + fragID);
		}

		XalanNode* const	frag = nl.item(0);

		if(XalanNode::ELEMENT_NODE == frag->getNodeType())
		{
			if(m_diagnosticsPrintWriter != 0)
			{
				pushTime(frag);
			}

			XalanAutoPtr<Stylesheet>	theGuard;

			if(isRoot)
			{
				StylesheetRoot* const	theLocalRoot =
					constructionContext.create(stringHolder);

				stylesheet = theLocalRoot;

				m_stylesheetRoot = theLocalRoot;
			}
			else
			{
#if defined(XALAN_OLD_STYLE_CASTS)
				stylesheet = constructionContext.create(*((StylesheetRoot*)m_stylesheetRoot), stringHolder);
#else
				stylesheet = constructionContext.create(*const_cast<StylesheetRoot*>(m_stylesheetRoot), stringHolder);
#endif

				theGuard.reset(stylesheet);
			}

			StylesheetHandler stylesheetProcessor(*stylesheet, constructionContext);

			FormatterTreeWalker tw(stylesheetProcessor);

			stylesheetProcessor.startDocument();

			tw.traverseSubtree(frag);

			stylesheetProcessor.endDocument();

			if(m_diagnosticsPrintWriter != 0)
			{
				displayDuration(
						XalanDOMString(XALAN_STATIC_UCODE_STRING("Setup of ")) + localXSLURLString,
						frag);
			}

			stylesheet->postConstruction(constructionContext);

			theGuard.release();
		}
		else
		{
			stylesheetDoc = 0;

			error("Node pointed to by fragment identifier was not an element: " + fragID);
		}
	}
	else
	{
		if(m_diagnosticsPrintWriter != 0)
		{
			diag(XalanDOMString(XALAN_STATIC_UCODE_STRING("========= Parsing and preparing ")) +
					localXSLURLString +
					XALAN_STATIC_UCODE_STRING(" =========="));
			pushTime(&localXSLURLString);
		}

		XalanAutoPtr<Stylesheet>	theGuard;

		const XalanDocument* const	theOwnerDocument =
				fragBase.getNodeType() == XalanNode::DOCUMENT_NODE ?
#if defined(XALAN_OLD_STYLE_CASTS)
				(const XalanDocument*)&fragBase :
#else
				static_cast<const XalanDocument*>(&fragBase) :
#endif
				fragBase.getOwnerDocument();
		assert(theOwnerDocument != 0);

		// Catch any XMLExceptions thrown, since we may not
		// be able to resolve the URL.  In that case, the
		// parser will throw an error.  We do this because
		// we don't know what sort of EntityResolvers might
		// be active, so we never want to error out here.
		try
		{
			if (length(xmlBaseIdent) == 0)
			{
				URISupport::getURLStringFromString(
							localXSLURLString,
							m_xpathEnvSupport.findURIFromDoc(theOwnerDocument),
							localXSLURLString);
			}
			else
			{
				URISupport::getURLStringFromString(
							localXSLURLString,
							xmlBaseIdent,
							localXSLURLString);
			}
		}
		catch(const XERCES_CPP_NAMESPACE_QUALIFIER XMLException&)
		{
		}

		if(isRoot)
		{
			StylesheetRoot* const	theLocalRoot =
					constructionContext.create(localXSLURLString);

			stylesheet = theLocalRoot;

			m_stylesheetRoot = theLocalRoot;
		}
		else
		{
#if defined(XALAN_OLD_STYLE_CASTS)
			stylesheet = new Stylesheet(*(StylesheetRoot*)m_stylesheetRoot, localXSLURLString, constructionContext);
#else
			stylesheet = new Stylesheet(*const_cast<StylesheetRoot*>(m_stylesheetRoot), localXSLURLString, constructionContext);
#endif

			theGuard.reset(stylesheet);
		}

		StylesheetHandler stylesheetProcessor(*stylesheet, constructionContext);

		typedef StylesheetConstructionContext::URLAutoPtrType	URLAutoPtrType;

		URLAutoPtrType	xslURL(constructionContext.getURLFromString(localXSLURLString));

		XSLTInputSource		inputSource(xslURL->getURLText());

		m_parserLiaison.parseXMLStream(inputSource, stylesheetProcessor);

		stylesheet->postConstruction(constructionContext);

		theGuard.release();

		if(m_diagnosticsPrintWriter != 0)
		{
			displayDuration("Parsing and init of " + localXSLURLString, &localXSLURLString);
		}
	}

	return stylesheet;
}



const StylesheetRoot*
XSLTEngineImpl::getStylesheetRoot() const
{
	return m_stylesheetRoot;
}



void
XSLTEngineImpl::setStylesheetRoot(const StylesheetRoot*		theStylesheet)
{
	m_stylesheetRoot = theStylesheet;
}



void
XSLTEngineImpl::setExecutionContext(StylesheetExecutionContext*		theExecutionContext)
{
	m_executionContext = theExecutionContext;
}



//==========================================================
// SECTION: Diagnostic functions
//==========================================================

XSLTEngineImpl::size_type
XSLTEngineImpl::getTraceListeners() const
{
	return m_traceListeners.size();
}



void
XSLTEngineImpl::addTraceListener(TraceListener* tl)
{
	if (tl != 0)
	{
		m_traceListeners.push_back(tl);
	}
}



void
XSLTEngineImpl::removeTraceListener(TraceListener*	tl)
{
	XALAN_USING_STD(remove)

	const TraceListenerVectorType::iterator		i =
		remove(
			m_traceListeners.begin(),
			m_traceListeners.end(),
			tl);

	m_traceListeners.erase(i);
}



void
XSLTEngineImpl::fireGenerateEvent(const GenerateEvent&	ge)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerGenerateFunctor(ge));
}



void
XSLTEngineImpl::fireSelectEvent(const SelectionEvent&	se)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerSelectFunctor(se));
}



void
XSLTEngineImpl::fireTraceEvent(const TracerEvent& te)
{
	XALAN_USING_STD(for_each)

	for_each(
		m_traceListeners.begin(),
		m_traceListeners.end(),
		TraceListener::TraceListenerTraceFunctor(te));
}



bool
XSLTEngineImpl::getTraceSelects() const
{
	return m_traceSelects;
}



void
XSLTEngineImpl::setTraceSelects(bool	b)
{
	m_traceSelects = b;
}



void
XSLTEngineImpl::message(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	problem(msg, ProblemListener::eMESSAGE, sourceNode, styleNode);
}



void
XSLTEngineImpl::message(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eMESSAGE, locator, sourceNode);
}



void
XSLTEngineImpl::problem(
			const XalanDOMString&				msg, 
			ProblemListener::eClassification	classification,
			const XalanNode*					sourceNode,
			const ElemTemplateElement*			styleNode) const
{
	const XalanDOMChar*		id = 0;

	XalanDOMString			uri;

	int						lineNumber = XalanLocator::getUnknownValue();
	int 					columnNumber = XalanLocator::getUnknownValue();

	const LocatorType*		locator = getLocatorFromStack();

	if (locator == 0 && styleNode != 0)
	{
		locator = styleNode->getLocator();
	}

	if (locator != 0)
	{
		id = locator->getPublicId();

		if (id == 0)
		{
			id = locator->getSystemId();
		}

		if (id != 0)
		{
			uri = id;
		}

		lineNumber = locator->getLineNumber();
		columnNumber = locator->getColumnNumber();
	}
	else if (styleNode != 0)
	{
		lineNumber = styleNode->getLineNumber();
		columnNumber = styleNode->getColumnNumber();

		uri = styleNode->getURI();
	}

	if (m_problemListener != 0)
	{
		m_problemListener->problem(
					ProblemListener::eXSLPROCESSOR,
					classification,
					sourceNode,
					styleNode,
					msg,
					id,
					lineNumber,
					columnNumber);
	}

	if (classification == ProblemListener::eERROR)
	{
		throw XSLTProcessorException(msg, uri, lineNumber, columnNumber);
	}
}



static const XalanDOMChar	theDummy = 0;

void
XSLTEngineImpl::problem(
			const XalanDOMString&				msg, 
			ProblemListener::eClassification	classification,
			const LocatorType&					locator,
			const XalanNode*					sourceNode) const
{
	const XalanDOMChar*		id = locator.getSystemId();

	if (id == 0)
	{
		id = &theDummy;
	}

	const XalanLocator::size_type	lineNumber = locator.getLineNumber();
	const XalanLocator::size_type 	columnNumber = locator.getColumnNumber();

	if (m_problemListener != 0)
	{
		m_problemListener->problem(
					ProblemListener::eXSLPROCESSOR,
					classification,
					sourceNode,
					0,
					msg,
					id,
					lineNumber,
					columnNumber);
	}

	if (classification == ProblemListener::eERROR)
	{
		throw XSLTProcessorException(msg, XalanDOMString(id), lineNumber, columnNumber);
	}
}



void
XSLTEngineImpl::warn(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	const LocatorType* const	locator = styleNode == 0 ? 0 : styleNode->getLocator();

	if (locator != 0)
	{
		problem(msg, ProblemListener::eWARNING, *locator, sourceNode);
	}
	else
	{
		problem(msg, ProblemListener::eWARNING, sourceNode, styleNode);
	}
}



void
XSLTEngineImpl::warn(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eWARNING, locator, sourceNode);
}



void
XSLTEngineImpl::warn(
			const char*					msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	warn(TranscodeFromLocalCodePage(msg), sourceNode, styleNode);
}



void
XSLTEngineImpl::error(
			const XalanDOMString&		msg,
			const XalanNode*			sourceNode,
			const ElemTemplateElement*	styleNode) const
{
	problem(msg, ProblemListener::eERROR, sourceNode, styleNode);
}



void
XSLTEngineImpl::error(
			const XalanDOMString&	msg,
			const LocatorType&		locator,
			const XalanNode*		sourceNode) const
{
	problem(msg, ProblemListener::eERROR, locator, sourceNode);
}



void
XSLTEngineImpl::pushTime(const void*	key)
{
	if(0 != key)
	{
#if defined(XALAN_STRICT_ANSI_HEADERS)
		m_durationsTable[key] = std::clock();
#else
		m_durationsTable[key] = clock();
#endif
	}
}



XSLTEngineImpl::ClockType
XSLTEngineImpl::popDuration(const void*		key)
{
	ClockType 	clockTicksDuration = 0;

	if(0 != key)
	{
		const DurationsTableMapType::iterator	i =
				m_durationsTable.find(key);

		if (i != m_durationsTable.end())
		{
#if defined(XALAN_STRICT_ANSI_HEADERS)
			clockTicksDuration = std::clock() - (*i).second;
#else
			clockTicksDuration = clock() - (*i).second;
#endif

			m_durationsTable.erase(i);
		}
	}

	return clockTicksDuration;
}



void
XSLTEngineImpl::displayDuration(
			const XalanDOMString&	info,
			const void*				key)
{
	if(0 != key)
	{
		const ClockType	theDuration = popDuration(key);

		if(m_diagnosticsPrintWriter != 0)
		{
			const double	millis = (double(theDuration) / CLOCKS_PER_SEC) * 1000.0;

			m_diagnosticsPrintWriter->print(info);

			m_diagnosticsPrintWriter->print(" took ");
			m_diagnosticsPrintWriter->print(millis);
			m_diagnosticsPrintWriter->println(" milliseconds.");
		}
	}
}



void
XSLTEngineImpl::setDiagnosticsOutput(PrintWriter*	pw)
{
	m_diagnosticsPrintWriter = pw;

	m_problemListener->setPrintWriter(pw);
}



void
XSLTEngineImpl::diag(const XalanDOMString& 	s) const
{
	if (0 != m_diagnosticsPrintWriter)
	{
		m_diagnosticsPrintWriter->println(s);
	}
}



void
XSLTEngineImpl::diag(const char*	s) const
{
	diag(TranscodeFromLocalCodePage(s));
}



void
XSLTEngineImpl::setQuietConflictWarnings(bool	b)
{
	m_quietConflictWarnings = b;
}



void
XSLTEngineImpl::setDocumentLocator(const LocatorType*	/* locator */)
{
	// Do nothing for now
}



void
XSLTEngineImpl::traceSelect(
			StylesheetExecutionContext& 	executionContext,
			const ElemTemplateElement&		theTemplate,
			const NodeRefListBase&			nl,
			const XPath*					xpath) const
{
	if (0 != m_diagnosticsPrintWriter)
	{
		XalanDOMString	msg = theTemplate.getElementName() + XalanDOMString(XALAN_STATIC_UCODE_STRING(": "));

		if(xpath != 0)
		{
			msg += xpath->getExpression().getCurrentPattern();
			msg += XALAN_STATIC_UCODE_STRING(", ");
		}
		else
		{
			msg += XALAN_STATIC_UCODE_STRING("*|text(), (default select), ");
		}

		msg += UnsignedLongToDOMString(nl.getLength());
		msg += XALAN_STATIC_UCODE_STRING(" selected");

		const XalanQName* const		mode = executionContext.getCurrentMode();

		if(mode != 0 && mode->isEmpty() == false)
		{
			msg += XALAN_STATIC_UCODE_STRING(", mode = {");
			msg += mode->getNamespace();
			msg += XALAN_STATIC_UCODE_STRING("}");
			msg += mode->getLocalPart();
		}

		m_diagnosticsPrintWriter->println(msg);
	}
}



void
XSLTEngineImpl::startDocument()
{
	assert(getFormatterListener() != 0);
	assert(m_executionContext != 0);

	if (getHasPendingStartDocument() == false)
	{
		assert(m_cdataStack.empty() == true || m_outputContextStack.size() != 1);

		m_resultNamespacesStack.pushContext();

		setHasPendingStartDocument(true);

		setMustFlushPendingStartDocument(false);

		if (m_hasCDATASectionElements == true)
		{
			m_cdataStack.push_back(false);
		}
	}
	else if (getMustFlushPendingStartDocument() == true)
	{
		getFormatterListener()->startDocument();

		if(getTraceListeners() > 0)
		{
			const GenerateEvent		ge(GenerateEvent::EVENTTYPE_STARTDOCUMENT);

			fireGenerateEvent(ge);
		}

		// Reset this, but leave getMustFlushPendingStartDocument() alone,
		// since it will still be needed.
		setHasPendingStartDocument(false);
	}
}



void
XSLTEngineImpl::endDocument()
{
	assert(getFormatterListener() != 0);
	assert(m_executionContext != 0);

	setMustFlushPendingStartDocument(true);

	flushPending();

	getFormatterListener()->endDocument();

	if(getTraceListeners() > 0)
	{
		const GenerateEvent		ge(GenerateEvent::EVENTTYPE_ENDDOCUMENT);

		fireGenerateEvent(ge);
	}

	if (m_hasCDATASectionElements == true)
	{
		m_cdataStack.pop_back();
	}
	assert(m_cdataStack.empty() == true);

	m_resultNamespacesStack.popContext();

	assert(m_resultNamespacesStack.empty() == true);
}



void
XSLTEngineImpl::addResultAttribute(
			AttributeListImpl&			attList,
			const XalanDOMString&		aname,
			const XalanDOMChar*			value)
{
	assert(value != 0);

	// Always exclude the implicit XML declaration...
	if (equals(aname, DOMServices::s_XMLNamespacePrefix) == false) 
	{
		bool	fExcludeAttribute = false;

		if (equals(aname, DOMServices::s_XMLNamespace) == true)
		{
			// OK, we're adding a default namespace declaration.  So see if the length
			// of the namespace is 0.  If it's not, go ahead and add the declaration.
			// If it's not, it means we're "turning off" the previous default
			// declaration.

			const XalanDOMString* const		currentDefaultNamespace =
						getNamespaceForPrefix(s_emptyString);

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

			// Note that we use an empty string for the prefix, instead of "xmlns", since the
			// prefix really is "".
			if (theLength != 0)
			{
				if (currentDefaultNamespace != 0 &&
					equals(*currentDefaultNamespace, value, theLength) == true)
				{
					fExcludeAttribute = true;
				}
				else
				{
					addResultNamespaceDecl(s_emptyString, value, theLength);
				}
			}
			else
			{
				// OK, we're turning of the previous default namespace declaration.
				// Check to see if there is one, and if there isn't, don't add
				// the namespace declaration _and_ don't add the attribute.
				if (currentDefaultNamespace != 0 && length(*currentDefaultNamespace) != 0)
				{
					addResultNamespaceDecl(s_emptyString, value, theLength);
				}
				else
				{
					fExcludeAttribute = true;
				}
			}
		}
		else if (startsWith(aname, DOMServices::s_XMLNamespaceWithSeparator) == true)
		{
			assert(m_executionContext != 0);

			StylesheetExecutionContext::GetAndReleaseCachedString	prefixGuard(*m_executionContext);

			XalanDOMString&		prefix = prefixGuard.get();

			substring(aname, prefix, DOMServices::s_XMLNamespaceWithSeparatorLength);

			const XalanDOMString* const	theNamespace = getResultNamespaceForPrefix(prefix);

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

			if (theNamespace == 0 || equals(*theNamespace, value, theLength) == false)
			{
				addResultNamespaceDecl(prefix, value, theLength);
			}
			else
			{
				fExcludeAttribute = true;
			}
		}

		if (fExcludeAttribute == false)
		{
			attList.addAttribute(
				c_wstr(aname),
				c_wstr(Constants::ATTRTYPE_CDATA),
				c_wstr(value));
		}
	}
}



bool
XSLTEngineImpl::pendingAttributesHasDefaultNS() const
{
	const AttributeListImpl&	thePendingAttributes =
		getPendingAttributes();

	const unsigned int	n = thePendingAttributes.getLength();

	for(unsigned int i = 0; i < n; i++)
	{
		if(equals(thePendingAttributes.getName(i),
				  DOMServices::s_XMLNamespace) == true)
		{
			return true;
		}
	}

	return false;
}



void
XSLTEngineImpl::flushPending()
{
	if(getHasPendingStartDocument() == true && 0 != length(getPendingElementName()))
	{
		assert(getFormatterListener() != 0);
		assert(m_executionContext != 0);

		if (m_stylesheetRoot->isOutputMethodSet() == false)
		{
			if (equalsIgnoreCaseASCII(getPendingElementName(),
								 Constants::ELEMNAME_HTML_STRING) == true &&
				pendingAttributesHasDefaultNS() == false)
			{
				if (getFormatterListener()->getOutputFormat() == FormatterListener::OUTPUT_METHOD_XML)
				{
					// Yuck!!! Ugly hack to switch to HTML on-the-fly.
					FormatterListener* const	theFormatter =
							getFormatterListener();
					assert(theFormatter->getWriter() != 0);

					setFormatterListenerImpl(
						m_executionContext->createFormatterToHTML(
							*theFormatter->getWriter(),
							theFormatter->getEncoding(),
							theFormatter->getMediaType(),
							theFormatter->getDoctypeSystem(),
							theFormatter->getDoctypePublic(),
							true,	// indent
							theFormatter->getIndent() > 0 ? theFormatter->getIndent() :
											StylesheetExecutionContext::eDefaultHTMLIndentAmount));

					if (m_hasCDATASectionElements == true)
					{
						m_hasCDATASectionElements = false;
					}
				}
			}
		}
	}

	if(getHasPendingStartDocument() == true && getMustFlushPendingStartDocument() == true)
	{
		startDocument();
	}

	XalanDOMString&		thePendingElementName = getPendingElementNameImpl();

	if(0 != length(thePendingElementName) && getMustFlushPendingStartDocument() == true)
	{
		assert(getFormatterListener() != 0);
		assert(m_executionContext != 0);

		if(m_hasCDATASectionElements == true)
		{
			m_cdataStack.push_back(isCDataResultElem(thePendingElementName));
		}

		AttributeListImpl&	thePendingAttributes =
				getPendingAttributesImpl();

		getFormatterListener()->startElement(c_wstr(thePendingElementName), thePendingAttributes);

		if(getTraceListeners() > 0)
		{
			const GenerateEvent		ge(
				GenerateEvent::EVENTTYPE_STARTELEMENT,
				thePendingElementName,
				&thePendingAttributes);

			fireGenerateEvent(ge);
		}

		thePendingAttributes.clear();

		clear(thePendingElementName);
	}
}



void
XSLTEngineImpl::startElement(const XalanDOMChar*	name)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	m_resultNamespacesStack.pushContext();

	setPendingElementName(name);

	setMustFlushPendingStartDocument(true);
}



void
XSLTEngineImpl::startElement(
			const XalanDOMChar*		name,
			AttributeListType&		atts)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	const unsigned int	nAtts = atts.getLength();

	assert(m_outputContextStack.empty() == false);

	AttributeListImpl&	thePendingAttributes =
		getPendingAttributesImpl();

	thePendingAttributes.clear();

	for(unsigned int i = 0; i < nAtts; i++)
	{
		thePendingAttributes.addAttribute(
			atts.getName(i),
			atts.getType(i),
			atts.getValue(i));
	}

	m_resultNamespacesStack.pushContext();

	setPendingElementName(name);
}



void
XSLTEngineImpl::endElement(const XalanDOMChar*	name)
{
	assert(getFormatterListener() != 0);
	assert(name != 0);

	flushPending();

	getFormatterListener()->endElement(name);

	if(getTraceListeners() > 0)
	{
		const GenerateEvent		ge(GenerateEvent::EVENTTYPE_ENDELEMENT, name, &getPendingAttributesImpl());

		fireGenerateEvent(ge);
	}

	m_resultNamespacesStack.popContext();

	if(m_hasCDATASectionElements == true)
	{
		assert(m_cdataStack.empty() == false);

		m_cdataStack.pop_back();
	}
}



void
XSLTEngineImpl::characters(
			const XalanDOMChar*			ch,
			XalanDOMString::size_type 	length)
{
	characters(ch,
			   0,
			   length);
{'fraction_non_alphanumeric': 0.12128648123466325, 'fraction_numerical': 0.00386143670357868, 'mean_word_length': 4.6350108794834, 'pattern_counts': {'":': 2, '<': 97, '<?xml version=': 0, '>': 235, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 22}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6065709', 'n_tokens_mistral': 30925, 'n_tokens_neox': 26194, 'n_words': 4905}