본문 바로가기

Windows/MFC

CXMLFile - A Simple C++ XML Parser

http://69.10.233.10/KB/XML/CXMLFile.aspx

#include "StdAfx.h"
#include "XMLFile.h"
#include "io.h"


CXMLFile::CXMLFile(void)
{
    // Init members
    m_dwDataSize = 0;
    m_lpData = NULL;
    m_lpXMLElement = NULL;
}

CXMLFile::~CXMLFile(void)
{
    // Unload previously loaded XML file
    Unload();
}

void CXMLFile::Unload()
{
    // Destroy XML element collection
    if (m_lpXMLElement)
    {
        delete m_lpXMLElement;
        m_lpXMLElement = NULL;
    }

    // Free data buffer
    if (m_lpData != NULL)
    {
        free(m_lpData);
        m_lpData = NULL;
        m_dwDataSize = 0;
    }
}

BOOL CXMLFile::LoadFromFile(LPTSTR lpszXMLFilePath)
{
    BOOL bResult = FALSE;

    // Unload previously loaded XML file
    Unload();

    // Try to open XML file
    FILE* xmlFile = _tfopen(lpszXMLFilePath, _T("r"));
    if (xmlFile != NULL)
    {
        // Get file length
        m_dwDataSize = _filelength(xmlFile->_file);

        // Read file data
        m_lpData = (BYTE*)malloc(m_dwDataSize*sizeof(BYTE));
        DWORD dwActualSize = 0;
        if ((dwActualSize=(DWORD)fread(m_lpData, sizeof(BYTE), m_dwDataSize, xmlFile)))
        {
            // Update actual data size
            if (dwActualSize != m_dwDataSize)
            {
                m_dwDataSize = dwActualSize;
                m_lpData = (BYTE*)realloc(m_lpData, m_dwDataSize*sizeof(BYTE));
            }

            // Create "XML:ROOT" element
            m_lpXMLElement = new CXMLElement();
            m_lpXMLElement->Create(_T("XML:ROOT"), XET_TAG);

            // Parse XML file
            DWORD dwStartOffset = 0;
            DWORD dwEndOffset = m_dwDataSize;
            if (ParseXMLElement(m_lpXMLElement, dwStartOffset, dwEndOffset))
            {
                bResult = TRUE;
            }
        }

        // Close XML file
        fclose(xmlFile);
    }

    return bResult;
}

BOOL CXMLFile::LoadFromStream(LPBYTE lpData, DWORD dwDataSize)
{
    BOOL bResult = FALSE;

    // Unload previously loaded XML file
    Unload();

    // Check for valid XML data
    if (lpData != NULL)
    {
        // Get data size
        m_dwDataSize = dwDataSize;

        // Get data
        m_lpData = (BYTE*)malloc(m_dwDataSize*sizeof(BYTE));
        memcpy(m_lpData, lpData, m_dwDataSize);

        // Create "XML:ROOT" element
        m_lpXMLElement = new CXMLElement();
        m_lpXMLElement->Create(_T("XML:ROOT"), XET_TAG);

        // Parse XML data
        DWORD dwStartOffset = 0;
        DWORD dwEndOffset = m_dwDataSize;
        if (ParseXMLElement(m_lpXMLElement, dwStartOffset, dwEndOffset))
        {
            bResult = TRUE;
        }
    }

    return bResult;
}

BOOL CXMLFile::SaveToFile(LPTSTR lpszXMLFilePath)
{
    BOOL bResult = FALSE;

    // Check for valid XML element collection
    if (m_lpXMLElement != NULL)
    {
        // Try to open XML file
        FILE* xmlFile = _tfopen(lpszXMLFilePath, _T("w"));
        if (xmlFile != NULL)
        {
            // Write XML prolog
            BYTE szProlog[] = "<?xml version=\"1.0\"?>\n";
            fwrite(szProlog, sizeof(BYTE), strlen((LPSTR)szProlog), xmlFile);

            // Save XML element collection
            CXMLElement* lpXMLElement = m_lpXMLElement->GetFirstChild();
            // Escape attributes
            while ((lpXMLElement != NULL) && (lpXMLElement->GetElementType() == XET_ATTRIBUTE))
            {
                lpXMLElement = m_lpXMLElement->GetNextChild();
            }
            while(lpXMLElement != NULL)
            {
                // Save XML element
                bResult = SaveXMLElement(xmlFile, lpXMLElement);
                lpXMLElement = m_lpXMLElement->GetNextChild();
            }

            // Close XML file
            fclose(xmlFile);
        }
    }

    return bResult;
}

