From 8214cd0ed198b4a5612d13799fbde3e90cd7d922 Mon Sep 17 00:00:00 2001
From: randomdan <randomdan@localhost>
Date: Mon, 31 Jan 2011 10:18:58 +0000
Subject: Call curl global cleanup when finished Don't bind filters on load
 complete, there might be multiple different users Add XSL rows Fix several
 problems with dump (bin me, console app should support views)

---
 project2/Jamfile.jam      |   3 +-
 project2/dumpTask.cpp     |  10 +--
 project2/exceptions.h     |   1 +
 project2/iterate.cpp      |   3 +
 project2/rowProcessor.cpp |   3 -
 project2/rowView.cpp      |   3 +
 project2/xslRows.cpp      | 199 ++++++++++++++++++++++++++++++++++++++++++++++
 project2/xslRows.h        |  57 +++++++++++++
 8 files changed, 269 insertions(+), 10 deletions(-)
 create mode 100644 project2/xslRows.cpp
 create mode 100644 project2/xslRows.h

diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam
index cdffad9..0bec35a 100644
--- a/project2/Jamfile.jam
+++ b/project2/Jamfile.jam
@@ -40,10 +40,11 @@ lib p2common :
 	;
 
 lib p2xml :
-	xmlRows.cpp
+	xmlRows.cpp xslRows.cpp
 	:
 	<library>../libmisc//misc
 	<library>libxmlpp
+	<library>libxslt
 	;
 
 lib p2processes :
