/ .. / / -> download
#include <string>
#include <libxml/xpathInternals.h>
#include "SaxonProcessor.h"
#include "XdmValue.h"
#include "XdmItem.h"
#include "XdmNode.h"
#include "XdmAtomicValue.h"
#include "saxon.h"

/* Return the string value of the first node matched by an XPath expression. */
static char *first_xpath_value(SaxonProcessor *processor, XdmNode *node, const char *expr)
{
	XPathProcessor *xpath_proc;
	XdmValue *res;
	char *ret;

	xpath_proc = processor->newXPathProcessor();
	xpath_proc->setContextItem(node);

	res = xpath_proc->evaluateSingle(expr);

	if (res == NULL) {
		ret = NULL;
	} else {
		ret = strdup(res->toString());
	}

	delete res;
	delete xpath_proc;

	return ret;
}

/* Generate an XPath expression to an XdmNode. */
static xmlChar *xpath_of(XdmNode *node, SaxonProcessor *processor)
{
	xmlNodePtr path, cur;
	xmlChar *dst = NULL;

	/* FIXME: Not sure how to handle node types besides XDM_ATOMIC_VALUE
	 *        and XDM_NODE, so just returning an XPath expression to the
	 *        root of the document. */
	if (node->getType() != XDM_NODE) {
		return xmlCharStrdup("/*");
	}

	path = xmlNewNode(NULL, BAD_CAST "path");

	/* Build XPath expression node by traversing up the tree. */
	while (node) {
		xmlNodePtr e;
		const xmlChar *name;
		XdmNode *parent;
		XDM_NODE_KIND node_kind;
		char *node_ns, *node_name;

		node_kind = node->getNodeKind();

		if (node_kind == DOCUMENT) {
			break;
		}

		parent = node->getParent();

		/* Get namespace prefix of node. */
		node_ns = first_xpath_value(processor, node, "substring-before(name(), ':')");

		/* Get local name of node. */
		node_name = first_xpath_value(processor, node, "local-name()");

		e = xmlNewChild(path, NULL, BAD_CAST "node", NULL);

		if (node_ns != NULL && strcmp(node_ns, "") != 0) {
			xmlSetProp(e, BAD_CAST "ns", BAD_CAST node_ns);
		}

		switch (node_kind) {
			case COMMENT:
				name = BAD_CAST "comment()";
				break;
			case PROCESSING_INSTRUCTION:
				name = BAD_CAST "processing-instruction()";
				break;
			case TEXT:
				name = BAD_CAST "text()";
				break;
			default:
				name = BAD_CAST node_name;
				break;
		}

		xmlSetProp(e, BAD_CAST "name", name);

		/* Locate the node's position within its parent. */
		if (node_kind != ATTRIBUTE) {
			XPathProcessor *xpath_proc;
			XdmValue *preceding;
			const char *preceding_xpath;

			xpath_proc = processor->newXPathProcessor();
			xpath_proc->setContextItem(node);

			if (node_kind == COMMENT) {
				preceding_xpath = "preceding-sibling::comment()";
			} else if (node_kind == PROCESSING_INSTRUCTION) {
				preceding_xpath = "preceding-sibling::processing-instruction()";
			} else if (node_kind == TEXT) {
				preceding_xpath = "preceding-sibling::text()";
			} else {
				XdmAtomicValue *ns_val   = processor->makeStringValue(node_ns);
				XdmAtomicValue *name_val = processor->makeStringValue(node_name);
				xpath_proc->setParameter("ns", ns_val);
				xpath_proc->setParameter("name", name_val);
				preceding_xpath = "preceding-sibling::*[namespace-uri()=$ns and local-name()=$name]";
			}

			preceding = xpath_proc->evaluate(preceding_xpath);

			if (preceding) {
				xmlChar pos[16];
				xmlStrPrintf(pos, 16, "%d", preceding->size() + 1);
				xmlSetProp(e, BAD_CAST "pos", pos);
			} else{
				xmlSetProp(e, BAD_CAST "pos", BAD_CAST "1");
			}

			delete preceding;
			delete xpath_proc;
		}

		node = parent;

		free(node_ns);
		free(node_name);
	}

	/* Convert XPath expression node to string. */
	for (cur = path->last; cur; cur = cur->prev) {
		xmlChar *ns, *name, *pos;

		ns   = xmlGetProp(cur, BAD_CAST "ns");
		name = xmlGetProp(cur, BAD_CAST "name");
		pos  = xmlGetProp(cur, BAD_CAST "pos");

		dst = xmlStrcat(dst, BAD_CAST "/");
		if (!pos) {
			dst = xmlStrcat(dst, BAD_CAST "@");
		}
		if (ns) {
			dst = xmlStrcat(dst, ns);
			dst = xmlStrcat(dst, BAD_CAST ":");
		}
		dst = xmlStrcat(dst, name);
		if (pos) {
			dst = xmlStrcat(dst, BAD_CAST "[");
			dst = xmlStrcat(dst, pos);
			dst = xmlStrcat(dst, BAD_CAST "]");
		}

		xmlFree(ns);
		xmlFree(name);
		xmlFree(pos);
	}

	xmlFreeNode(path);

	return dst;
}