BOOL CXMLFile::SaveXMLElement(FILE* file, CXMLElement* lpXMLElement)
{
    BOOL bResult = FALSE;

    // Check for valid XML file
    if (file != NULL)
    {
        // Check for valid XML element
        if (lpXMLElement != NULL)
        {
            // Set params
            BYTE szSOT = '<';
            BYTE szEOT = '>';
            BYTE szCLT = '/';
            BYTE szESC = ' ';
            BYTE szATTR = '=';
            BYTE szDQT = '\"';
            BYTE szCRLF = '\n';

            // Get XML element name
            LPTSTR lpszElementName = lpXMLElement->GetElementName();
            int iNameLen = (int)_tcslen(lpszElementName);
            LPBYTE lpNameData = (LPBYTE)malloc(iNameLen*sizeof(BYTE));
            for (int i=0; i<iNameLen; i++)
            {
                lpNameData[i] = (BYTE)lpszElementName[i];
            }

            // Check for XML element type
            if (lpXMLElement->GetElementType() == XET_TAG)
            {
                // Save XML element opening tag
                fwrite(&szSOT, sizeof(BYTE), 1, file);
                fwrite(lpNameData, sizeof(BYTE), iNameLen, file);

                // Save XML element attribute collection
                CXMLElement* lpXMLChildElement = lpXMLElement->GetFirstChild();
                while ((lpXMLChildElement != NULL) && (lpXMLChildElement->GetElementType() == XET_ATTRIBUTE))
                {
                    // Get attribute name
                    LPTSTR lpszAttributeName = lpXMLChildElement->GetElementName();
                    int iNameLen = (int)_tcslen(lpszAttributeName);
                    LPBYTE lpAttributeName = (LPBYTE)malloc(iNameLen*sizeof(BYTE));
                    for (int i=0; i<iNameLen; i++)
                    {
                        lpAttributeName[i] = (BYTE)lpszAttributeName[i];
                    }

                    // Get attribute value
                    LPTSTR lpszAttributeValue = lpXMLChildElement->GetValue();
                    int iValueLen;
                    LPBYTE lpAttributeValue;
                    if (lpszAttributeValue != NULL)
                    {
                        iValueLen = (int)_tcslen(lpszAttributeValue);
                        lpAttributeValue = (LPBYTE)malloc(iValueLen*sizeof(BYTE));
                        for (int i=0; i<iValueLen; i++)
                        {
                            lpAttributeValue[i] = (BYTE)lpszAttributeValue[i];
                        }
                    }
                    else
                    {
                        iValueLen = -1;
                        lpAttributeValue = NULL;
                    }

                    // Save XML attribute element
                    fwrite(&szESC, sizeof(BYTE), 1, file);
                    fwrite(lpAttributeName, sizeof(BYTE), iNameLen, file);
                    fwrite(&szATTR, sizeof(BYTE), 1, file);
                    fwrite(&szDQT, sizeof(BYTE), 1, file);
                    if (lpAttributeValue != NULL)
                    {
                        fwrite(lpAttributeValue, sizeof(BYTE), iValueLen, file);
                    }
                    fwrite(&szDQT, sizeof(BYTE), 1, file);
                    fwrite(&szESC, sizeof(BYTE), 1, file);

                    free(lpAttributeName);
                    if (lpAttributeValue != NULL)
                    {
                        free(lpAttributeValue);
                    }

                    lpXMLChildElement = lpXMLElement->GetNextChild();
                }
                fwrite(&szEOT, sizeof(BYTE), 1, file);
                fwrite(&szCRLF, sizeof(BYTE), 1, file);

                // Save XML element child collection
                while(lpXMLChildElement != NULL)
                {
                    // Save XML element
                    bResult = SaveXMLElement(file, lpXMLChildElement);
                    lpXMLChildElement = lpXMLElement->GetNextChild();
                }

                // Save XML element closing tag
                fwrite(&szSOT, sizeof(BYTE), 1, file);
                fwrite(&szCLT, sizeof(BYTE), 1, file);
                fwrite(lpNameData, sizeof(BYTE), iNameLen, file);
                fwrite(&szEOT, sizeof(BYTE), 1, file);
                fwrite(&szCRLF, sizeof(BYTE), 1, file);
            }
            else if (lpXMLElement->GetElementType() == XET_TEXT)
            {
                // Save XML text element
                fwrite(lpNameData, sizeof(BYTE), iNameLen, file);
                fwrite(&szCRLF, sizeof(BYTE), 1, file);
            }

            // Free data bufers
            free(lpNameData);

            bResult = TRUE;
        }
    }

    return bResult;
}

