// marsfact.h // header file for class CMarsXMLFactory, // a callback class for the XML Push model parser // CMarsXMLFactory is used to construct a MARS its file from the xml #pragma once #include "stack.h" #include // forward declarations of data structures class CXMLElement; struct TagInformation; class CMarsXMLFactory : public CMarsComObject, public IXMLNodeFactory { public: CMarsXMLFactory(); virtual ~CMarsXMLFactory(); // IUnknown STDMETHOD_(ULONG,AddRef)(); STDMETHOD_(ULONG,Release)(); STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject); // CMarsComObject // This class has no back references so the passivate stuff is a noop virtual HRESULT DoPassivate() { return S_OK; } // IXMLNodeFactory - used to communicate with the IXMLParser virtual HRESULT STDMETHODCALLTYPE NotifyEvent( IXMLNodeSource *pSource, XML_NODEFACTORY_EVENT iEvt); virtual HRESULT STDMETHODCALLTYPE BeginChildren( IXMLNodeSource *pSource, XML_NODE_INFO *pNodeInfo); virtual HRESULT STDMETHODCALLTYPE EndChildren( IXMLNodeSource *pSource, BOOL fEmpty, XML_NODE_INFO *pNodeInfo); virtual HRESULT STDMETHODCALLTYPE Error( IXMLNodeSource *pSource, HRESULT hrErrorCode, USHORT cNumRecs, XML_NODE_INFO **apNodeInfo); virtual HRESULT STDMETHODCALLTYPE CreateNode( IXMLNodeSource *pSource, PVOID pNodeParent, USHORT cNumRecs, XML_NODE_INFO **apNodeInfo); // CMarsXMLFactory // Call "Run" when you want to start parsing... // Note: pisDoc must be a synchronous stream (not E_PENDING) -- so there! HRESULT Run(IStream *pisDoc); // Set the TagInformation* array used to create nodes. These // arrays should be const and live past the lifetime of the // parsing; they are not copied void SetTagInformation(TagInformation **ptiaTags); // Sets the LONG parameter passed as the last argument to all of // the nodes created in CreateNode. (See the definition of // CreationFunction below) This parameter is the way to provide // your run-time pointer/reference or whatever to the objects that // the xml is supposed to be initializing void SetLParam(LONG lParamNew); protected: HRESULT SetElementAttributes(CXMLElement *pxElem, XML_NODE_INFO **apNodeInfo, ULONG cInfoLen); HRESULT CreateElement(LPCWSTR wzTagName, ULONG cLen, CXMLElement **ppxElem); CStack m_elemStack; TagInformation **m_ptiaTags; LONG m_lParamArgument; }; // The AttributeInformation and TagInformation structs, which are used to determine syntax: what // element creation function is called for what tag name struct AttributeInformation { LPWSTR wzAttName; VARTYPE vt; }; typedef CXMLElement * (*CreationFunction)(LPCWSTR, VARTYPE, TagInformation **, AttributeInformation **, LONG lParam); struct TagInformation { LPWSTR wzTagName; CreationFunction funcCreate; VARTYPE vt; TagInformation **ptiaChildren; AttributeInformation **paiaAttributes; }; // The CXMLElement class, from which all the elements used by the // CMarsXMLFactory must be derived. The methods can be pick and chose // implemented; the base class implementations spew a TraceMsg error // and return S_FALSE or E_NOTIMPL as appropriate class CXMLElement { private: ULONG m_cRef; public: // CXMLElement is NOT a COM object, so these methods are // given wierd names to prevent confusion... ULONG Add_Ref() { return ++m_cRef; } ULONG Release_Ref() { if (--m_cRef == 0) { delete this; return 0; } else return m_cRef; } // these methods are used by the CMarsXMLFactory and should return S_FALSE on not impl // (to indicate an unexpected operation due to unexpected xml) virtual HRESULT OnNodeComplete(); // Addchild takes ownership of the element on S_OK; otherwise CMarsXMLFactory deletes pxeChild virtual HRESULT AddChild(CXMLElement *pxeChild); virtual HRESULT SetAttribute(LPCWSTR wzName, ULONG cLenName, LPCWSTR pwzValue, ULONG cLenValue); // // HACKHACK paddywack: // The implementer of this function should be aware of the strange // semantics here: SetInnerXMLText is sometimes called multiple times // due to issues with the Push-model parser. If SetInnerXMLText is called // more than once, the subsequent called mean "AppendInnerXMLText". :) // virtual HRESULT SetInnerXMLText(LPCWSTR pwzText, ULONG cLen); // GetName can return NULL virtual LPCWSTR GetName(); // The rest of these methods are generalized methods to access CXMLElements. // They should generally return E_whatever on failure or not impl. // This interface can be changed, as long as whoever changes it also makes sure // to serve up the changes in CXMLGenericElement // These methods are not used by any of the code in marsfact.cpp // The GetContent and GetAttribute methods return pointers to existing variants; // these variants should be considered const // TODO: No procedure should take VARIANT**: we need to make all of these // VARIANT* virtual HRESULT GetContent(VARIANT *pvarOut); virtual HRESULT GetAttribute(LPCWSTR wzName, VARIANT *pvarOut); virtual void FirstChild(); virtual void NextChild(); virtual CXMLElement *CurrentChild(); virtual CXMLElement *DetachCurrentChild(); virtual BOOL IsDoneChild(); protected: CXMLElement() { m_cRef = 1; } virtual ~CXMLElement() { ATLASSERT(0 == m_cRef); } }; struct CSimpleNode { CSimpleNode *m_psnodeNext; void *m_pvData; }; // Generic implementation of the CXMLElement class. class CXMLGenericElement : public CXMLElement { public: virtual ~CXMLGenericElement(); virtual HRESULT OnNodeComplete(); virtual HRESULT AddChild(CXMLElement *pxeChild); virtual HRESULT SetAttribute(LPCWSTR wzName, ULONG cLenName, LPCWSTR pwzValue, ULONG cLenValue); virtual HRESULT SetInnerXMLText(LPCWSTR pwzText, ULONG cLen); virtual LPCWSTR GetName(); virtual HRESULT GetContent(VARIANT *pvarOut); virtual HRESULT GetAttribute(LPCWSTR wzName, VARIANT *pvarOut); virtual void FirstChild(); virtual void NextChild(); // These methods return NULL if invalid // DetachCurrentChild does the same as CurrentChild, but also removes the child from the // list virtual CXMLElement *CurrentChild(); virtual CXMLElement *DetachCurrentChild(); virtual BOOL IsDoneChild(); static CXMLElement *CreateInstance(LPCWSTR wzName, VARTYPE vt, TagInformation **ptiaChildren, AttributeInformation **paiaAttributes, LONG lParam); protected: CXMLGenericElement(LPCWSTR wzName, VARTYPE vt, TagInformation **ptiaChildren, AttributeInformation **paiaAttributes); CXMLGenericElement() {} VARTYPE m_vtData; CComVariant m_varData; CSimpleNode *m_psnodeAttributes; // The header node is a member variable. CSimpleNode m_snodeChildrenFirst; CSimpleNode *m_psnodeChildrenFirst; CSimpleNode *m_psnodeChildrenEnd; CSimpleNode *m_psnodeChildrenIter; TagInformation **m_ptiaChildren; AttributeInformation **m_paiaAttributes; CComBSTR m_bstrName; }; // Some helpful functions for handling explicit length strings // Returns true if the strings are the same up to the nth char, // and the zero terminated string (third param) has a null character after that // position BOOL StrEqlNToSZ(const WCHAR *wzN, int n, const WCHAR *wzSZ); // Retunrs true if wz is L"true" or L"TRUE" and cLen is 4 // Must be "bool" for CComVariant to become VT_BOOL bool StrToIsTrueNW(const WCHAR *wz, ULONG cLen); // Converts the first cLen chars to a long, returning a E_FAIL if there is a non-digit // If L'\0' is encountered, the conversion stops at that point HRESULT StrToLongNW(const WCHAR *wzString, ULONG cLen, LONG *plong); // SpewTraceMessage copies the cLen wchars to a bstr and then // calls TraceMsg(TF_XMLPARSING | TF_WARNING, L"%sstring=%s", wzDesc, wzBstr) #ifdef DEBUG void SpewUnrecognizedString(const WCHAR *wzString, ULONG cLen, const WCHAR *wzDesc); #else #define SpewUnrecognizedString(strByLength, len, strDesc) #endif // I used TraceMst(..., L"...%s...", string) all over the place without checking string; // As everyone knows (except me!) printf faults on NULL for a %s arg, hence this macro #define NULL_STRING_PROTECT(str) str ? str : L""