diff options
Diffstat (limited to 'project2/xslRows.cpp')
-rw-r--r-- | project2/xslRows.cpp | 199 |
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; +} + |