BOOL CXMLFile::ParseXMLElement(CXMLElement* lpXMLElement, DWORD dwStartOffset, DWORD dwEndOffset)
{
    BOOL bResult = FALSE;

    // Check for valid data
    if (m_lpData != NULL)
    {
        // Parse XML data
        BOOL bPlainText = TRUE;
        DWORD i = dwStartOffset;
        DWORD dwAttributeOffset;
        DWORD dwFlags = FLG_DEF;
        DWORD dwMode = MOD_DEF;
        _TCHAR lpName[255], lpOpenTagName[255], lpCloseTagName[255], lpAttributeName[255], lpAttributeValue[255];
        int iNameLength = 0;
        DWORD startOffset, endOffset;
        _tcscpy((LPTSTR)lpOpenTagName, _T(""));
        _tcscpy((LPTSTR)lpCloseTagName, _T(""));
        while ((i <= dwEndOffset) && (i < m_dwDataSize))
        {
            // Check for input character
            switch (m_lpData[i])
            {
                case XML_SOT:
                {
                    // Update flags
                    dwFlags |= FLG_SOT;
                    dwFlags |= FLG_OPT;
                    bPlainText = FALSE;

                    // Clear name
                    _tcscpy((LPTSTR)lpName, _T(""));
                    iNameLength = 0;

                    // Set new mode
                    dwMode = MOD_DEF;
                }
                break;

                case XML_EOT:
                {
                    // Update flags
                    dwFlags |= FLG_EOT;

                    // Set new mode
                    dwMode = MOD_CLS;

                    dwStartOffset = i + 1;
                }
                break;

                case XML_PRT:
                {
                    // Update flags
                    dwFlags |= FLG_PRT;

                    // Set new mode
                    dwMode = MOD_DEF;
                }
                break;

                case XML_SPT:
                {
                    // Update flags
                    dwFlags |= FLG_SPT;

                    // Set new mode
                    dwMode = MOD_DEF;
                }
                break;

                case XML_ESC:
                {
                    // Update flags
                    dwFlags |= FLG_ESC;

                    // Set new mode
                    if (dwMode != MOD_DEF)
                    {
                        dwMode = MOD_CLS;
                    }
                }
                break;

                case XML_CLT:
                {
                    // Update flags
                    dwFlags |= FLG_CLT;
                }
                break;

                case XML_ATR:
                {
                    // Update flags
                    dwFlags |= FLG_ATR;

                    // Clear attribute
                    _tcscpy((LPTSTR)lpAttributeName, _T(""));
                    _tcscpy((LPTSTR)lpAttributeValue, _T(""));
                }
                break;

                default:
                {
                    // Default (do nothing)
                }
            }

            // Check mode
            switch (dwMode)
            {
                case MOD_APN:
                {
                    // Append name
                    lpName[iNameLength++] = m_lpData[i];
                }
                break;

                case MOD_CLS:
                {
                    // Close name
                    lpName[iNameLength++] = '\0';

                    // Check for opening tag
                    if (dwFlags & FLG_OPT)
                    {
                        // Update flags
                        dwFlags &= ~FLG_OPT;

                        // Check for opened tag
                        if (_tcscmp(lpOpenTagName, _T("")) == 0)
                        {
                            // Update start offset
                            startOffset = i + 1;

                            // Save tag name
                            _tcsncpy(lpOpenTagName, lpName, iNameLength);
                            lpCloseTagName[0] = '/';
                            _tcsncpy(lpCloseTagName+1, lpOpenTagName, iNameLength);
                        }
                        // Compare tag names
                        else if (_tcsncmp(lpName, lpCloseTagName, iNameLength) == 0)
                        {
                            // Update end offset
                            endOffset = i - iNameLength - 1;

                            // Create new XML child element
                            CXMLElement* pNewXMLElement = new CXMLElement();
                            pNewXMLElement->Create(lpOpenTagName, XET_TAG);
                            lpXMLElement->AppendChild(pNewXMLElement);

                            // Parse child XML element
                            bResult = ParseXMLElement(pNewXMLElement, startOffset, endOffset);
                            if (!bResult)
                            {
                                return FALSE;
                            }

                            // Clear opening and closing tag name
                            _tcscpy(lpOpenTagName, _T(""));
                            _tcscpy(lpCloseTagName, _T(""));
                        }
                        else
                        {
                            bResult = FALSE;
                        }
                    }
                    // Check for closing tag
                    if (dwFlags & FLG_CLT)
                    {
                        // Update flags
                        dwFlags &= ~FLG_CLT;
                    }
                }
                break;

                case MOD_DEF:
                {
                    // Default (do nothing)
                }
                break;
            }

            // Check flags
            if (dwFlags & FLG_SOT)
            {
                // Set new mode
                dwMode = MOD_APN;

                // Update flags
                dwFlags &= ~FLG_SOT;
            }
            else if (dwFlags & FLG_EOT)
            {
                // Set new mode
                dwMode = MOD_DEF;

                // Update flags
                dwFlags &= ~FLG_EOT;
            }
            else if (dwFlags & FLG_PRT)
            {
                // Set new mode
                dwMode = MOD_DEF;

                // Update flags
                dwFlags &= ~FLG_PRT;
            }
            else if (dwFlags & FLG_ESC)
            {
                // Set new mode
                dwMode = MOD_DEF;

                // Update flags
                dwFlags &= ~FLG_ESC;
            }
            else if (dwFlags & FLG_ATR)
            {
                // Extract attribute name and value
                dwAttributeOffset = ExtractAttribute(i, lpAttributeName, lpAttributeValue);

                // Update flags
                dwFlags &= ~FLG_ATR;
            }

            // Check for valid attribute
            if (i == dwAttributeOffset)
            {
                // Check for valid tag
                if (_tcscmp(lpOpenTagName, _T("")) == 0)
                {
                    // Create new XML child element
                    CXMLElement* pNewXMLElement = new CXMLElement();
                    pNewXMLElement->Create(lpAttributeName, XET_ATTRIBUTE);
                    pNewXMLElement->SetValue(lpAttributeValue);
                    lpXMLElement->AppendChild(pNewXMLElement);
                }
            }

            // Increment offset
            i++;
        }

        // Check for XML text element
        if (bPlainText)
        {
            // Extract element name
            int iNameLen = dwEndOffset - dwStartOffset + 1;
            LPTSTR lpszElementName = (LPTSTR)malloc((iNameLen+1)*sizeof(_TCHAR));
            int k = 0;
            for (DWORD j=dwStartOffset; j<=dwEndOffset; j++)
            {
                lpszElementName[k++] = m_lpData[j];
            }
            lpszElementName[k] = '\0';

            // Create new XML child element
            CXMLElement* pNewXMLElement = new CXMLElement();
            pNewXMLElement->Create(lpszElementName, XET_TEXT);
            lpXMLElement->AppendChild(pNewXMLElement);
            free(lpszElementName);

            bResult = TRUE;
        }
    }

    return bResult;
}