diff --git a/project2/dumpTask.cpp b/project2/dumpTask.cpp
index 0d485f8..caf3c86 100644
--- a/project2/dumpTask.cpp
+++ b/project2/dumpTask.cpp
@@ -24,17 +24,15 @@ DumpTask::loadComplete(const CommonObjects *)
 void
 DumpTask::execute() const
 {
-	const RowSet::RowValuesStack::value_type & r = RowSet::Stack().back();
+	const RowSet::RowValuesStack::value_type & r = *++RowSet::Stack().rbegin();
 	unsigned int cols = r->columnCount();
 	for (unsigned int c = 0; c < cols; c += 1) {
 		if (c > 0) {
 			fprintf(stderr, ", ");
 		}
-		try {
-			fprintf(stderr, "%s = '%s'", r->getColumnName(c).c_str(), r->getCurrentValue(c).c_str());
-		}
-		catch (const RowSet::FieldDoesNotExist &) {
-			fprintf(stderr, "%s = null", r->getColumnName(c).c_str());
+		fprintf(stderr, "%s = ", r->getColumnName(c).c_str());
+		if (!r->isNull(c)) {
+			fprintf(stderr, "'%s'", r->getCurrentValue(c).c_str());
 		}
 	}
 	fprintf(stderr, "\n");
diff --git a/project2/exceptions.h b/project2/exceptions.h
index 84c8c19..d156636 100644
--- a/project2/exceptions.h
+++ b/project2/exceptions.h
@@ -11,6 +11,7 @@ class NotSupported : public std::runtime_error {
 };
 class FileNotReadable : public std::exception { };
 class FileNotWritable : public std::exception { };
+class FilterNotFound : public std::exception { };
 
 #endif
 
diff --git a/project2/iterate.cpp b/project2/iterate.cpp
index 9e5c7f7..f4c8d81 100644
--- a/project2/iterate.cpp
+++ b/project2/iterate.cpp
@@ -35,6 +35,9 @@ Iterate::rowReady() const
 void
 Iterate::execute() const
 {
+	if (!filter.empty()) {
+		source->setFilter(filter);
+	}
 	RowSet::beginRow(source.get());
 	try {
 		source->execute(this);
diff --git a/project2/rowProcessor.cpp b/project2/rowProcessor.cpp
index 26275a9..71d2548 100644
--- a/project2/rowProcessor.cpp
+++ b/project2/rowProcessor.cpp
@@ -16,8 +16,5 @@ void
 RowProcessor::loadComplete(const CommonObjects * co)
 {
 	source = co->getSource(recordSource);
-	if (!filter.empty()) {
-		source->setFilter(filter);
-	}
 }
 
diff --git a/project2/rowView.cpp b/project2/rowView.cpp
index e0cca9c..10fece5 100644
--- a/project2/rowView.cpp
+++ b/project2/rowView.cpp
@@ -59,6 +59,9 @@ RowView::rowReady() const
 void
 RowView::execute(const Presenter * p) const
 {
+	if (!filter.empty()) {
+		source->setFilter(filter);
+	}
 	presenter = p;
 	presenter->pushSub(rootName);
 	RowSet::beginRow(source.get());
diff --git a/project2/xslRows.cpp b/project2/xslRows.cpp
new file mode 100644
index 0000000..696edb9
--- /dev/null
+++ b/project2/xslRows.cpp
@@ -0,0 +1,199 @@
+#include "xslRows.h"
+#include "rowProcessor.h"
+#include "xml.h"
+#include "exceptions.h"
+#include "xmlObjectLoader.h"
+#include <boost/lexical_cast.hpp>
+#include <libxml/HTMLparser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "../libmisc/curlsup.h"
+
+ElementLoaderImpl<XslRows> xslrowLoader("xslrows");
+
+class XmlParseError : public std::exception { };
+class XpathInitError : public std::exception { };
+class XpathEvalError : public std::exception { };
+class ResourceDownloadError : public std::exception { };
+
+XslRows::XslRows(const xmlpp::Element * p) :
+	SourceObject(p),
+	RowSet(p),
+	url(p->get_attribute_value("url")),
+	html(p->get_attribute_value("html") == "true"),
+	warnings(p->get_attribute_value("warnings") != "false")
+{
+	BOOST_FOREACH(const xmlpp::Node * node, p->find("filterview")) {
+		const xmlpp::Element * elem = dynamic_cast<const xmlpp::Element *>(node);
+		if (elem) {
+			FilterViewPtr fv = new FilterView(elem);
+			fvs[fv->name] = fv;
+		}
+	}
+	BOOST_FOREACH(const xmlpp::Node * node, p->find("namespace")) {
+		const xmlpp::Element * elem = dynamic_cast<const xmlpp::Element *>(node);
+		if (elem) {
+			namespaces[elem->get_attribute_value("prefix")] = elem->get_attribute_value("url");
+		}
+	}
+}
+
+XslRows::~XslRows()
+{
+}
+
+void
+XslRows::loadComplete(const CommonObjects *)
+{
+}
+
+void
+XslRows::setFilter(const Glib::ustring & f)
+{
+	FilterViews::const_iterator i = fvs.find(f);
+	if (i == fvs.end()) {
+		throw FilterNotFound();
+	}
+	fv = i->second;
+}
+
+size_t
+XslRows::handleDataHelper(const char * ptr, size_t size, size_t nmemb, void *stream)
+{
+	std::string * buf = static_cast<std::string *>(stream);
+	buf->append(ptr, size * nmemb);
+	return size * nmemb;
+}
+
+xmlDocPtr
+XslRows::getDocument(const Glib::ustring & url) const
+{
+	Documents::const_iterator i = documents.find(url);
+	if (i == documents.end()) {
+		CurlHandle::Ptr c = new CurlHandle();
+		c->setopt(CURLOPT_URL, url.c_str());
+		c->setopt(CURLOPT_FOLLOWLOCATION, 1);
+		c->setopt(CURLOPT_ENCODING, "deflate, gzip");
+		c->setopt(CURLOPT_USERAGENT, "project2/0.3");
+		std::string buf;
+		c->setopt(CURLOPT_WRITEDATA, &buf);
+		c->setopt(CURLOPT_WRITEFUNCTION, &handleDataHelper);
+		if (c->perform()) {
+			throw ResourceDownloadError();
+		}
+
+		int flags = 0;
+		flags |= warnings ? 0 : XML_PARSE_NOWARNING | XML_PARSE_NOERROR;
+		xmlDocPtr doc = html ?
+			htmlReadMemory(buf.c_str(), buf.length(), url.c_str(), NULL, flags) :
+			xmlReadMemory(buf.c_str(), buf.length(), url.c_str(), NULL, flags);
+		if (!doc) {
+			throw XmlParseError();
+		}
+		documents.insert(Documents::value_type(url, Documents::value_type::second_type(doc, xmlFreeDoc)));
+		return doc;
+	}
+	else {
+		return i->second.get();
+	}
+}
+
+void
+XslRows::execute(const RowProcessor * rp) const
+{
+	xmlDocPtr doc = getDocument(url());
+	xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+	if (!xpathCtx) {
+		throw XpathInitError();
+	}
+	BOOST_FOREACH(const Namespaces::value_type & ns, namespaces) {
+		xmlXPathRegisterNs(xpathCtx, BAD_CAST ns.first.c_str(), BAD_CAST ns.second.c_str());
+	}
+	xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(BAD_CAST fv->root().c_str(), xpathCtx);
+	if (!xpathObj || !xpathObj->nodesetval) {
+		xmlXPathFreeContext(xpathCtx);
+		throw XpathEvalError();
+	}
+	rowNum = 1;
+	for (int row = 0; row < xpathObj->nodesetval->nodeNr; row += 1) {
+		xmlNodePtr rowRoot = xpathObj->nodesetval->nodeTab[row];
+		xpathCtx->node = rowRoot;
+		values.clear();
+		BOOST_FOREACH(const FilterView::XPaths::value_type & xp, fv->xpaths) {
+			xmlXPathObjectPtr xpathObjI = xmlXPathEvalExpression(BAD_CAST xp.second().c_str(), xpathCtx);
+			if (!xpathObjI) {
+				xmlXPathFreeObject(xpathObj);
+				xmlXPathFreeContext(xpathCtx);
+				throw XpathEvalError();
+			}
+			if (xpathObjI->floatval) {
+				values[xp.first] = boost::shared_ptr<const Glib::ustring>(new Glib::ustring(boost::lexical_cast<Glib::ustring>(xpathObjI->floatval)));
+			}
+			else if (xpathObjI->stringval) {
+				values[xp.first] = boost::shared_ptr<const Glib::ustring>(new Glib::ustring((const char *)xpathObjI->stringval));
+			}
+			else if (xpathObjI->nodesetval && xpathObjI->nodesetval->nodeNr == 1) {
+				if (xpathObjI->nodesetval->nodeTab[0]->children && xpathObjI->nodesetval->nodeTab[0]->children->content) {
+					xmlChar * val = xpathObjI->nodesetval->nodeTab[0]->children->content;
+					values[xp.first] = boost::shared_ptr<const Glib::ustring>(new Glib::ustring((const char *)val));
+				}
+			}
+			xmlXPathFreeObject(xpathObjI);
+		}
+		rp->rowReady();
+		rowNum += 1;
+	}
+	xmlXPathFreeObject(xpathObj);
+	xmlXPathFreeContext(xpathCtx);
+}
+
+XslRows::FilterView::FilterView(const xmlpp::Element * p) :
+	name(p->get_attribute_value("name")),
+	root(p->get_attribute_value("root"))
+{
+	BOOST_FOREACH(const xmlpp::Node * node, p->find("field")) {
+		const xmlpp::Element * elem = dynamic_cast<const xmlpp::Element *>(node);
+		if (elem) {
+			xpaths.insert(XPaths::value_type(elem->get_attribute_value("name"), elem->get_attribute_value("xpath")));
+		}
+	}
+}
+
+const Glib::ustring &
+XslRows::getCurrentValue(const Glib::ustring & id) const
+{
+	return *values.find(id)->second;
+}
+
+const Glib::ustring &
+XslRows::getCurrentValue(unsigned int col) const
+{
+	return getCurrentValue(getColumnName(col));
+}
+
+bool
+XslRows::isNull(unsigned int col) const
+{
+	return isNull(getColumnName(col));
+}
+
+bool
+XslRows::isNull(const Glib::ustring & col) const
+{
+	return (values.find(col) == values.end());
+}
+
+unsigned int
+XslRows::columnCount() const
+{
+	return fv->xpaths.size();
+}
+
+const Glib::ustring &
+XslRows::getColumnName(unsigned int col) const
+{
+	FilterView::XPaths::const_iterator i = fv->xpaths.begin();
+	while (col--) i++;
+	return i->first;
+}
+
diff --git a/project2/xslRows.h b/project2/xslRows.h
new file mode 100644
index 0000000..0cc92c7
--- /dev/null
+++ b/project2/xslRows.h
@@ -0,0 +1,57 @@
+#ifndef XSLROWS_H
+#define XSLROWS_H
+
+#include <libxml++/nodes/element.h>
+#include <libxml/tree.h>
+#include <boost/intrusive_ptr.hpp>
+#include <map>
+#include "rowSet.h"
+
+class XslRows : public RowSet {
+	public:
+
+		XslRows(const xmlpp::Element * p);
+		~XslRows();
+
+		void execute(const RowProcessor *) const;
+		virtual void loadComplete(const CommonObjects *);
+		virtual void setFilter(const Glib::ustring &);
+		unsigned int columnCount() const;
+		const Glib::ustring & getColumnName(unsigned int col) const;
+		const Glib::ustring & getCurrentValue(const Glib::ustring & id) const;
+		const Glib::ustring & getCurrentValue(unsigned int col) const;
+		bool isNull(unsigned int col) const;
+		bool isNull(const Glib::ustring & id) const;
+
+		const Variable url;
+		const bool html;
+		const bool warnings;
+
+	private:
+		class FilterView : public virtual IntrusivePtrBase {
+			public:
+				typedef std::map<const Glib::ustring, Variable> XPaths;
+
+				FilterView(const xmlpp::Element * p);
+
+				const Glib::ustring name;
+				const Variable root;
+				XPaths xpaths;
+		};
+		typedef boost::intrusive_ptr<FilterView> FilterViewPtr;
+		typedef std::map<const Glib::ustring, FilterViewPtr> FilterViews;
+		FilterViews fvs;
+		FilterViewPtr fv;
+
+		typedef std::map<const Glib::ustring, Glib::ustring> Namespaces;
+		mutable Namespaces namespaces;
+		typedef std::map<const Glib::ustring, boost::shared_ptr<const Glib::ustring> > Values;
+		mutable Values values;
+		typedef std::map<const Glib::ustring, boost::shared_ptr<xmlDoc> > Documents;
+		mutable Documents documents;
+		static size_t handleDataHelper(const char * ptr, size_t size, size_t nmemb, void *stream);
+		xmlDocPtr getDocument(const Glib::ustring & url) const;
+};
+
+#endif
+
-- 
cgit v1.2.3