From 66032f772aec1583f419419addf9fe241ae02bca Mon Sep 17 00:00:00 2001 From: randomdan 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 : ../libmisc//misc libxmlpp + 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 +#include +#include +#include +#include "../libmisc/curlsup.h" + +ElementLoaderImpl 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(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(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(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(new Glib::ustring(boost::lexical_cast(xpathObjI->floatval))); + } + else if (xpathObjI->stringval) { + values[xp.first] = boost::shared_ptr(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(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(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 +#include +#include +#include +#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 XPaths; + + FilterView(const xmlpp::Element * p); + + const Glib::ustring name; + const Variable root; + XPaths xpaths; + }; + typedef boost::intrusive_ptr FilterViewPtr; + typedef std::map FilterViews; + FilterViews fvs; + FilterViewPtr fv; + + typedef std::map Namespaces; + mutable Namespaces namespaces; + typedef std::map > Values; + mutable Values values; + typedef std::map > 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