summaryrefslogtreecommitdiff
path: root/project2/xslRows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'project2/xslRows.cpp')
-rw-r--r--project2/xslRows.cpp199
1 files changed, 199 insertions, 0 deletions
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;
+}
+