DWORD CXMLFile::ExtractAttribute(DWORD offset, LPTSTR lpszAttributeName, LPTSTR lpszAttributeValue)
{
    DWORD dwResult = 0;

    // Extract attribute name
    int k = 0;
    DWORD dwNameOffset = offset - 1;
    while ((dwNameOffset >= 0) && (m_lpData[dwNameOffset] != XML_ESC) && (m_lpData[dwNameOffset] != XML_SOT) &&
        (m_lpData[dwNameOffset] != XML_EOT) && (m_lpData[dwNameOffset] != XML_PRT) && (m_lpData[dwNameOffset] != XML_SPT))
    {
        if ((m_lpData[dwNameOffset] != XML_SQT) && (m_lpData[dwNameOffset] != XML_DQT))
        {
            lpszAttributeName[k++] = m_lpData[dwNameOffset];
        }
        dwNameOffset--;
    }
    lpszAttributeName[k] = '\0';
    // Reverse attribute name
    _TCHAR tchTmp;
    for (int i=0; i<k/2; i++)
    {
        tchTmp = lpszAttributeName[i];
        lpszAttributeName[i] = lpszAttributeName[k-i-1];
        lpszAttributeName[k-i-1] = tchTmp;
    }

    // Extract attribute value
    DWORD dwStartAttributeOffset=0, dwEndAttributeOffset=0;
    DWORD dwValueOffset = offset + 1;
    while (dwValueOffset < m_dwDataSize)
    {
        if ((m_lpData[dwValueOffset] == XML_SQT) || (m_lpData[dwValueOffset] == XML_DQT))
        {
            dwStartAttributeOffset = dwValueOffset + 1;
            break;
        }
        dwValueOffset++;
    }
    dwValueOffset++;
    while (dwValueOffset < m_dwDataSize)
    {
        if ((m_lpData[dwValueOffset] == XML_SQT) || (m_lpData[dwValueOffset] == XML_DQT))
        {
            dwEndAttributeOffset = dwValueOffset - 1;
            break;
        }
        dwValueOffset++;
    }
    k = 0;
    for (DWORD j=dwStartAttributeOffset; j<=dwEndAttributeOffset; j++)
    {
        lpszAttributeValue[k++] = m_lpData[j];
    }
    lpszAttributeValue[k] = '\0';

/*    DWORD dwValueOffset = offset + 1;
    k = 0;
    while ((dwValueOffset < m_dwDataSize) && (m_lpData[dwValueOffset] != XML_ESC) && (m_lpData[dwValueOffset] != XML_SOT) &&
        (m_lpData[dwValueOffset] != XML_EOT) && (m_lpData[dwValueOffset] != XML_PRT) && (m_lpData[dwValueOffset] != XML_SPT))
    {
        if ((m_lpData[dwValueOffset] != XML_SQT) && (m_lpData[dwValueOffset] != XML_DQT))
        {
            lpszAttributeValue[k++] = m_lpData[dwValueOffset];
        }
        dwValueOffset++;
    }
    lpszAttributeValue[k] = '\0';*/

    dwResult = dwValueOffset;
    return dwResult;
}


