/*
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "XPathEvaluator.hpp"



#include <xalanc/DOMSupport/DOMSupportDefault.hpp>



#include "ElementPrefixResolverProxy.hpp"
#include "XObjectFactoryDefault.hpp"
#include "XPathEnvSupportDefault.hpp"
#include "XPathConstructionContextDefault.hpp"
#include "XPathExecutionContextDefault.hpp"
#include "XPathFactoryDefault.hpp"
#include "XPathProcessorImpl.hpp"
#include "XPathInit.hpp"



XALAN_CPP_NAMESPACE_BEGIN



static XPathInit*	theXPathInit = 0;

static MemoryManagerType* s_memoryManager = 0;

void
XPathEvaluator::initialize(MemoryManagerType& theManager)
{
    theXPathInit = XPathInit::create(theManager);

    s_memoryManager = &theManager;
}



void
XPathEvaluator::terminate()
{
	theXPathInit->~XPathInit();

    s_memoryManager->deallocate(theXPathInit);

	theXPathInit = 0;

    s_memoryManager = 0;
}



XPathEvaluator::XPathEvaluator(MemoryManagerType& theManager) :
    m_xobjectFactory(theManager, XObjectFactoryDefault::create(theManager)),
	m_xpathFactory(theManager, XPathFactoryDefault::createXPathFactoryDefault(theManager)),
	m_constructionContext(theManager, XPathConstructionContextDefault::create(theManager)),
	m_executionContext(theManager, XPathExecutionContextDefault::create(theManager)),
    m_memoryManager(theManager)

{
}



XPathEvaluator::~XPathEvaluator()
{
	// Just in case these are still set, reset them
	// to 0.
	m_executionContext->setXPathEnvSupport(0);

	m_executionContext->setXObjectFactory(0);

	m_executionContext->setDOMSupport(0);
}



XalanNode*
XPathEvaluator::selectSingleNode(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const XalanElement*		namespaceNode)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpathString,
			namespaceNode));

	const NodeRefListBase&	theNodeList = theResult->nodeset();

	return theNodeList.getLength() == 0 ? 0 : theNodeList.item(0);
}



XalanNode*
XPathEvaluator::selectSingleNode(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const PrefixResolver&	prefixResolver)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpathString,
			prefixResolver));

	const NodeRefListBase&	theNodeList = theResult->nodeset();

	return theNodeList.getLength() == 0 ? 0 : theNodeList.item(0);
}



XalanNode*
XPathEvaluator::selectSingleNode(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const XalanElement*		namespaceNode)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpath,
			namespaceNode));

	const NodeRefListBase&	theNodeList = theResult->nodeset();

	return theNodeList.getLength() == 0 ? 0 : theNodeList.item(0);
}



XalanNode*
XPathEvaluator::selectSingleNode(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const PrefixResolver&	prefixResolver)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpath,
			prefixResolver));

	const NodeRefListBase&	theNodeList = theResult->nodeset();

	return theNodeList.getLength() == 0 ? 0 : theNodeList.item(0);
}



NodeRefList&
XPathEvaluator::selectNodeList(
            NodeRefList&            result,
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const XalanElement*		namespaceNode)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpathString,
			namespaceNode));

	result = (theResult->nodeset());

    return result;
}



NodeRefList&
XPathEvaluator::selectNodeList(
            NodeRefList&            result,
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const PrefixResolver&	prefixResolver)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpathString,
			prefixResolver));

	result = (theResult->nodeset());

    return result;
}



NodeRefList&
XPathEvaluator::selectNodeList(
            NodeRefList&            result,
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const XalanElement*		namespaceNode)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpath,
			namespaceNode));

    result = theResult->nodeset();

	return result;
}



NodeRefList&
XPathEvaluator::selectNodeList(
            NodeRefList&            result,
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const PrefixResolver&	prefixResolver)
{
	const XObjectPtr	theResult(
		evaluate(
			domSupport,
			contextNode,
			xpath,
			prefixResolver));

    result = theResult->nodeset();

	return result;
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const XalanElement*		namespaceNode)
{
	XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

    const ElementPrefixResolverProxy    thePrefixResolver(
                    namespaceNode,
                    theEnvSupportDefault,
                    domSupport,
                    m_memoryManager);

	return evaluate(
			domSupport,
			contextNode,
			xpathString,
			thePrefixResolver,
			theEnvSupportDefault);
 
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const XalanElement*		namespaceNode)
{
	XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

    const ElementPrefixResolverProxy    thePrefixResolver(
                    namespaceNode,
                    theEnvSupportDefault,
                    domSupport,
                    m_memoryManager);

	return evaluate(
				domSupport,
				contextNode,
				xpath,
				thePrefixResolver,
				theEnvSupportDefault);
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const PrefixResolver&	prefixResolver)
{
	XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

	return	evaluate(
			domSupport,
			contextNode,
			xpathString,
			prefixResolver,
			theEnvSupportDefault);
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const PrefixResolver&	prefixResolver)
{
	XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

	return evaluate(
				domSupport,
				contextNode,
				xpath,
				prefixResolver,
				theEnvSupportDefault);
}



XPath*
XPathEvaluator::createXPath(const XalanDOMChar*		xpathString)
{
	DOMSupportDefault		theDOMSupport(m_memoryManager);

    XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

    const ElementPrefixResolverProxy    thePrefixResolver(
                    0,
                    theEnvSupportDefault,
                    theDOMSupport,
                    m_memoryManager);

	return createXPath(xpathString, thePrefixResolver);
}



XPath*
XPathEvaluator::createXPath(
			const XalanDOMChar*		xpathString,
			DOMSupport&				domSupport,
			const XalanElement*		namespaceNode)
{
	XPathEnvSupportDefault	theEnvSupportDefault(m_memoryManager);

    const ElementPrefixResolverProxy    thePrefixResolver(
                    namespaceNode,
                    theEnvSupportDefault,
                    domSupport,
                    m_memoryManager);

	return createXPath(xpathString, thePrefixResolver);
}



typedef XPathConstructionContext::GetAndReleaseCachedString     GetAndReleaseCachedString;



XPath*
XPathEvaluator::createXPath(
			const XalanDOMChar*		xpathString,
			const PrefixResolver&	prefixResolver)
{
	XPath* const	theXPath = m_xpathFactory->create();
	assert(theXPath != 0);

	XPathProcessorImpl		theProcessor(m_memoryManager);

    GetAndReleaseCachedString   theGuard(*m_constructionContext.get());

    XalanDOMString&     theTempString = theGuard.get();

    theTempString = xpathString;

    theProcessor.initXPath(
			*theXPath,
			*m_constructionContext.get(),
			theTempString,
			prefixResolver);

	return theXPath;
}



bool
XPathEvaluator::destroyXPath(XPath*		theXPath)
{
	assert(theXPath != 0);

	const bool	theResult = m_xpathFactory->returnObject(theXPath);

	if (m_xpathFactory->getInstanceCount() == 0)
	{
		m_constructionContext->reset();
	}

	return theResult;
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XalanDOMChar*		xpathString,
			const PrefixResolver&	prefixResolver,
			XPathEnvSupport&		envSupport)
{
	assert(contextNode != 0);
	assert(xpathString != 0);

	// Create an XPath, and an XPathProcessorImpl to process
	// the XPath.
	XPath				theXPath(m_memoryManager);

	XPathProcessorImpl  theProcessor(m_memoryManager);

    GetAndReleaseCachedString   theGuard(*m_constructionContext.get());

    XalanDOMString&     theTempString = theGuard.get();

    theTempString = xpathString;

    theProcessor.initXPath(
			theXPath,
			*m_constructionContext.get(),
			theTempString,
			prefixResolver);

	return evaluate(
                domSupport,
                contextNode,
                theXPath,
                prefixResolver,
                envSupport);
}



XObjectPtr
XPathEvaluator::evaluate(
			DOMSupport&				domSupport,
			XalanNode*				contextNode,
			const XPath&			xpath,
			const PrefixResolver&	prefixResolver,
			XPathEnvSupport&		envSupport)
{
	// Reset these, in case we've been here before...
	m_executionContext->reset();
	m_xobjectFactory->reset();

	// Set up the connections between the execution context and
	// the provided support objects...
	m_executionContext->setXPathEnvSupport(&envSupport);

	m_executionContext->setXObjectFactory(m_xobjectFactory.get());

	m_executionContext->setDOMSupport(&domSupport);

	// OK, evaluate the expression...
	const XObjectPtr	theResult(
		xpath.execute(
			contextNode,
			prefixResolver,
			*m_executionContext.get()));

	// Break the connectons we set...
	m_executionContext->setXPathEnvSupport(0);

	m_executionContext->setXObjectFactory(0);

	m_executionContext->setDOMSupport(0);

	return theResult;
}



XALAN_CPP_NAMESPACE_END
