Introduction
I have a project to develop flexible menus and screens, and I am thinking to use XML as my format for the configuration file.
Thanks to Mike Melnikov. I read his project: "object-oriented XML parser" and I thought about how to adopt the project to mine.
Unfortunately, I have difficulty to adopt his project, because:
- I couldn’t put his project to became one .dll library and used by another .dll library.
- I can’t find a way to get data without ‘breaking’ the object-oriented structure.
- I like to use classes not structures.
So, I tried to read how his project worked and reengineered his project to my XML project. I changed the name to become RTSXML.dll.
Requirement
- I need accessing tree in a comfort way (well, at least for myself :-)).
- I need accessing child node in an elegant way, with no ‘breaking’ the OO concept.
Solution
I am still using the parser from ‘Object-oriented XML parser’ project and developed my own tree class structured.
CRTSXMLNode *CRTSXMLManager::Read(LPCTSTR filename)
{
CRTSXMLNode *pRoot = NULL;
if (SUCCEEDED(CreateDocument()))
{
if (SUCCEEDED(m_pIXMLDOMDocument->put_async(VARIANT_FALSE)))
{
VARIANT_BOOL vSuccess =
m_pIXMLDOMDocument->load(_variant_t(filename));
if(vSuccess)
{
MSXML::IXMLDOMNodePtr pNode = m_pIXMLDOMDocument;
if (pNode)
{
pRoot = new CRTSXMLNode;
pRoot->m_sTitle = "Root RTSXMLDocument";
pRoot->m_nOrder = 1;
CreateRTSXMLNode(pNode, pRoot);
m_pIXMLDOMDocument = NULL;
return pRoot;
}
}
}
}
ReportError(m_pIXMLDOMDocument->GetparseError());
return NULL;
}
The concept of tree structure is like this: in every node of the tree there are some child nodes and some attribute values:
class CRTSXMLNode
{
public:
[..deleted…]
CMapStringToString m_pAttrs;
CArray<CRTSXMLNode *, CRTSXMLNode *> m_pChilds;
[...deleted…]
public:
CRTSXMLNode();
virtual ~CRTSXMLNode();
public:
int GetFirstAttr(CString &key, CString &value);
int GetNextAttr(CString &key, CString &value);
int GetAttr(LPCTSTR key, int &value, int failedValue=-1);
int GetAttr(LPCTSTR key, CString &value);
int NumberOfAttrs() { return m_pAttrs.GetCount(); }
public:
CRTSXMLNode *GetFirstChild();
CRTSXMLNode *GetNextChild();
CRTSXMLNode *GetChilds(LPCTSTR key);
CRTSXMLNode *GetChild(int nOrder);
int NumberOfChilds() { return m_pChilds.GetUpperBound()+1; }
CRTSXMLNode *FindChilds(LPCTSTR keyTitle);
public:
void AddAttr(LPCTSTR key, LPCTSTR value) { m_pAttrs.SetAt(key, value); }
void AddChild(CRTSXMLNode *node);
void ReleaseAttrs();
void ReleaseChilds();
public:
void Trace(int nLevel=0);
}
I am using CMapStringToString
to keep my attribute values, because I need a fast access to the attribute without worrying about the order of attribute appearance and using CArray
to keep child nodes based on order of appearance.
How to use
I am thinking “reusability” without changing anything in future projects, so I created RTSXML.dll library for those implementations.
For using the library, I created two new classes outside of the library: CXMLWindowReader
and CXMLWindow
.
CXMLWindowReader
is a class for preparing and reading the XML file. Result of this class is CXMLNode
and becomes input for CXMLWindow
.
CXMLWindow
is a generalization of lexical class. For this sample, I created CXMLMenu
which inherited from CXMLWindow
.
class EXT_WINDOW_CLASS CXMLMenu : public CXMLWindow
{
protected:
CMenu *m_pMenu;
CMenu m_pBuffer[100];
int m_nBuffer;
protected:
int CreateMenu(CMenu *pMenu, CRTSXMLNode *pNode);
public:
CXMLMenu(CMenu *pMenu = NULL);
virtual ~CXMLMenu();
public:
void SetMenu(CMenu *pMenu) { m_pMenu = pMenu; }
public:
int Build(CRTSXMLNode *pNode);
};
and the important thing is implementation of int Build(CRTSXMLNode *pNode)
function, which looks like this:
int CXMLMenu::Build(CRTSXMLNode *pNode)
{
return CreateMenu(m_pMenu, pNode);
}
int CXMLMenu::CreateMenu(CMenu *pMenu, CRTSXMLNode *pNode)
{
CString szValue;
CString szDesc;
int nId;
CRTSXMLNode *child = pNode->GetFirstChild();
while (child)
{
if (child->m_sTitle == "popup")
{
CMenu *pSubMenu = &m_pBuffer[m_nBuffer++];
pSubMenu->CreatePopupMenu();
child->GetAttr("menu", szValue);
pMenu->AppendMenu(MF_POPUP, (UINT)pSubMenu->m_hMenu, szValue);
CreateMenu(pSubMenu, child);
}
else
if (child->m_sTitle == "item")
{
child->GetAttr("menu", szValue);
child->GetAttr("text", szDesc);
child->GetAttr("id", nId);
if (szValue == "separator")
pMenu->AppendMenu(MF_SEPARATOR, 0);
else
{
pMenu->AppendMenu(MF_STRING, nId, szValue);
}
}
child = pNode->GetNextChild();
}
return 1;
}
In “menu” node (not shown in code above. See the source code) there are two kinds of child nodes: they are “popup” and “item”.
“popup” is used to define popup menu and “item” is used to define item of the menu. Every popup menu can have two child nodes: these are another popup menu and item.
For complete reference, you could download my source code. Thank you.
In my next article, I will explain how to implement frame window using XML configuration file.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.