CXMLElement* CXMLFile::GetRoot()
{
    // Return XML file root element
    return m_lpXMLElement;
}


CXMLElement::CXMLElement(void)
{
    // Init members
    m_Type = XET_INVALID;
    m_lpszName = NULL;
    m_iChildNumber = 0;
    m_lpXMLChildArray = NULL;
    m_lpXMLChildPointer = NULL;
    m_lpXMLLastChild = NULL;
    m_lpszValue = NULL;
}

CXMLElement::~CXMLElement(void)
{
    // Destroy XML element
    Destroy();
}

void CXMLElement::Destroy()
{
    // Destroy child elements
    XMLElementArray *pPrev, *pCurr;
    pCurr = m_lpXMLChildArray;
    pPrev = NULL;
    while (pCurr != NULL)
    {
        pPrev = pCurr;
        pCurr = pCurr->pNext;
        delete pPrev->pCurrent;
        delete pPrev;
    }

    // Destroy XML element
    m_Type = XET_INVALID;
    if (m_lpszName != NULL)
    {
        free(m_lpszName);
        m_lpszName = NULL;
    }

    // Destroy value
    if (m_lpszValue != NULL)
    {
        free(m_lpszValue);
        m_lpszValue = NULL;
    }
}

void CXMLElement::Create(LPTSTR lpszElementName, XML_ELEMENT_TYPE type)
{
    // Check for valid XML element name
    if (lpszElementName != NULL)
    {
        // Create new XML element
        m_Type = type;
        int iNameLen = (int)_tcslen(lpszElementName);
        m_lpszName = (LPTSTR)malloc((iNameLen+1)*sizeof(_TCHAR));
        _tcsncpy(m_lpszName, lpszElementName, iNameLen);
        m_lpszName[iNameLen] = '\0';
    }
}

