#pragma once ///////////////////////////////////////////////////////////////////// // PersistHelp.h - demo one way to persist objects to XML file // // // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2017 // ///////////////////////////////////////////////////////////////////// /* * This package builds wrapper classes for strings, doubles, and Widgets. * They provide the same functionality as the wrapped type, but also * attach methods save() and restore() to persist the wrapped type. * * Package operations: * ------------------- * This package contains: * - IPersist interface - defines the methods needed to * serialize to XML and deserialize from XML * - StringWrapper class that persists strings * - DoubleWrapper class that persists doubles * - Widget class with int and string properties * - WidgetWrapper class that persits Widgets * * Required Files: * --------------- * PersistHelp.h, PersistHelp.cpp, * Convert.h, Convert.cpp, * CppProperties.h, CppProperties.cpp, * XmlDocument.h, XmlDocument.cpp, XmlElement.h, XmlElement.cpp, * ---- required to build XmlDocument from file or string ---- * XmlParser.h, XmlParser.cpp, * XmlElementParts.h, XmlElementParts.cpp, * ITokenizer.h, Tokenizer.h, Tokenizer.cpp, * * Build Instructions: * ------------------- * - Uses XmlDocument, so build XmlDocument project as static library * after undefining its test stub */ #include #include #include "../Utilities/Utilities.h" #include "../CppProperties/CppProperties.h" #include "../XmlDocument/XmlDocument/XmlDocument.h" #include "../XmlDocument/XmlElement/XmlElement.h" #include "../NoSqlDb/NoSqlDb.h" using namespace XmlProcessing; using namespace Utilities; namespace NoSQLDB { ///////////////////////////////////////////////////////////////////// // IPersist interface // - declares methods Persistance Wrappers must use // template struct IPersist { using Xml = std::string; virtual ~IPersist() {} virtual IPersist& operator=(const T& t) = 0; virtual operator T() = 0; virtual Xml save() = 0; virtual void restore(const Xml& xml) = 0; std::string id() { return typeid(*this).name(); } }; ///////////////////////////////////////////////////////////////////// // StringWrapper class // - wrapper classes implement the persistance interface // - they almost certainly should be non-template classes // because they have to be type specific // class StringWrapper : public IPersist { public: StringWrapper(const std::string& str = "") : str_(str) {} StringWrapper& operator=(const std::string& s); // covariant return type Xml save(); void restore(const Xml& xml); operator std::string(); private: std::string str_; }; //----< assignment overload >---------------------------------------- inline StringWrapper& StringWrapper::operator=(const std::string& s) { str_ = s; return *this; } //----< saves instance state as XML string >------------------------- inline StringWrapper::Xml StringWrapper::save() { return "" + str_ + ""; } //---< restores instance state from XML string >--------------------- inline void StringWrapper::restore(const Xml& xml) { size_t posStart = xml.find_first_of(">") + 1; size_t posEnd = xml.find_last_of("------------------------ inline StringWrapper::operator std::string() { return str_; } ///////////////////////////////////////////////////////////////////// // DoubleWrapper class // class DoubleWrapper : public IPersist { public: DoubleWrapper(double d = 0) : d_(d) {} DoubleWrapper& operator=(const double& d); Xml save(); void restore(const Xml& xml); operator double(); private: double d_; }; //----< assignment overload >---------------------------------------- inline DoubleWrapper& DoubleWrapper::operator=(const double& d) { d_ = d; return *this; } //----< saves instance state as XML string >------------------------- inline DoubleWrapper::Xml DoubleWrapper::save() { return "" + Utilities::Convert::toString(d_) + ""; } //---< restores instance state from XML string >--------------------- inline void DoubleWrapper::restore(const Xml& xml) { size_t posStart = xml.find_first_of(">") + 1; size_t posEnd = xml.find_last_of("::fromString(bodyString); } //----< cast operator returns double member >------------------------ inline DoubleWrapper::operator double() { return d_; } ///////////////////////////////////////////////////////////////////// // ElementWrapper class // template class ElementWrapper : public IPersist> { public: ElementWrapper(Element& elem) : elem_(elem) {} ElementWrapper& operator=(const Element& elem); Xml save(); void restore(const Xml& xml); operator Element() { return elem_; } private: Element elem_; }; //----< assignment overload >---------------------------------------- template ElementWrapper& ElementWrapper::operator=(const Element& elem) { elem_ = elem; return *this; } //----< saves instance state as XML string >------------------------- template typename ElementWrapper::Xml ElementWrapper::save() { XmlDocument doc; // make and add root element to doc SPtr root = makeTaggedElement("Element"); doc.docElement() = root; // make Property name element and add to root std::string nameStr = Element(*this).name; // force cast SPtr pNameElem = makeTaggedElement("name"); pNameElem->addChild(makeTextElement(nameStr)); root->addChild(pNameElem); // make Property category element and add to root std::string categoryStr = Element(*this).category; SPtr pCategoryElem = makeTaggedElement("category"); pCategoryElem->addChild(makeTextElement(categoryStr)); root->addChild(pCategoryElem); // make Property dateTime element and add to root DateTime dt = (Element(*this).dateTime); std::string dateTimeStr = dt.time(); SPtr pDateTimeElem = makeTaggedElement("dateTime"); pDateTimeElem->addChild(makeTextElement(dateTimeStr)); root->addChild(pDateTimeElem); // make Property children element and add to root NoSQLDB::Children children = Element(*this).children; SPtr pChildrenElem = makeTaggedElement("children"); for (Key k : children) { SPtr pChild = makeTaggedElement("childKey"); pChild->addChild(makeTextElement(k)); pChildrenElem->addChild(pChild); } root->addChild(pChildrenElem); // make Property data element and add to root Data data = Element(*this).data; SPtr pDataElem = makeTaggedElement("data"); pDataElem->addChild(makeTextElement(Convert::toString(data))); root->addChild(pDataElem); return doc.toString(); } //---< restores instance state from XML string >--------------------- using SPtr = std::shared_ptr; using SPtrs = std::vector; inline std::string getNthChildText(size_t n, SPtrs sptrs) { if (sptrs.size() < n + 1) return ""; if (sptrs[n]->children().size() == 0) return ""; return trim(sptrs[n]->children()[0]->value()); } inline std::string getFirstChildText(SPtrs sptrs) { return getNthChildText(0, sptrs); } template void ElementWrapper::restore(const Xml& xml) { XmlDocument doc(xml, XmlDocument::str); elem_.name = getFirstChildText(doc.descendents("name").select()); elem_.category = getFirstChildText(doc.descendents("category").select()); std::string dateTimeStr = getFirstChildText(doc.descendents("dateTime").select()); elem_.dateTime = DateTime(dateTimeStr); SPtrs sPtrs = doc.descendents("childKey").select(); for (size_t i = 0; i < sPtrs.size(); ++i) { Children& children = elem_.children.getValue(); children.push_back(getNthChildText(i, sPtrs)); } std::string dataText = getFirstChildText(doc.descendents("data").select()); elem_.data = Utilities::Convert::fromString(dataText); } ///////////////////////////////////////////////////////////////////// // NoSqlDb Record // template class NoSQLDBRecord { public: Property key; Property> elem; }; ///////////////////////////////////////////////////////////////////// // NoSqlDb RecordWrapper // template class NoSQLDBRecordWrapper : IPersist> { public: virtual NoSQLDBRecordWrapper& operator=(const NoSQLDBRecord& t) = 0; virtual operator NoSQLDBRecord() = 0; virtual Xml save() = 0; virtual void restore(const Xml& xml) = 0; private: NoSQLDBRecord record_; }; ///////////////////////////////////////////////////////////////////// // Widget and WidgetWrapper classes // class Widget { public: Property wint; Property wstr; }; class WidgetWrapper : public IPersist { public: WidgetWrapper() {}; WidgetWrapper(Widget widget) : widget_(widget) {} WidgetWrapper& operator=(const Widget& widget); Xml save(); void restore(const Xml& xml); operator Widget(); private: Widget widget_; }; //----< assignment operator overload >------------------------------- inline WidgetWrapper& WidgetWrapper::operator=(const Widget& widget) { widget_ = widget; return *this; } }