/ .. / / -> download
#include <string>
#include <iostream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xqilla/xqilla-dom3.hpp>
#include <libxml/xpathInternals.h>
#include "xqilla.h"

using namespace xercesc;

/* Get an XPath 1.0 expression to a DOMNode. */
static xmlChar *xpath_of(DOMNode *node)
{
	xmlNodePtr path = xmlNewNode(NULL, BAD_CAST "path");
	xmlChar *dst = NULL;

	while (node) {
		DOMNode::NodeType type;
		xmlNodePtr e;

		type = node->getNodeType();

		if (type == DOMNode::DOCUMENT_NODE) {
			break;
		}

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

		/* Get the name of the node. */
		char *name;

		switch (type) {
			case DOMNode::COMMENT_NODE:
				name = strdup("comment()");
				break;
			case DOMNode::PROCESSING_INSTRUCTION_NODE:
				name = strdup("processing-instruction()");
				break;
			case DOMNode::TEXT_NODE:
				name = strdup("text()");
				break;
			default:
				name = XMLString::transcode(node->getNodeName());
				break;
		}

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

		if (type == DOMNode::ATTRIBUTE_NODE) {
			node = ((DOMAttr *) node)->getOwnerElement();
		} else {
			/* Get the position of the node relative to other nodes
			 * with the same name.
			 */
			int n = 1;
			xmlChar pos[16];

			for (DOMNode *cur = node->getFirstChild(); cur; cur = cur->getNextSibling()) {
				if (cur == node) {
					break;
				} else if (cur->getNodeType() == type && (type != DOMNode::ELEMENT_NODE || XMLString::equals(cur->getNodeName(), node->getNodeName()))) {
					++n;
				}
			}

			xmlStrPrintf(pos, 16, "%d", n);
			xmlSetProp(e, BAD_CAST "pos", pos);

			node = node->getParentNode();
		}
	}

	/* Construct the XPath 1.0 expression. */
	for (xmlNodePtr cur = path->last; cur; cur = cur->prev) {
		xmlChar *name = xmlGetProp(cur, BAD_CAST "name");
		xmlChar *pos  = xmlGetProp(cur, BAD_CAST "pos");

		dst = xmlStrcat(dst, BAD_CAST "/");

		if (!pos) {
			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(name);
		xmlFree(pos);
	}

	xmlFreeNode(path);

	return dst;
}

/* Get Xerces and XQilla version information. */
extern "C" const char *xqilla_version(void)
{
	return XERCES_FULLVERSIONDOT;
}

/* Initialize XQilla and Xerces, and return the DOM implementation. */
extern "C" void *xqilla_initialize(void)
{
	XQillaPlatformUtils::initialize();
	return DOMImplementationRegistry::getDOMImplementation(X("XPath2"));
}

/* Cleanup XQilla and Xerces. */
extern "C" void xqilla_terminate(void)
{
	XQillaPlatformUtils::terminate();
}

/* Create a new DOMLSParser. */
extern "C" void *xqilla_create_parser(void *impl)
{
	DOMImplementation *i = (DOMImplementation *) impl;
	DOMLSParser *parser = i->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
	return parser;
}

/* Release a DOMLSParser. */
extern "C" void xqilla_free_parser(void *parser)
{
	DOMLSParser *p = (DOMLSParser *) parser;
	p->release();
}

/* Create a Xerces DOMDocument from a libxml xmlDocPtr. */
extern "C" void *xqilla_create_doc(void *impl, void *parser, xmlDocPtr doc)
{
	DOMImplementation *i = (DOMImplementation *) impl;
	DOMLSParser *p = (DOMLSParser *) parser;
	xmlChar *xml;
	int size;

	/* The DTD in S1000D CSDB objects is often incorrect, which causes
	 * issues with stricter parsers like Xerces, so this strips it off
	 * before parsing. It should not be required for BREX validation.
	 */
	xmlDocPtr doc_no_dtd = xmlNewDoc(BAD_CAST "1.0");
	xmlDocSetRootElement(doc_no_dtd, xmlCopyNode(xmlDocGetRootElement(doc), 1));

	xmlDocDumpMemory(doc_no_dtd, &xml, &size);

	xmlFreeDoc(doc_no_dtd);

	DOMLSInput *input = i->createLSInput();

	XMLCh *stringData = XMLString::transcode((char *) xml);
	input->setStringData(stringData);

	DOMDocument *d = p->parse(input);

	xmlFree(xml);
	delete stringData;
	delete input;

	return d;
}

/* Create a new DOMXPathNSResolver. */
extern "C" void *xqilla_create_ns_resolver(void *doc)
{
	DOMDocument *d = (DOMDocument *) doc;
	DOMXPathNSResolver *resolver = d->createNSResolver(NULL);

	/* Apparently required for default XPath 2.0 functions/types to work? */
	resolver->addNamespaceBinding(X("xs"), X("http://www.w3.org/2001/XMLSchema"));

	return resolver;
}

/* Register a namespace with a DOMXPathNSResolver. */
extern "C" void xqilla_register_namespace(void *resolver, const xmlChar *prefix, const xmlChar *uri)
{
	DOMXPathNSResolver *r = (DOMXPathNSResolver *) resolver;
	r->addNamespaceBinding(X((const char *) prefix), X((const char *) uri));
}

/* Evaluate an XPath 2.0 expression using XQilla and return a libxml nodeset. */
extern "C" xmlXPathObjectPtr xqilla_eval_xpath(void *doc, void *ns_resolver, const xmlChar *expr, xmlXPathContextPtr ctx)
{
	DOMDocument *d = (DOMDocument *) doc;
	DOMXPathNSResolver *n = (DOMXPathNSResolver *) ns_resolver;

	try {
		AutoRelease<DOMXPathExpression> expression(d->createExpression(X((const char *) expr), n));
		AutoRelease<DOMXPathResult> result(expression->evaluate(d, DOMXPathResult::ITERATOR_RESULT_TYPE, 0));
		xmlNodeSetPtr nodeset = xmlXPathNodeSetCreate(NULL);
		xmlXPathObjectPtr obj = NULL;

		while (result->iterateNext()) {
			if (result->isNode()) {
				DOMNode *node = result->getNodeValue();
				xmlChar *xpath = xpath_of(node);
				xmlXPathObjectPtr o = xmlXPathEval(xpath, ctx);

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

				xmlFree(xpath);
				xmlXPathFreeObject(o);

			} else {
				obj = xmlXPathNewBoolean(result->getBooleanValue());
			}
		}

		if (!obj) {
			obj = xmlXPathNewNodeSetList(nodeset);
		}

		xmlXPathFreeNodeSet(nodeset);

		return obj;
	} catch (DOMXPathException e) {
		char *m = XMLString::transcode(e.msg);
		std::cerr << "XQilla: " << m << std::endl;
		delete m;

		return NULL;
	}
}


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