void CXMLElement::AppendChild(CXMLElement* lpXMLChild)
{
    // Check for valid XML child element
    if (lpXMLChild != NULL)
    {
        // Create new XMLElementArray element
        XMLElementArray* pNewXMLChild = new XMLElementArray;
        pNewXMLChild->pCurrent = lpXMLChild;
        pNewXMLChild->pNext = NULL;

        // Add new XML child element
        m_iChildNumber++;
        if (m_lpXMLChildArray == NULL)
        {
            // Add root element
            m_lpXMLChildArray = pNewXMLChild;
            m_lpXMLChildPointer = m_lpXMLChildArray;
            m_lpXMLLastChild = m_lpXMLChildArray;
        }
        else
        {
            // Append to last element
            m_lpXMLLastChild->pNext = pNewXMLChild;
            m_lpXMLLastChild = pNewXMLChild;
        }
    }
}

LPTSTR CXMLElement::GetElementName()
{
    // Return XML element name
    return m_lpszName;
}

XML_ELEMENT_TYPE CXMLElement::GetElementType()
{
    // Return XML element type
    return m_Type;
}

int CXMLElement::GetChildNumber()
{
    // Return XML element child number
    return m_iChildNumber;
}

CXMLElement* CXMLElement::GetFirstChild()
{
    CXMLElement* pResult = NULL;

    // Check for valid child collection pointer
    m_lpXMLChildPointer = m_lpXMLChildArray;
    if (m_lpXMLChildPointer != NULL)
    {
        // Return first XML element child collection element
        pResult = m_lpXMLChildPointer->pCurrent;
    }

    return pResult;
}

CXMLElement* CXMLElement::GetCurrentChild()
{
    CXMLElement* pResult = NULL;

    // Check for valid child collection pointer
    if (m_lpXMLChildPointer != NULL)
    {
        // Return current XML element child collection element
        pResult = m_lpXMLChildPointer->pCurrent;
    }

    return pResult;
}

CXMLElement* CXMLElement::GetNextChild()
{
    CXMLElement* pResult = NULL;

    // Check for valid child collection pointer
    if (m_lpXMLChildPointer != NULL)
    {
        // Get next XML element child collection element
        m_lpXMLChildPointer = m_lpXMLChildPointer->pNext;

        // Return XML element child collection next element
        if (m_lpXMLChildPointer != NULL)
        {
            pResult = m_lpXMLChildPointer->pCurrent;
        }
    }

    return pResult;
}

CXMLElement* CXMLElement::GetLastChild()
{
    CXMLElement* pResult = NULL;

    // Check for valid child collection pointer
    if (m_lpXMLLastChild != NULL)
    {
        // Get last XML element child collection element
        pResult = m_lpXMLLastChild->pCurrent;
    }

    return pResult;
}