/* Report version of Saxon/C. */
extern "C" const char *saxon_version(void *saxon_processor)
{
	return ((SaxonProcessor *) saxon_processor)->version();
}

/* Create new Saxon processor. */
extern "C" void *saxon_new_processor(void)
{
	return new SaxonProcessor(false);
}

/* Free Saxon processor. */
extern "C" void saxon_free_processor(void *saxon_processor)
{
	SaxonProcessor *p = (SaxonProcessor *) saxon_processor;
	delete p;
}

/* Cleanup Saxon JVM. */
extern "C" void saxon_cleanup(void)
{
	SaxonProcessor::release();
}

/* Create new Saxon XPath processor. */
extern "C" void *saxon_new_xpath_processor(void *saxon_processor)
{
	SaxonProcessor *p = (SaxonProcessor *) saxon_processor;
	return p->newXPathProcessor();
}

/* Free Saxon XPath processor. */
extern "C" void saxon_free_xpath_processor(void *xpath_processor)
{
	XPathProcessor *p = (XPathProcessor *) xpath_processor;
	delete p;
}

/* Register a namespace prefix in a Saxon XPath processor. */
extern "C" void saxon_register_namespace(void *xpath_processor, const xmlChar *prefix, const xmlChar *href)
{
	((XPathProcessor *) xpath_processor)->declareNamespace((const char *) prefix, (const char *) href);
}

/* Create a Saxon XdmNode from a libxml2 doc. */
extern "C" void *saxon_new_node(void *saxon_processor, xmlDocPtr doc)
{
	SaxonProcessor *p = (SaxonProcessor *) saxon_processor;
	xmlChar *xml;
	int size;
	XdmNode *node;

	/* Pass libxml2 doc to Saxon processor as string. */
	xmlDocDumpMemory(doc, &xml, &size);
	node = p->parseXmlFromString((const char *) xml);
	xmlFree(xml);

	return node;
}

/* Free a Saxon XdmNode. */
extern "C" void saxon_free_node(void *saxon_node)
{
	XdmNode *node = (XdmNode *) saxon_node;

	delete node;
}

/* Use Saxon to evaluate an XPath expression in a libxml2 XPath context,
 * returning a libxml2 nodeset. */
extern "C" xmlXPathObjectPtr saxon_eval_xpath(void *saxon_processor, void *xpath_processor, void *saxon_node, const xmlChar *expr, xmlXPathContextPtr ctx)
{
	SaxonProcessor *saxon_proc = (SaxonProcessor *) saxon_processor;
	XPathProcessor *xpath_proc = (XPathProcessor *) xpath_processor;
	XdmNode *node = (XdmNode *) saxon_node;
	XdmValue *value;
	xmlXPathObjectPtr obj;

	/* Evaluate the XPath expression. */
	xpath_proc->setContextItem(node);
	value = xpath_proc->evaluate((const char *) expr);

	if (value) {
		XDM_TYPE type = value->getType();

		/* Create XPath object with boolean interpretation of atomic value. */
		if (type == XDM_ATOMIC_VALUE) {
			obj = xmlXPathNewBoolean(((XdmAtomicValue *) value)->getBooleanValue());
		/* Create XPath object with a nodeset of the matched nodes. */
		} else {
			xmlNodeSetPtr nodeset;
			int count, i;

			/* Create an empty libxml2 nodeset. */
			nodeset = xmlXPathNodeSetCreate(NULL);

			/* For each Saxon node, construct an XPath 1.0 expression to it
			 * in the document, and use that to add the libxml2 node to the
			 * nodeset. */
			for (i = 0, count = value->size(); i < count; ++i) {
				XdmNode *n = (XdmNode *) value->itemAt(i);
				xmlChar *xpath = xpath_of(n, saxon_proc);
				xmlXPathObjectPtr o = xmlXPathEval(xpath, ctx);

				if (o && !xmlXPathNodeSetIsEmpty(o->nodesetval)) {
					xmlXPathNodeSetAdd(nodeset, o->nodesetval->nodeTab[0]);
				}

				xmlFree(xpath);
				xmlXPathFreeObject(o);
			}

			/* Wrap the nodeset in a libxml2 XPath object. */
			obj = xmlXPathNewNodeSetList(nodeset);
		}
	} else {
		/* Default XPath object if nothing was found. */
		obj = xmlXPathNewBoolean(false);
	}

	delete value;

	return obj;
}


/ gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/s1kd-brexcheck/saxon/saxon.cpp
Styles: Light Dark Classic