#ifndef XMLBASE_H #define XMLBASE_H #pragma once #include #include #include #include "mmcerror.h" #include "macros.h" #include "tstring.h" #include "strings.h" #include "cstr.h" /*+-------------------------------------------------------------------------* This file contains code required to persist data using XML format. The classes defined here fall into following categories: Main persistence process engine CPersistor | this class is the working horse for XML persistence | every class supporting persistence gets the reference to | CPersistor to it's Persist method, where it implements | persisting ot their own code. Class' own code is persisted | by calling Persist* methods on persistor and passing internal | variables to them. The tree of CPersist objects is created during | persist operation (load or save) and lives only during this operation. MSXML interface wrappers: CXMLElement (wraps IXMLDOMNode interface) CXMLElementCollection (wraps IXMLDOMNodeList interface) CXMLDocument (wraps IXMLDOMDocument interface) | these wrappers add very little to the interfaces wrapped | - the throw SC type of exception instead of returning the error code | - maintain internal smart pointer to wrapped interfaces | - return error from methods if the interface has not be set base classes for classes supporting XML persistence CXMLObject - generic persistence support XMLListCollectionBase - persistence support for std::list XMLListCollectionImp - persistence support for std::list XMLMapCollectionBase - persistence support for std::map XMLMapCollection - persistence support for std::map XMLMapCollectionImp - persistence support for std::map | for object to support persistence it needs to derive from any of listed | classes (at least from CXMLObject). Other classes add some more functionality | to the derived class. Generic value persistece support CXMLValue - support for set of generic types (like int, string, etc) CXMLBoolean - support for BOOL and bool types | CXMLValue mostly used by implicit cast of the object given to | CPersistor::PersistAttribute or CPersistor::PersistContents Wrappers, adding persistence to regular types XMLPoint - persistence for POINT type XMLRect - persistence for RECT type XMLListCollectionWrap - persistence for std::list type XMLMapCollectionWrap - persistence for std::map type CXML_IStorage - persistence thru IStorage CXML_IStream - persistence thru IStream CXMLPersistableIcon - persistence for console icon CXMLVariant - persistence for CComVariant CXMLEnumeration - persistence for enumerations by literals CXMLBitFlags - persistence for bit-flags by literals | these classes usually take the reference to object they persist as | a parameter to the constructor and usually are constructed | on stack solely to persist the object and die afterwards SEE THE SAMPLE BELOW ---------------------------------------------------------------------------- SAMPLE: say we have classes A, B what need to be persisted (access specifiers ommited) class A { int i; }; class B { int j; A a; }; and we would want to persist them in format (assume A::i = 1, B::j = 2) : 1 we need to change the classes to support persistence: class A : public CXMLObject // to inherit persistence capability { int i; DEFINE_XML_TYPE("AT") // to define the tag name virtual void Persist(CPersistor &persistor) // to implement persistence for own staff { persistor.PersistContents(i); // persist i as AT element contents } }; class B : public CXMLObject // to inherit persistence capability { int j; A a; DEFINE_XML_TYPE("BT") // to define the tag name virtual void Persist(CPersistor &persistor) // to implement persistence for own staff { persistor.PersistAttribute(_T("INDEX"), j); // persist j persistor.Persist(a); // persist a } }; to have it in the string we may use: B b; std::wstring xml_text; b.ScSaveToString(&xml_text); ---------------------------------------------------------------------------- */ // forward declarations class CPersistor; class CXMLObject; class CXMLElement; class CXMLDocument; class CXMLValueExtension; enum XMLAttributeType { attr_required, attr_optional }; // special modes for certain persistors. These can be used to pass in information about // how to persist. Not as scalable as a class hierarchy, but useful nonetheless. enum PersistorMode { persistorModeNone = 0x0000, persistorModeValueSplitEnabled = 0x0001, // used for StringTableStrings, indicates that all strings should be persisted by value persistorModeDefault = persistorModeValueSplitEnabled // the default setting }; /*+-------------------------------------------------------------------------* * CONCEPT OF BINARY STORAGE * * To make xml documents more readable, a part of it (containing base64 - encoded * binary data) is stored at separate element located at the end of document. * the following sample illustrates this * * NOT_USING_BINARY_STROAGE USING_BINARY_STROAGE * * * * very_long_bin_data * ..... * * other_long_bin_data * very_long_bin_data * * * very_long_bin_data * * * * * The decision to be saved at binary storage is made by the CXMLObject. * It informs the persistor by returning "true" from UsesBinaryStorage() method; * * In addition ( to make it locatable ) elements may have 'Name' attribute. * CXMLObject may supply it by returning non-NULL pointer to 'Name' attribute value * from virtual method GetBinaryEntryName(). * * Storage is created / commited by methods provided in CXMLDocument * * NOTE: all mentioned methods, as well as GetXMLType() MUST return 'static' values. * To make XML document consistent, values need to be fixed [hardcoded]. *+-------------------------------------------------------------------------*/ /*+-------------------------------------------------------------------------* * class CXMLObject * * * PURPOSE: The basic XML persistent object. has a name and a Persist function. * When the object is persisted, an element with the name of the * object is created. The Persist function is then called with * a persistor created on the element. * *+-------------------------------------------------------------------------*/ class CXMLObject { // this is overridden public: virtual LPCTSTR GetXMLType() = 0; virtual void Persist(CPersistor &persistor) = 0; // following methods are implemented by binary elements only. // leave it to this base class for most CXMLObject-derived classes. // see comment "CONCEPT OF BINARY STORAGE" above virtual bool UsesBinaryStorage() { return false; } // this is optional. Overwrite only if you REALLY NEED the name virtual LPCTSTR GetBinaryEntryName() { return NULL; } public: // implemented by CXMLObject. Do not override SC ScSaveToString(std::wstring *pString, bool bPutHeader = false); // set bPutHeader = true to write out the "?xml" tag SC ScSaveToDocument( CXMLDocument& xmlDocument ); SC ScLoadFromString(LPCWSTR lpcwstrSource, PersistorMode mode = persistorModeNone); SC ScLoadFromDocument( CXMLDocument& xmlDocument ); }; /*+-------------------------------------------------------------------------* * MACRO DEFINE_XML_TYPE * * * PURPOSE: puts must-to-define methods to XCMLObject derived class implementation * Since xml tag is rather class attribute than object, static method * is provided to retrieve the type when object is not available * Virtual method is provided for tag to be available from gen. purpose * functions using the pointer to the base class. * * USAGE: add DEFINE_XML_TYPE(pointer_to_string) * * NOTE: 'public' access qualifier will be applied for lines following the macro *+-------------------------------------------------------------------------*/ #define DEFINE_XML_TYPE(name) \ public: \ virtual LPCTSTR GetXMLType() { return name; } \ static LPCTSTR _GetXMLType() { return name; } /*+-------------------------------------------------------------------------* * class CXMLElementCollection * * * PURPOSE: Wrapper around IXMLDOMNodeList. * * NOTE: Throws exceptions! *+-------------------------------------------------------------------------*/ class CXMLElementCollection { CComQIPtr m_sp; public: CXMLElementCollection(const CXMLElementCollection &other) { m_sp = other.m_sp; } CXMLElementCollection(IXMLDOMNodeList *ptr = NULL) { m_sp = ptr; } bool IsNull() { return m_sp == NULL; } void get_count(long *plCount); void item(LONG lIndex, CXMLElement *pElem); }; /*+-------------------------------------------------------------------------* * class CXMLElement * * * PURPOSE: Wrapper around IXMLDOMNode *+-------------------------------------------------------------------------*/ class CXMLElement { CComQIPtr m_sp; public: CXMLElement(LPUNKNOWN pElem = NULL) { m_sp = pElem; } CXMLElement(const CXMLElement& elem) { m_sp = elem.m_sp; } bool IsNull() { return m_sp == NULL; } // returns indentation to ad to child element or closing tag // to have nice-looking document. indentation depends on element depth bool GetTextIndent(CComBSTR& bstrIndent, bool bForAChild); void get_tagName(CStr &strTagName); void get_parent(CXMLElement * pParent); void setAttribute(const CStr &strPropertyName, const CComBSTR &bstrPropertyValue); bool getAttribute(const CStr &strPropertyName, CComBSTR &bstrPropertyValue); void removeAttribute(const CStr &strPropertyName); void get_children(CXMLElementCollection *pChildren); void get_type(DOMNodeType *pType); void get_text(CComBSTR &bstrContent); void addChild(CXMLElement& rChildElem); void removeChild(CXMLElement& rChildElem); void replaceChild(CXMLElement& rNewChildElem, CXMLElement& rOldChildElem); void getNextSibling(CXMLElement * pNext); void getChildrenByName(LPCTSTR strTagName, CXMLElementCollection *pChildren); void put_text(BSTR bstrValue); }; /*+-------------------------------------------------------------------------* * class CXMLDocument * * * PURPOSE: Wrapper class for IXMLDOMDocument * *+-------------------------------------------------------------------------*/ class CXMLDocument { CComQIPtr m_sp; public: CXMLDocument& operator = (IXMLDOMDocument *pDoc) { m_sp = pDoc; return *this; } bool IsNull() { return m_sp == NULL; } operator CXMLElement() { return CXMLElement(m_sp); } void get_root(CXMLElement *pElem); void createElement(DOMNodeType type, BSTR bstrTag, CXMLElement *pElem); // members to maintain the binary storage // see comment "CONCEPT OF BINARY STORAGE" above // used on storing (at top level, prior to persisting) // - creates an element for storing binary stuff void CreateBinaryStorage(); // used on loading (at top level, prior to persisting) // - locates an element to be used for loading binary stuff void LocateBinaryStorage(); // used on storing (at top level, after persisting the main staff) // - attaches the binary strage as the last child element of elemParent void CommitBinaryStorage(); // returns element representing binary storage. Used from CPersistor CXMLElement GetBinaryStorage() { return m_XMLElemBinaryStorage; } SC ScCoCreate(bool bPutHeader); SC ScLoad(LPCWSTR strSource); SC ScLoad(IStream *pStream, bool bSilentOnErrors = false ); SC ScSaveToFile(LPCTSTR lpcstrFileName); SC ScSave(CComBSTR &bstrResult); private: // element representing binary storage CXMLElement m_XMLElemBinaryStorage; }; /*+-------------------------------------------------------------------------* * class CXMLBinary * * * PURPOSE: GetGlobalSize() is alway rounded to allocation units, * so to know the actual size of the memory blob, we need to * carry the size information with HGLOBAL. * This struct is solely to bind these guys *+-------------------------------------------------------------------------*/ class CXMLBinary { public: CXMLBinary(); CXMLBinary(HGLOBAL handle, size_t size); ~CXMLBinary() { Detach(); } void Attach(HGLOBAL handle, size_t size); HGLOBAL Detach(); size_t GetSize() const; HGLOBAL GetHandle() const; SC ScAlloc(size_t size, bool fZeroInit = false); SC ScRealloc(size_t new_size, bool fZeroInit = false); SC ScFree(); SC ScLockData(const void **ppData) const; SC ScLockData(void **ppData) { return ScLockData(const_cast(ppData)); } SC ScUnlockData() const; protected: // implementation helpers private: // not implemented CXMLBinary(const CXMLBinary&); // not implemented; not allowed; operator = (CXMLBinary&); // not implemented; not allowed; private: HGLOBAL m_Handle; size_t m_Size; mutable unsigned m_Locks; }; /*+-------------------------------------------------------------------------* * class CXMLAutoBinary * * * PURPOSE: same as CXMLAutoBinary, but frees the memory on destruction *+-------------------------------------------------------------------------*/ class CXMLAutoBinary : public CXMLBinary { public: CXMLAutoBinary() : CXMLBinary() {} CXMLAutoBinary(HGLOBAL handle, size_t size) : CXMLBinary(handle, size) {} ~CXMLAutoBinary() { ScFree(); } }; /*+-------------------------------------------------------------------------* * class CXMLBinaryLock * * * PURPOSE: provides data locking functionality which is automatically removed * in destructor *+-------------------------------------------------------------------------*/ class CXMLBinaryLock { public: CXMLBinaryLock(CXMLBinary& binary); ~CXMLBinaryLock(); template SC ScLock(T **ppData) { return ScLockWorker(reinterpret_cast(ppData)); } SC ScUnlock(); private: // not implemented CXMLBinaryLock(const CXMLBinaryLock&); // not implemented; not allowed; operator = (CXMLBinaryLock&); // not implemented; not allowed; private: SC ScLockWorker(void **ppData); bool m_bLocked; CXMLBinary& m_rBinary; }; /*+-------------------------------------------------------------------------* * class CXMLValue * * * PURPOSE: Holds any type of value, in the spirit of a variant, except * that a pointer to the original object is kept. This allows * reading as well as writing to occur on the original object. * *+-------------------------------------------------------------------------*/ class CXMLValue { friend class CXMLBoolean; enum XMLType { XT_I4, //LONG XT_UI4, //ULONG XT_UI1, //BYTE XT_I2, //SHORT XT_DW, //DWORD XT_BOOL,//BOOL XT_CPP_BOOL,//bool XT_UINT,//UINT XT_INT, //INT XT_STR, //CStr XT_WSTR, // std::wstr XT_TSTR, // tstring XT_GUID, // GUID XT_BINARY, //HGLOBAL - unparsable data XT_EXTENSION }; const XMLType m_type; union { LONG * pL; ULONG * pUl; BYTE * pByte; SHORT * pS; DWORD * pDw; UINT * pUint; INT * pInt; CStr * pStr; std::wstring * pWStr; tstring * pTStr; GUID * pGuid; CXMLBinary * pXmlBinary; bool * pbool; BOOL * pBOOL; CXMLValueExtension * pExtension; } m_val; // private constructor. used by friend class CXMLBoolean CXMLValue(XMLType type) : m_type(type) { } public: CXMLValue(const CXMLValue &v) : m_type(v.m_type), m_val(v.m_val) { } CXMLValue(LONG &l) : m_type(XT_I4) { m_val.pL=&l; } CXMLValue(ULONG &ul) : m_type(XT_UI4) { m_val.pUl=&ul; } CXMLValue(BYTE &b) : m_type(XT_UI1) { m_val.pByte=&b; } CXMLValue(SHORT &s) : m_type(XT_I2) { m_val.pS=&s; } CXMLValue(UINT &u) : m_type(XT_UINT) { m_val.pUint=&u; } CXMLValue(INT &i) : m_type(XT_INT) { m_val.pInt=&i; } CXMLValue(CStr &str) : m_type(XT_STR) { m_val.pStr=&str; } CXMLValue(std::wstring &str) : m_type(XT_WSTR) { m_val.pWStr=&str; } CXMLValue(GUID &guid) : m_type(XT_GUID) { m_val.pGuid = &guid; } CXMLValue(CXMLBinary &binary) : m_type(XT_BINARY) { m_val.pXmlBinary = &binary; } CXMLValue(tstring &tstr) : m_type(XT_TSTR) { m_val.pTStr = &tstr; } CXMLValue(CXMLValueExtension& ext) : m_type(XT_EXTENSION) { m_val.pExtension = &ext; } SC ScReadFromBSTR(const BSTR bstr); // read input into the underlying variable. SC ScWriteToBSTR (BSTR * pbstr ) const; // writes the value into provided string LPCTSTR GetTypeName() const; // The following method is called when value is persisted as stand-alone element. // Depending on the result returned the contents may go to Binary Storage // see comment "CONCEPT OF BINARY STORAGE" above bool UsesBinaryStorage() const { return m_type == XT_BINARY; } }; /*+-------------------------------------------------------------------------* * CXMLBoolean * * * PURPOSE: special case: booleans. Need to be printed as true/false, NOT as integer. * *+-------------------------------------------------------------------------*/ class CXMLBoolean : public CXMLValue { public: CXMLBoolean(BOOL &b) : CXMLValue(XT_BOOL) { m_val.pBOOL = &b;} CXMLBoolean(bool &b) : CXMLValue(XT_CPP_BOOL) { m_val.pbool = &b;} }; /*+-------------------------------------------------------------------------* * CXMLValueExtension * * * PURPOSE: interface to extend CXMLValue by more sophisticated types * *+-------------------------------------------------------------------------*/ class CXMLValueExtension { public: virtual SC ScReadFromBSTR(const BSTR bstr) = 0; // read input into the underlying variable. virtual SC ScWriteToBSTR (BSTR * pbstr ) const = 0; // writes the value into provided string virtual LPCTSTR GetTypeName() const = 0; }; /*+-------------------------------------------------------------------------* * class EnumLiteral * * * PURPOSE: to define enum-to-literal mapping arrays (used by CXMLEnumeration) * *+-------------------------------------------------------------------------*/ struct EnumLiteral { UINT m_enum; LPCTSTR m_literal; }; /*+-------------------------------------------------------------------------* * class CXMLEnumeration * * * PURPOSE: to persist enumeration as string literal * using array of enum-to-literal mappings * *+-------------------------------------------------------------------------*/ class CXMLEnumeration : public CXMLValueExtension { // just an enum sized type to hold reference // while many enum types will be used, internally they // will be cast to this type enum enum_t { JUST_ENUM_SIZE_VALUE }; public: // template constructor to allow different enums to be persisted template CXMLEnumeration(_ENUM& en, const EnumLiteral * const etols, size_t count) : m_pMaps(etols) , m_count(count), m_rVal((enum_t&)(en)) { // folowing lines won't compile in case you are trying to pass // type other than enum or int COMPILETIME_ASSERT( sizeof(en) == sizeof(enum_t) ); UINT testit = en; } // CXMLValueExtension metods required to implement SC ScReadFromBSTR(const BSTR bstr); // read input into the underlying variable. SC ScWriteToBSTR (BSTR * pbstr ) const; // writes the value into provided string LPCTSTR GetTypeName() const { return _T("Enumerations"); } // to enable passing itself as CMLValue operator CXMLValue () { return CXMLValue (*this); } private: enum_t &m_rVal; const EnumLiteral * const m_pMaps; const size_t m_count; }; /*+-------------------------------------------------------------------------* * class CXMLBitFlags * * * PURPOSE: to persist bit flags as string literals * using array of enum-to-literal mappings * *+-------------------------------------------------------------------------*/ class CXMLBitFlags { public: // template constructor to allow different enums to be persisted template CXMLBitFlags(_integer& flags, const EnumLiteral * const etols, size_t count) : m_pMaps(etols) , m_count(count), m_rVal((UINT&)flags) { // folowing lines won't compile in case you are trying to pass // type other than enum or int COMPILETIME_ASSERT( sizeof(flags) == sizeof(UINT) ); UINT testit = flags; } void PersistMultipleAttributes(LPCTSTR name, CPersistor &persistor); private: UINT &m_rVal; const EnumLiteral * const m_pMaps; const size_t m_count; }; /*+-------------------------------------------------------------------------* * class XMLPoint * * * PURPOSE: Holds the name and value of a point object * *+-------------------------------------------------------------------------*/ class XMLPoint : public CXMLObject { CStr m_strObjectName; POINT & m_point; public: XMLPoint(const CStr& strObjectName, POINT &point); DEFINE_XML_TYPE(XML_TAG_POINT); virtual void Persist(CPersistor &persistor); }; /*+-------------------------------------------------------------------------* * class XMLRect * * * PURPOSE: Holds the name and value of a rectangle object * *+-------------------------------------------------------------------------*/ class XMLRect : public CXMLObject { CStr m_strObjectName; RECT & m_rect; public: XMLRect(const CStr strObjectName, RECT &rect); DEFINE_XML_TYPE(XML_TAG_RECTANGLE); virtual void Persist(CPersistor &persistor); }; /*+-------------------------------------------------------------------------* * class XMLListCollectionBase * * * PURPOSE: Defines the base list collection class for persisting stl:list's * It's intended to be used as a base for deriving list persitence classes * Persist method implements "load" by iterating thru xml elements * and calling OnNewElement for each, and can be reused by derived classes. * * USAGE: Probably the better idea is to use XMLListColLectionImp * as a base to your collection instead of this class (it is richer). Use this class * only if your class has special items which does not allow you to use that class. * *+-------------------------------------------------------------------------*/ class XMLListCollectionBase: public CXMLObject { public: // function called when new element is to be created and loaded virtual void OnNewElement(CPersistor& persistor) = 0; virtual void Persist(CPersistor& persistor); }; /*+-------------------------------------------------------------------------* * class XMLListCollectionImp * * PURPOSE: A base class for stl::list derived collections implementing persitence of * the list items as linear sequence of xml items. * The items kept in the list must be either CXMLObject-derived or be * of the simple type (one accepted by CXMLVBalue constructors) * * USAGE: Derive your class from XMLListCollectionImp parametrized by the list, * instead of deriving from stl::list directly. use DEFINE_XML_TYPE * to define tag name for collections element. * * NOTE: Your class should implement: GetXMLType() to be functional. * you can use DEFINE_XML_TYPE macro to do it for you * * NOTE: if provided implementation does not fit for you - f.i. your elements need * a parameter to the constructor, or special initialization, * use XMLListCollectionBase instead and provide your own methods *+-------------------------------------------------------------------------*/ template class XMLListCollectionImp: public XMLListCollectionBase , public LT { typedef typename LT::iterator iter_t; public: virtual void Persist(CPersistor& persistor) { if (persistor.IsStoring()) { for(iter_t it = begin(); it != end(); ++it) persistor.Persist(*it); } else { clear(); // let the base class do the job // it will call OnNewElement for every element found XMLListCollectionBase::Persist(persistor); } } // method called to create and load new element virtual void OnNewElement(CPersistor& persistor) { iter_t it = insert(end()); persistor.Persist(*it); } }; /*+-------------------------------------------------------------------------* * class XMLListCollectionWrap * * PURPOSE: A wrapper around stl::list to support persisting * To be used to persist stl::list objects "from outside" - i.e. without * deriving the list class from persistence-enabled classes. * * USAGE: If you have list m_l to persist, create the object XMLListCollectionWrap wrap(m_l,"tag") * on the stack and persist that object (f.i. Persistor.Persist(wrap) * * NOTE: if provided implementation does not fit for you - see if you can use * XMLListCollectionImp or XMLListCollectionBase as a base for your list. *+-------------------------------------------------------------------------*/ template class XMLListCollectionWrap: public XMLListCollectionBase { typedef typename LT::iterator iter_t; LT & m_l; CStr m_strListType; public: XMLListCollectionWrap(LT &l, const CStr &strListType) : m_l(l), m_strListType(strListType) {} virtual void Persist(CPersistor& persistor) { if (persistor.IsStoring()) { for(iter_t it = m_l.begin(); it != m_l.end(); ++it) persistor.Persist(*it); } else { m_l.clear(); // let the base class do the job // it will call OnNewElement for every element found XMLListCollectionBase::Persist(persistor); } } // method called to create and load new element virtual void OnNewElement(CPersistor& persistor) { iter_t it = m_l.insert(m_l.end()); persistor.Persist(*it); } virtual LPCTSTR GetXMLType() {return m_strListType;} private: // to prevent ivalid actions on object XMLListCollectionWrap(const XMLListCollectionWrap& other); XMLListCollectionWrap& operator = ( const XMLListCollectionWrap& other ) { return *this; } }; /*+-------------------------------------------------------------------------* * class XMLMapCollectionBase * * * PURPOSE: Defines the base map collection class for persisting stl:map's * It's intended to be used as a base for deriving map persitence classes. * Persist method implements "load" by iterating thru xml elements * and calling OnNewElement for each pair, and can be reused by derived classes. * * USAGE: Probably the better idea is to use XMLMapCollection or XMLMapColLectionImp * as a base to your collection instead of this class (they are richer). Use this class * only if your class has special items which does not allow you to use those classes. * *+-------------------------------------------------------------------------*/ class XMLMapCollectionBase: public CXMLObject { public: // function called when new element is to be created and loaded virtual void OnNewElement(CPersistor& persistKey,CPersistor& persistVal) = 0; // base implementation [loading only!] enumerates elements calling OnNewElement for each virtual void Persist(CPersistor& persistor); }; /*+-------------------------------------------------------------------------* * class XMLMapCollection * * PURPOSE: Use this class only when you cannot use XMLMapCollectionImp due to * special requirements on construction of the elements kept in the map * (see also purpose of XMLMapCollectionImp) * * USAGE: Derive your class from XMLMapCollection parametrized by the map, * instead of deriving from stl::map directly. use DEFINE_XML_TYPE * to define tag name for collections element. Define OnNewElement * * NOTE: Your class should implement: GetXMLType() to be functional. * you can use DEFINE_XML_TYPE macro to do it for you * * NOTE: Your class should implement: OnNewElement to be functional. * * NOTE: if provided implementation does not fit for you - * use XMLMapCollectionBase instead and provide your own methods *+-------------------------------------------------------------------------*/ template class XMLMapCollection: public XMLMapCollectionBase, public MT { typedef typename MT::iterator iter_t; public: virtual void Persist(CPersistor& persistor) { if (persistor.IsStoring()) { for(iter_t it = begin(); it != end(); ++it) { MT::key_type *pKey = const_cast(&it->first); persistor.Persist(*pKey); persistor.Persist(it->second); } } else { clear(); XMLMapCollectionBase::Persist(persistor); } } }; /*+-------------------------------------------------------------------------* * class XMLMapCollectionImp * * PURPOSE: A base class for stl::map derived collections implementing persitence of * the map items as linear sequence of xml items. * The items kept in the map must be either CXMLObject-derived or be * of the simple type (one accepted by CXMLVBalue constructors) * * USAGE: Derive your class from XMLMapCollectionImp parametrized by the map, * instead of deriving from stl::map directly. use DEFINE_XML_TYPE * to define tag name for collections element. * * NOTE: Your class should implement: GetXMLType() to be functional. * you can use DEFINE_XML_TYPE macro to do it for you * * NOTE: if provided implementation does not fit for you - f.i. your elements need * a parameter to the constructor, or special initialization, * use XMLMapCollection or XMLMapCollectionBase instead and provide your own methods *+-------------------------------------------------------------------------*/ template class XMLMapCollectionImp: public XMLMapCollection { public: virtual void OnNewElement(CPersistor& persistKey,CPersistor& persistVal) { MT::key_type key; persistKey.Persist(key); MT::referent_type val; persistVal.Persist(val); insert(MT::value_type(key,val)); } }; /*+-------------------------------------------------------------------------* * class XMLListCollectionWrap * * PURPOSE: A wrapper around stl::map to support persisting * To be used to persist stl::map objects "from outside" - i.e. without * deriving the map class from persistence-enabled classes. * * USAGE: If you have map m_m to persist, create the object XMLMapCollectionWrap wrap(m_m,"tag") * on the stack and persist that object (f.i. Persistor.Persist(wrap) * * NOTE: if provided implementation does not fit for you - see if you can use * XMLMapCollection or XMLMapCollectionImp or XMLMapCollectionBase as a base for your map *+-------------------------------------------------------------------------*/ template class XMLMapCollectionWrap: public XMLMapCollectionBase { typedef typename MT::iterator iter_t; MT & m_m; CStr m_strMapType; public: XMLMapCollectionWrap(MT &m, const CStr &strMapType) : m_m(m), m_strMapType(strMapType) {} virtual void OnNewElement(CPersistor& persistKey,CPersistor& persistVal) { MT::key_type key; persistKey.Persist(key); MT::referent_type val; persistVal.Persist(val); m_m.insert(MT::value_type(key,val)); } virtual void Persist(CPersistor& persistor) { if (persistor.IsStoring()) { for(iter_t it = m_m.begin(); it != m_m.end(); ++it) { MT::key_type *pKey = const_cast(&it->first); persistor.Persist(*pKey); persistor.Persist(it->second); } } else { m_m.clear(); XMLMapCollectionBase::Persist(persistor); } } virtual LPCTSTR GetXMLType() {return m_strMapType;} private: // to prevent ivalid actions on object XMLMapCollectionWrap(const XMLMapCollectionWrap& other); XMLMapCollectionWrap& operator = ( const XMLMapCollectionWrap& other ) { return *this; } }; /*+-------------------------------------------------------------------------* * class CPersistor * * * PURPOSE: Defines the Persistor class used for XML serialization * Persistors are aware if the file is being loaded or saved. Thus, * methods like Persist work for both directions, and * uses references to the data being persisted. * * USAGE: 1) a persistor can be created "underneath" another persistor, using * the appropriate constructor. * 2) To make an object persistable, derive it from CXMLObject, and * implement the abstract methods. The syntax persistor.Persits(object) * will then automatically work correctly. * 3) To persist an element use Pesist method. It will create new / locate * CPersist object for an element (under this persistor's element) * 4) Collection classes when persisting their members specify "bLockedOnChild" * constructors parameter as "true". This technique changes persistor's * behavior. Insted of loacting the element (#3), constructor of new * persistor will only check if element pointed by parent is of required type. * * NOTES: 1) StringTableStrings can be saved with either the ID inline or the * actual string value inline. The latter is useful when loading/saving * XML to/from a string instead of a file. This is controlled by the * EnableValueSplit method. * Binary storage usage is also controlled by it *+-------------------------------------------------------------------------*/ class CPersistor { // NOTE: if member variable is to be inherited by child persistors, // don't forget to add it to CPersistor::BecomeAChildOf method CXMLDocument m_XMLDocument; CXMLElement m_XMLElemCurrent; bool m_bIsLoading; bool m_bLockedOnChild; DWORD m_dwModeFlags; // any special modes private: void SetMode(PersistorMode mode, bool bEnable) {m_dwModeFlags = (m_dwModeFlags & ~mode) | (bEnable ? mode : 0);} bool IsModeSet(PersistorMode mode) {return (m_dwModeFlags & mode);} public: void SetMode(PersistorMode mode) {m_dwModeFlags = mode;} public: // construct a persistor from a parent persistor. // this creates a new XML element with the given name, // and everything persisted to the new persistor // is persisted under this element. CPersistor(CPersistor &persistorParent, const CStr &strElementType, LPCTSTR szElementName = NULL); // construct a new persistor for given document and root element // everything persisted to the new persistor // is persisted under this element. CPersistor(CXMLDocument &document, CXMLElement& rElemRoot); // used to create child persistor on particular element // bLockedOnChild is used to create a "fake parent" persistor, which // will always return the child without trying to locate it (pElemCurrent) CPersistor(CPersistor &other, CXMLElement& rElemCurrent, bool bLockedOnChild = false); CXMLDocument & GetDocument() {return (m_XMLDocument);} CXMLElement & GetCurrentElement() {return (m_XMLElemCurrent);} bool HasElement(const CStr &strElementType, LPCTSTR szstrElementName); void EnableValueSplit (bool bEnable) { SetMode(persistorModeValueSplitEnabled, bEnable); } // the various modes bool FEnableValueSplit() {return IsModeSet(persistorModeValueSplitEnabled);} // Load/Store mode related functions. bool IsLoading() {return m_bIsLoading;} bool IsStoring() {return !m_bIsLoading;} void SetLoading(bool b) {m_bIsLoading = b;} // special methods to set/get the Name attribute of a persistor void SetName(const CStr & strName); CStr GetName(); // Canned persistence methods: // .. to persist CXMLObject-derived object under own sub-element // object_body void Persist(CXMLObject &object, LPCTSTR lpstrName = NULL); // .. to persist value of the simple type under own sub-element // void Persist(CXMLValue xval, LPCTSTR name = NULL); // .. to persist value as named attribute of this element // void PersistAttribute(LPCTSTR name,CXMLValue xval,const XMLAttributeType type = attr_required); // .. to persist value as contents of this element // value // NOTE: xml element cannot have both value-as-contents and sub-elements void PersistContents(CXMLValue xval); // .. to persist flags as separate attributes void PersistAttribute( LPCTSTR name, CXMLBitFlags& flags ); /***************************************************************************\ * * METHOD: CPersistor::PersistList * * PURPOSE: it is designated for persisting std::list type of collections * as a subelement of the persistor. * NOTE: list elements need to be either CXMLObject-derived or be * of the simple type (one accepted by CXMLVBalue constructors) * NOTE2: list elements must have default constuctor available * * PARAMETERS: * const CStr &strListType - [in] tag of new subelement * LPCTSTR name - [in] name attr. of new subelement (NULL == none) * std::list& val - [in] list to be persisted * * RETURNS: * void * \***************************************************************************/ template void PersistList(const CStr &strListType, LPCTSTR name,std::list& val) { typedef std::list LT; XMLListCollectionWrap lcol(val,strListType); Persist(lcol, name); } /***************************************************************************\ * * METHOD: PersistMap * * PURPOSE: it is designated for persisting std::map type of collections * as a subelement of the persistor. * NOTE: map elements (both key and val) need to be either CXMLObject-derived * or be of the simple type (one accepted by CXMLVBalue constructors) * NOTE2: map elements must have default constuctor available * * PARAMETERS: * const CStr &strListType - [in] tag of new subelement * LPCTSTR name - [in] name attr. of new subelement (NULL == none) * std::map& val - [in] map to be persisted * * RETURNS: * void * \***************************************************************************/ template void PersistMap(const CStr &strMapType, LPCTSTR name, std::map& val) { typedef std::map MT; XMLMapCollectionWrap mcol(val,strMapType); Persist(mcol, name); } void PersistString(LPCTSTR lpstrName, CStringTableStringBase &str); private: // private implementation helpers // common constructor, not to be used from outside. // provided as common place for member initialization // all the constructors should call it prior to doing anything specific. void CommonConstruct(); // element creation / locating CXMLElement AddElement(const CStr &strElementType, LPCTSTR szElementName); CXMLElement GetElement(const CStr &strElementType, LPCTSTR szstrElementName, int iIndex = -1); void AddTextElement(BSTR bstrData); void GetTextElement(CComBSTR &bstrData); CXMLElement CheckCurrentElement(const CStr &strElementType, LPCTSTR szstrElementName); void BecomeAChildOf(CPersistor &persistorParent, CXMLElement elem); }; /*+-------------------------------------------------------------------------* * class CXML_IStorage * * PURPOSE: This class provides memory-based implementation of IStorage plus * it supports persisting the data on the storage to/from XML. * Mostly used to create IStorage for snapin data to be saved * to console file as XML binary blob * * USAGE: You will create the object whenever you need a memory-based IStorage * implementation. To access IStorage interface use GetIStorage() method. * It will create a storage if does not have one under control already. * You will use returned pointer for Read and Write operations. * Whenever required you will pass the object to CPersistor::Persist method * to have it persisted using XML persistence model. * * NOTE: You are encoureged to use GetIStorage() for accessing the undelying IStorage. * Do not cache returned pointer, since the storage may change when persisted * from XML, and this invalidates the pointer. However if you AddRef, * the pointer will be valid as long as the last reference is released. * That means it may outlive CXML_IStorage object itself - nothing wrong with that. * You only must be aware that once persistence (loading) from XML is completed, * CXML_IStorage will switch to the new storage and it will not be in sync with * the pointer you have. Always use GetIStorage() to get the current pointer. * *+-------------------------------------------------------------------------*/ class CXML_IStorage : public CXMLObject { public: // interface methods not throwing any exceptions SC ScInitializeFrom( IStorage *pSource ); SC ScInitialize(bool& bCreatedNewOne); SC ScGetIStorage( IStorage **ppStorage ); SC ScRequestSave(IPersistStorage * pPersistStorage); // instruct to persist to binary storage virtual bool UsesBinaryStorage() { return true; } DEFINE_XML_TYPE(XML_TAG_ISTORAGE); public: // interface methods throwing SC's virtual void Persist(CPersistor &persistor); private: IStoragePtr m_Storage; ILockBytesPtr m_LockBytes; // following methods\data is for trace support in CHK builds #ifdef DBG public: DBG_PersistTraceData m_dbg_Data; #endif // #ifdef DBG }; // class CXML_IStorage /*+-------------------------------------------------------------------------* * class CXML_IStream * * PURPOSE: This class provides memory-based implementation of IStream plus * it supports persisting the data on the stream to/from XML. * Mostly used to create IStream for snapin data to be saved * to console file as XML binary blob * * USAGE: You will create the object whenever you need a memory-based IStream * implementation. To access IStream interface use GetIStream() method. * It will create a stream if does not have one under control already. * You will use returned pointer for Read and Write operations. * Whenever required you will pass the object to CPersistor::Persist method * to have it persisted using XML persistence model. * * NOTE: You are encoureged to use GetIStream() for accessing the undelying IStream. * Do not cache returned pointer, since the stream may change when persisted * from XML, and this invalidates the pointer. However if you AddRef, * the pointer will be valid as long as the last reference is released. * That means it may outlive CXML_IStream object itself - nothing wrong with that. * You only must be aware that once persistence (loading) from XML is completed, * CXML_IStream will switch to the new stream and it will not be in sync with * the pointer you have. Always use GetIStream() to get the current pointer. * * NOTE: Every call to GetIStream() moves stream cursor to the begining of the stream * *+-------------------------------------------------------------------------*/ class CXML_IStream : public CXMLObject { public: // interface methods not throwing any exceptions SC ScInitializeFrom( IStream *pSource ); SC ScInitialize(bool& bCreatedNewOne); SC ScSeekBeginning(); SC ScGetIStream( IStream **ppStream ); SC ScRequestSave(IPersistStorage * pPersistStream); inline SC ScRequestSave(IPersistStream * pPersistStream) { return ScRequestSaveX(pPersistStream); } inline SC ScRequestSave(IPersistStreamInit * pPersistStreamInit) { return ScRequestSaveX(pPersistStreamInit); } // instruct to persist to binary storage virtual bool UsesBinaryStorage() { return true; } DEFINE_XML_TYPE(XML_TAG_ISTREAM); public: virtual void Persist(CPersistor &persistor); private: template SC ScRequestSaveX(TIPS * pPersistStream) { DECLARE_SC(sc, TEXT("CXML_IStream::ScRequestSaveX")); // initialize bool bCreatedNewOne = false; // unused here sc = ScInitialize(bCreatedNewOne); if (sc) return sc; // recheck pointers sc = ScCheckPointers( m_Stream, E_UNEXPECTED ); if (sc) return sc; ULARGE_INTEGER null_size = { 0, 0 }; sc = m_Stream->SetSize( null_size ); if(sc) return sc; sc = ScSeekBeginning(); if(sc) return sc; sc = pPersistStream->Save( m_Stream, TRUE ); if(sc) return sc; // commit the changes - this ensures everything is in HGLOBAL sc = m_Stream->Commit( STGC_DEFAULT ); if(sc) return sc; #ifdef DBG if (S_FALSE != pPersistStream->IsDirty()) DBG_TraceNotResettingDirty(typeid(TIPS).name()); #endif // #ifdef DBG return sc; } private: IStreamPtr m_Stream; #ifdef DBG // tracing support public: DBG_PersistTraceData m_dbg_Data; void DBG_TraceNotResettingDirty(LPCSTR strIntfName); #endif // #ifdef DBG }; // class CXML_IStream /*+-------------------------------------------------------------------------* * class CXMLPersistableIcon * * PURPOSE: Persists wraps for persisting CPersistableIcon * * USAGE: Create object whenever you need to persist to CPersistableIcon * *+-------------------------------------------------------------------------*/ class CPersistableIcon; class CXMLPersistableIcon : public CXMLObject { CPersistableIcon& m_Icon; public: CXMLPersistableIcon(CPersistableIcon& Icon) : m_Icon(Icon) {} DEFINE_XML_TYPE(XML_TAG_ICON); virtual void Persist(CPersistor &persistor); }; /*+-------------------------------------------------------------------------* * CXMLVariant * * This class implements a CComVariant that can persist itself to/from an * XML persistor. *--------------------------------------------------------------------------*/ class CXMLVariant : public CComVariant, public CXMLObject { public: // construction and assignment forwarders CXMLVariant() {} CXMLVariant(const VARIANT& varSrc) : CComVariant(varSrc) {} CXMLVariant(const CComVariant& varSrc) : CComVariant(varSrc) {} CXMLVariant(const CXMLVariant& varSrc) : CComVariant(varSrc) {} CXMLVariant(BSTR bstrSrc) : CComVariant(bstrSrc) {} CXMLVariant(LPCOLESTR lpszSrc) : CComVariant(lpszSrc) {} #ifndef OLE2ANSI CXMLVariant(LPCSTR lpszSrc) : CComVariant(lpszSrc) {} #endif CXMLVariant(bool bSrc) : CComVariant(bSrc) {} CXMLVariant(int nSrc) : CComVariant(nSrc) {} CXMLVariant(BYTE nSrc) : CComVariant(nSrc) {} CXMLVariant(short nSrc) : CComVariant(nSrc) {} CXMLVariant(float fltSrc) : CComVariant(fltSrc) {} CXMLVariant(double dblSrc) : CComVariant(dblSrc) {} CXMLVariant(CY cySrc) : CComVariant(cySrc) {} CXMLVariant(long nSrc, VARTYPE vtSrc = VT_I4) : CComVariant(nSrc, vtSrc) {} CXMLVariant& operator=(const CXMLVariant& varSrc) { CComVariant::operator=(varSrc); return (*this); } CXMLVariant& operator=(const CComVariant& varSrc) { CComVariant::operator=(varSrc); return (*this); } CXMLVariant& operator=(const VARIANT& varSrc) { CComVariant::operator=(varSrc); return (*this); } CXMLVariant& operator=(BSTR bstrSrc) { CComVariant::operator=(bstrSrc); return (*this); } CXMLVariant& operator=(LPCOLESTR lpszSrc) { CComVariant::operator=(lpszSrc); return (*this); } #ifndef OLE2ANSI CXMLVariant& operator=(LPCSTR lpszSrc) { CComVariant::operator=(lpszSrc); return (*this); } #endif CXMLVariant& operator=(bool bSrc) { CComVariant::operator=(bSrc); return (*this); } CXMLVariant& operator=(int nSrc) { CComVariant::operator=(nSrc); return (*this); } CXMLVariant& operator=(BYTE nSrc) { CComVariant::operator=(nSrc); return (*this); } CXMLVariant& operator=(short nSrc) { CComVariant::operator=(nSrc); return (*this); } CXMLVariant& operator=(long nSrc) { CComVariant::operator=(nSrc); return (*this); } CXMLVariant& operator=(float fltSrc) { CComVariant::operator=(fltSrc); return (*this); } CXMLVariant& operator=(double dblSrc) { CComVariant::operator=(dblSrc); return (*this); } CXMLVariant& operator=(CY cySrc) { CComVariant::operator=(cySrc); return (*this); } public: DEFINE_XML_TYPE(XML_TAG_VARIANT); virtual void Persist(CPersistor &persistor); bool IsPersistable() const { return (IsPersistable(this)); } static bool IsPersistable(const VARIANT* pvar) { if (pvar == NULL) return (false); /* * we can only store variants that are "simple" (i.e. not by-ref, * array, etc.) */ return ((V_VT(pvar) & ~VT_TYPEMASK) == 0); } }; /***************************************************************************\ * * CLASS: CConsoleFilePersistor * * PURPOSE: File persistence black box. all console file - user data logic * is hiden under this class * * USAGE: Use instance of this class to load and save console file, * NOTE - saving should be done with the same instance the console * was loaded. * Good idea for your class maintaning console (such as AMCDocument) * to either derive of contain instance of this class * \***************************************************************************/ class CConsoleFilePersistor { public: // public interface CConsoleFilePersistor() : m_bCRCValid(false) {} SC ScSaveConsole(LPCTSTR lpstrConsolePath, bool bForAuthorMode, const CXMLDocument& xmlDocument); SC ScLoadConsole(LPCTSTR lpstrConsolePath, bool& bXmlBased, CXMLDocument& xmlDocument, IStorage **ppStorage); static SC ScGetUserDataFolder(tstring& strUserDataFolder); private: // implementation helpers static SC ScGetUserDataPath(LPCTSTR lpstrOriginalPath, tstring& strUserDataPath); static SC ScGetUserData(const tstring& strUserDataConsolePath, const tstring& strFileCRC, bool& bValid, CXMLDocument& xmlDocument); static SC ScOpenDocAsStructuredStorage(LPCTSTR lpszPathName, IStorage **ppStorage); static SC ScLoadXMLDocumentFromFile(CXMLDocument& xmlDocument, LPCTSTR strFileName, bool bSilentOnErrors = false); private: // compress/uncompress the file static void GetBinaryCollection(CXMLDocument& xmlDocument, CXMLElementCollection& colBinary); static SC ScCompressUserStateFile(LPCTSTR szConsoleFilePath, CXMLDocument & xmlDocument); static SC ScUncompressUserStateFile(CXMLDocument &xmlDocumentOriginal, CXMLDocument& xmlDocument); private: // internal data tstring m_strFileCRC; bool m_bCRCValid; }; #endif // XMLBASE_H