#include "variables.h" #include "xmlObjectLoader.h" #include "exceptions.h" #include "appEngine.h" #include "session.h" #include "rowUser.h" #include "rowSet.h" #include #include #include #include #include #include #include #include SimpleMessageException(UnknownVariableType); SimpleMessageException(UnknownVariableSource); SimpleMessageException(NoVariableDefinition); enum VT_typeID { DefaultType, StringPtr, String, Int, UInt, LInt, LUInt, LLInt, LLUInt, Float, Double, DateTime, DateTimePtr, }; static VT_typeID getVariableTypeFromName(const std::string & src) { if (src.empty()) return String; if (src == "string") return String; if (src == "stringptr") return StringPtr; if (src == "int") return Int; if (src == "uint") return UInt; if (src == "lint") return LInt; if (src == "luint") return LUInt; if (src == "llint") return LLInt; if (src == "lluint") return LLUInt; if (src == "float") return Float; if (src == "double") return Double; if (src == "datetime") return DateTime; if (src == "datetimeptr") return DateTimePtr; throw UnknownVariableType(src); } static VariableType makeVariableType(const Glib::ustring & src, const VT_typeID format = DefaultType) { switch (format) { default: case DefaultType: case StringPtr: return boost::shared_ptr(new Glib::ustring(src)); case String: return src; case Int: return boost::lexical_cast(src); case UInt: return boost::lexical_cast(src); case LInt: return boost::lexical_cast(src); case LUInt: return boost::lexical_cast(src); case LLInt: return boost::lexical_cast(src); case LLUInt: return boost::lexical_cast(src); case Float: return boost::lexical_cast(src); case Double: return boost::lexical_cast(src); case DateTime: return boost::posix_time::time_from_string(src); case DateTimePtr: return boost::shared_ptr(new boost::posix_time::ptime(boost::posix_time::time_from_string(src))); } } VariableType::VariableType() : _VT(), convertCache(NULL), freer(NULL) { } VariableType::VariableType(const VariableType & vt) : _VT(*((const _VT *)&vt)), convertCache(NULL), freer(NULL) { } VariableType::~VariableType() { if (freer && convertCache) { freer(convertCache); } } void VariableType::operator=(const VariableType & vt) { if (freer && convertCache) { freer(convertCache); } freer = NULL; convertCache = NULL; _VT::operator=(*((const _VT *)&vt)); } /// Variable implementation whose value is a literal value of some known type class VariableLiteral : public VariableImpl { public: VariableLiteral(const Glib::ustring & src, const VT_typeID format = DefaultType) : val(makeVariableType(src, format)) { } virtual const VariableType & value() const { return val; } private: VariableType val; }; /// Base class for variables whose content is dynamic class VariableImplDyn : public VariableImpl { public: VariableImplDyn(const xmlpp::Element * e) : cacheValid(false) { try { if (e) { 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; }; /// Variable implementation to access session contents 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; }; /// Variable implementation to access call parameters 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; }; /// Variable implementation to access URI path fragments 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; } /// Variable implementation to access fields in row sets 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(depth); } 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; }; /// Variable implementation which has some fixed value class VariableFixed : public VariableImpl { public: VariableFixed(VariableType v) : var(v) { } const VariableType & value() const { return var; } private: VariableType var; }; /// Variable implementation to access platform configuration values class VariableConfig : public VariableImplDyn { public: VariableConfig(const xmlpp::Element * e) : VariableImplDyn(e), name(e->get_attribute_value("name")) { } const VariableType & value() const { if (!cacheValid) { cache = ApplicationEngine::getCurrent()->getCurrentConfig()->getValue(name); cacheValid = true; } return cache; } private: const Glib::ustring name; }; Variable::Variable(VariableType def) : var(new VariableFixed(def)) { } 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 == "config") var = new VariableConfig(c); else if (source == "literal" || source.empty()) { if (const xmlpp::Attribute * la = c->get_attribute("value")) { var = new VariableLiteral(la->get_value(), getVariableTypeFromName(c->get_attribute_value("type"))); } else if (const xmlpp::TextNode * t = c->get_child_text()) { var = new VariableLiteral(t->get_content(), getVariableTypeFromName(c->get_attribute_value("type"))); } else { var = new VariableLiteral(VariableType()); } } else throw UnknownVariableSource(source); return; } } if (!required) { var = new VariableFixed(def); return; } throw NoVariableDefinition(n); } 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)); }