//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//

static const char revid[] = "$Id: QueryContext.cpp,v 1.35 2003/12/05 12:37:04 merrells Exp $";

#include "dbxml_config.h"
#include "dbxml/XmlPortability.hpp"
#if defined(DBXML_XPATH_PATHAN)
#include <pathan/ext/XPathEvaluatorExt.hpp>
#endif
#include "XPathSelectionTreeParser.hpp"
#include "QueryContext.hpp"
#include "Value.hpp"
#include "OperationContext.hpp"
#include "UTF8.hpp"

#include <set>

#if defined(DBXML_DOM_XERCES2)
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/dom/DOM.hpp>
#if defined(XERCES_HAS_CPP_NAMESPACE)
  XERCES_CPP_NAMESPACE_USE
#endif
#endif

#if defined(DBXML_XPATH_PATHAN)
#include <pathan/XPathEvaluator.hpp>
#endif

typedef std::set<std::string> StringSet ;
StringSet *knownFunctionNames = 0;

QueryContext::QueryContext(XmlQueryContext::ReturnType rt, XmlQueryContext::EvaluationType et)
	: returnType_(rt),
	withMetadata_(true),
	evaluationType_(et),
	xpathSelectionTreeParser_(new XPathSelectionTreeParser),
	domParser_(0),
	xpathEvaluator_(0),
	operationContext_(0),
	tmpDocument_(0)
{
	XMLPlatformUtils::Initialize();
	setNamespace("dbxml", "http://www.sleepycat.com/2002/dbxml");
	if (knownFunctionNames == 0) {
		knownFunctionNames = new StringSet;
		knownFunctionNames->insert("last");
		knownFunctionNames->insert("position");
		knownFunctionNames->insert("count");
		knownFunctionNames->insert("id");
		knownFunctionNames->insert("local-name");
		knownFunctionNames->insert("namespace-uri");
		knownFunctionNames->insert("name");
		knownFunctionNames->insert("string");
		knownFunctionNames->insert("concat");
		knownFunctionNames->insert("starts-with");
		knownFunctionNames->insert("contains");
		knownFunctionNames->insert("substring-before");
		knownFunctionNames->insert("substring-after");
		knownFunctionNames->insert("substring");
		knownFunctionNames->insert("string-length");
		knownFunctionNames->insert("normalize-space");
		knownFunctionNames->insert("translate");
		knownFunctionNames->insert("boolean");
		knownFunctionNames->insert("not");
		knownFunctionNames->insert("true");
		knownFunctionNames->insert("false");
		knownFunctionNames->insert("lang");
		knownFunctionNames->insert("number");
		knownFunctionNames->insert("sum");
		knownFunctionNames->insert("floor");
		knownFunctionNames->insert("ceiling");
		knownFunctionNames->insert("round");
	}
}

QueryContext::~QueryContext()
{
	delete xpathSelectionTreeParser_;
	delete domParser_;
	delete xpathEvaluator_;
	delete operationContext_;
	delete tmpDocument_;
	XMLPlatformUtils::Terminate();
}

void QueryContext::setVariableValue(const std::string &name, const XmlValue &value)
{
	switch (value.getType(0)) {
	case XmlValue::STRING:
	case XmlValue::NUMBER:
	case XmlValue::BOOLEAN:
		break;
	case XmlValue::DOCUMENT:
	case XmlValue::NODE:
	case XmlValue::VARIABLE:
	case XmlValue::NONE:
	default:
		throw XmlException(XmlException::INVALID_VALUE,
				   "setVariableValue expects a typed value. A string, number or boolean. Not a document, node, or variable.");
		break;
	}
	variables_.setVariableValue(name, value);
}

bool QueryContext::getVariableValue(const std::string &name, XmlValue &value) const
{
	return variables_.getVariableValue(name, value);
}

void QueryContext::setNamespace( const std::string &prefix, const std::string &uri )
{
	namespaces_[prefix] = uri;
}

std::string QueryContext::getNamespace( const std::string &prefix )
{
	return namespaces_[prefix];
}