void CXMLElement::SetValue(LPTSTR lpszValue)
{
    // Check for valie value
    if (lpszValue != NULL)
    {
        // Delete previous value
        if (m_lpszValue != NULL)
        {
            free(m_lpszValue);
            m_lpszValue = NULL;
        }

        // Set element value
        int iValueLen = (int)_tcslen(lpszValue);
        m_lpszValue = (LPTSTR)malloc((iValueLen+1)*sizeof(_TCHAR));
        _tcsncpy(m_lpszValue, lpszValue, iValueLen);
        m_lpszValue[iValueLen] = '\0';
    }
}

LPTSTR CXMLElement::GetValue()
{
    // Return element value
    return m_lpszValue;
}

#pragma once


#define FLG_DEF        0x0000        // Default flag
#define XML_SOT        '<'            // Start of tag symbol
#define FLG_SOT        0x0001        // Start of tag flag
#define XML_EOT        '>'            // End of tag symbol
#define FLG_EOT        0x0002        // End of tag flag
#define XML_PRT        '?'            // Prolog tag symbol
#define FLG_PRT        0x0004        // Prolog tag flag
#define XML_SPT        '!'            // Special tag symbol
#define FLG_SPT        0x0008        // Special tag flag
#define XML_ESC        ' '            // Escape symbol
#define FLG_ESC        0x0010        // Escape flag
#define XML_CLT        '/'            // Closing tag symbol
#define FLG_CLT        0x0020        // Closing tag flag
#define FLG_OPT        0x0040        // Opening tag flag
#define XML_ATR        '='            // Attribute symbol
#define FLG_ATR        0x0080        // Attribute flag
#define XML_SQT        '\''        // Single quote symbol
#define XML_DQT        '\"'        // Double quote symbol
#define MOD_DEF        0x0000        // Mode "Default"
#define MOD_APN        0x0001        // Mode "Append"
#define MOD_CLS        0x0002        // Mode "Close"


typedef enum _XML_ELEMENT_TYPE
{
    XET_INVALID = 0,
    XET_TAG,
    XET_ATTRIBUTE,
    XET_TEXT

} XML_ELEMENT_TYPE;


class CXMLElement
{
    typedef struct _XMLElementArray
    {
        CXMLElement* pCurrent;
        _XMLElementArray* pNext;

    } XMLElementArray;

public:
    // Public methods
    CXMLElement(void);
    virtual ~CXMLElement(void);
    void Create(LPTSTR lpszElementName, XML_ELEMENT_TYPE type);
    void AppendChild(CXMLElement* lpXMLChild);
    LPTSTR GetElementName();
    XML_ELEMENT_TYPE GetElementType();
    int GetChildNumber();
    CXMLElement* GetFirstChild();
    CXMLElement* GetCurrentChild();
    CXMLElement* GetNextChild();
    CXMLElement* GetLastChild();
    void SetValue(LPTSTR lpszValue);
    LPTSTR GetValue();

private:
    // Private methods
    void Destroy();

private:
    // Private members
    XML_ELEMENT_TYPE m_Type;
    LPTSTR m_lpszName;
    int m_iChildNumber;
    XMLElementArray* m_lpXMLChildArray;
    XMLElementArray* m_lpXMLChildPointer;
    XMLElementArray* m_lpXMLLastChild;
    LPTSTR m_lpszValue;
};


class CXMLFile
{
public:
    // Public methods
    CXMLFile(void);
    virtual ~CXMLFile(void);
    BOOL LoadFromFile(LPTSTR lpszXMLFilePath);
    BOOL LoadFromStream(LPBYTE lpData, DWORD dwDataSize);
    BOOL SaveToFile(LPTSTR lpszXMLFilePath);
    CXMLElement* GetRoot();

private:
    // Private methods
    void Unload();
    BOOL Parse();
    DWORD ExtractAttribute(DWORD offset, LPTSTR lpszAttributeName, LPTSTR lpszAttributeValue);
    BOOL ParseXMLElement(CXMLElement* lpXMLElement, DWORD dwStartOffset, DWORD dwEndOffset);
    BOOL SaveXMLElement(FILE* file, CXMLElement* lpXMLElement);

private:
    // Private members
    DWORD m_dwDataSize;
    BYTE* m_lpData;
    CXMLElement* m_lpXMLElement;
};