#include "variables.h" #include "exceptions.h" #include "appEngine.h" #include "session.h" #include "rowUser.h" #include "genericVisitor.h" #include #include #include #include #include #include #include #include class UnknownVariableType : public std::exception { }; class UnknownVariableSource : public std::exception { }; class NoVariableDefinition : public std::exception { }; static VariableType makeVariableType(const Glib::ustring & src, const std::string & type, const std::string & format = std::string()) { if (type == "stringptr" || type.empty()) return boost::shared_ptr(new Glib::ustring(src)); if (type == "string") return src; if (type == "int") return boost::lexical_cast(src); if (type == "uint") return boost::lexical_cast(src); if (type == "lint") return boost::lexical_cast(src); if (type == "luint") return boost::lexical_cast(src); if (type == "llint") return boost::lexical_cast(src); if (type == "lluint") return boost::lexical_cast(src); if (type == "float") return boost::lexical_cast(src); if (type == "double") return boost::lexical_cast(src); if (type == "datetime") return boost::posix_time::time_from_string(src); if (type == "datetimeptr") return boost::shared_ptr(new boost::posix_time::ptime(boost::posix_time::time_from_string(src))); throw UnknownVariableType(); } class VariableLiteral : public VariableImpl { public: VariableLiteral(const Glib::ustring & src, const std::string & type = std::string()) : val(makeVariableType(src, type)) { } virtual const VariableType & value() const { return val; } private: VariableType val; }; class VariableImplDyn : public VariableImpl { public: VariableImplDyn(const xmlpp::Element * e) : cacheValid(false) { try { defaultValue = Variable(e, "default"); } catch (NoVariableDefinition) { // That's cool... no default } } virtual const VariableType & value() const = 0; protected: mutable VariableType cache; mutable bool cacheValid; boost::optional defaultValue; }; class VariableSession : public VariableImplDyn { public: VariableSession(const xmlpp::Element * e) : VariableImplDyn(e), name(e->get_attribute_value("name")) { } const VariableType & value() const { try { cache = ApplicationEngine::getCurrent()->session()->GetValue(name); } catch (Session::VariableNotFound) { if (!defaultValue) { throw; } cache = (*defaultValue)(); } return cache; } private: const Glib::ustring name; }; class VariableParam : public VariableImplDyn { public: VariableParam(const xmlpp::Element * e) : VariableImplDyn(e), name(e->get_attribute_value("name")) { } const VariableType & value() const { if (!cacheValid) { try { cache = ApplicationEngine::getCurrent()->env()->getParamQuery(name); } catch (ParamNotFound) { if (!defaultValue) { throw; } cache = (*defaultValue)(); } cacheValid = true; } return cache; } private: const Glib::ustring name; }; class VariableUri : public VariableImplDyn { public: VariableUri(const xmlpp::Element * e) : VariableImplDyn(e), index(atoi(e->get_attribute_value("index").c_str())) { } const VariableType & value() const { if (!cacheValid) { try { cache = ApplicationEngine::getCurrent()->env()->getParamUri(index); } catch (UriElementOutOfRange) { if (!defaultValue) { throw; } cache = (*defaultValue)(); } cacheValid = true; } return cache; } private: unsigned int index; }; static void assignHelper(VariableType & dest, const VariableType & src) { dest = src; } class VariableParent : public VariableImplDyn, public RowUser { public: VariableParent(const xmlpp::Element * e, const RowUser * d) : VariableImplDyn(e), row(NULL), depth(atoi(e->get_attribute_value("depth").c_str())), attr(e->get_attribute("attribute")), name(attr ? e->get_attribute_value("attribute") : e->get_attribute_value("name")), dep(d) { } VariableParent(const Glib::ustring & n, bool a, unsigned int d) : VariableImplDyn(NULL), row(NULL), depth(d), attr(a), name(n), dep(NULL) { } ~VariableParent() { if (row) { row->finish(this); if (dep) { row->finish(dep); } } } const VariableType & value() const { if (!cacheValid) { try { if (!row) { RowSet::RowValuesStack::const_reverse_iterator r = RowSet::Stack().rbegin(); for (size_t p = depth; p--; r++) ; if (r == RowSet::Stack().rend()) { throw RowSet::ParentOutOfRange(); } row = *r; row->use(this); if (dep) { row->use(dep); } bind(); } getValue(cache, row); } catch (RowSet::ParentOutOfRange) { if (!defaultValue) { throw; } cache = (*defaultValue)(); } catch (RowSet::FieldDoesNotExist) { if (!defaultValue) { throw; } cache = (*defaultValue)(); } cacheValid = true; } return cache; } void rowChanged() const { cacheValid = false; } protected: void bind() const { if (attr) { getValue = boost::bind(&assignHelper, _1, boost::bind(row->resolveAttr(name))); } else { typedef VariableType (RowSet::*gCV)(const Glib::ustring &) const; getValue = boost::bind(&assignHelper, _1, boost::bind((gCV)&RowSet::getCurrentValue, _2, name)); } } mutable ConstRowSetPtr row; const size_t depth; const bool attr; const Glib::ustring name; const RowUser * dep; mutable boost::function2 getValue; }; class VariableFixed : public VariableImpl { public: VariableFixed(VariableType v) : var(v) { } const VariableType & value() const { return var; } private: VariableType var; }; Variable::Variable(const xmlpp::Element * e, const Glib::ustring & n, bool required, VariableType def) { xmlpp::Attribute * a = e->get_attribute(n); if (a) { var = new VariableLiteral(a->get_value()); return; } xmlpp::Element::NodeList cs = e->get_children(n); if (cs.size() == 1) { const xmlpp::Element * c = dynamic_cast(cs.front()); if (c) { Glib::ustring source = c->get_attribute_value("source"); if (source == "session") var = new VariableSession(c); else if (source == "parent") var = new VariableParent(c, NULL); else if (source == "uri") var = new VariableUri(c); else if (source == "param") var = new VariableParam(c); else if (source == "literal" || source.empty()) var = new VariableLiteral(c->get_attribute_value("value"), c->get_attribute_value("type")); else throw UnknownVariableSource(); return; } } if (!required) { var = new VariableFixed(def); return; } throw NoVariableDefinition(); } Variable::Variable(VariableImpl * v) : var(v) { } VariableImpl::~VariableImpl() { } Variable Variable::makeParent(const Glib::ustring & name, bool attr, unsigned int dep) { return Variable(new VariableParent(name, attr, dep)); }