void QueryContext::removeNamespace( const std::string &prefix )
{
	namespaces_[prefix] = "";
}

void QueryContext::clearNamespaces()
{
	namespaces_.clear();
}

bool QueryContext::isFunction(const std::string &name) const
{
	StringSet::const_iterator i = knownFunctionNames->find(name);
	return i != knownFunctionNames->end();
}

void QueryContext::setupXPathQueryContext(XPathEvaluator &evaluator, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *nsDocument, XPathNSResolver **docNSResolver)
{
	// Add the namespace prefix to URI mappings as attributes of the document element.
	DOMElement *nsroot = nsDocument->createElement(UTF8ToXMLCh("root").str());

	NamespaceMap::iterator nsi;
	for (nsi = namespaces_.begin(); nsi != namespaces_.end(); ++nsi) {
		XMLCh *attrURI = XMLString::transcode("http://www.w3.org/2000/xmlns/");
		XMLCh *attrName = XMLString::transcode(("xmlns:" + nsi->first).c_str());
		XMLCh *attrValue = XMLString::transcode(nsi->second.c_str());
		nsroot->setAttributeNS(attrURI, attrName, attrValue);
		XMLString::release(&attrValue);
		XMLString::release(&attrName);
		XMLString::release(&attrURI);
	}

	// Set up variables.  Vanilla Pathan doesn't export this
	// class, but we had do do this in order to pass the variable
	// bindings through the Pathan API.
	//
	XPathEvaluatorExt *evaluatorExt = static_cast<XPathEvaluatorExt *>(&evaluator);
	VariableBindings::Values values = variables_.getValues();
	VariableBindings::Values::iterator vi;
	for (vi = values.begin(); vi != values.end(); ++vi) {
		UTF8ToXMLCh name(vi->first.c_str());
		Value *v = vi->second;
		XMLCh *strval;
		XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *docval;
		switch (v->getType(0)) {
		case XmlValue::STRING:
			strval = XMLString::transcode(v->asString(0).c_str());
			evaluatorExt->setGlobalVar(name.str(), strval);
			XMLString::release(&strval);
			break;
		case XmlValue::NUMBER:
			evaluatorExt->setGlobalVar(name.str(), v->asNumber(0));
			break;
		case XmlValue::BOOLEAN:
			evaluatorExt->setGlobalVar(name.str(), v->asBoolean(0));
			break;
		case XmlValue::DOCUMENT:
			docval = v->asDocument(0).getDOM(false);
			evaluatorExt->setGlobalVar(name.str(), docval);
			delete docval;
			break;
		case XmlValue::NODE:
			// JCM - evaluatorExt->setGlobalVar(name.str(), v->asNode(0));
			break;
		}
	}

	// Create an XPathNSResolver using the XPathEvaluator factory class
	// The XPathNSResolver class binds XML namespace prefixes to the
	// full namespace string.
	//
	*docNSResolver = evaluator.createNSResolver(nsroot);
}

XercesDOMParser &QueryContext::getDOMParser()
{
	if (domParser_ == 0) {
		domParser_ = new XercesDOMParser;
		domParser_->useScanner(XMLUni::fgWFXMLScanner);
	}
	return *domParser_;
}

XPathEvaluator &QueryContext::getXPathEvaluator()
{
	if (xpathEvaluator_ == 0) {
		xpathEvaluator_ = XPathEvaluator::createEvaluator();
	}
	return *xpathEvaluator_;
}

OperationContext &QueryContext::getOperationContext()
{
	if (operationContext_ == 0) {
		operationContext_ = new OperationContext();
	}
	return *operationContext_;
}

void QueryContext::setTransaction(DbTxn *txn)
{
	getOperationContext().set(txn);
}

XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument &QueryContext::getTemporaryDocument()
{
	if (tmpDocument_ == 0) {
		DOMImplementation *factory = DOMImplementation::getImplementation();
		tmpDocument_ = factory->createDocument();
	}
	return *tmpDocument_;
}
