// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../include/fxcrt/fx_xml.h"
#include "xml_int.h"
CXML_Parser::~CXML_Parser()
{
    if (m_bOwnedStream) {
        m_pDataAcc->Release();
    }
}
FX_BOOL CXML_Parser::Init(FX_LPBYTE pBuffer, size_t size)
{
    if (m_pAllocator) {
        m_pDataAcc = FX_NewAtAllocator(m_pAllocator)CXML_DataBufAcc(pBuffer, size, m_pAllocator);
    } else {
        m_pDataAcc = FX_NEW CXML_DataBufAcc(pBuffer, size, NULL);
    }
    if (!m_pDataAcc) {
        return FALSE;
    }
    return Init(TRUE);
}
FX_BOOL CXML_Parser::Init(IFX_FileRead *pFileRead)
{
    if (m_pAllocator) {
        m_pDataAcc = FX_NewAtAllocator(m_pAllocator)CXML_DataStmAcc(pFileRead, m_pAllocator);
    } else {
        m_pDataAcc = FX_NEW CXML_DataStmAcc(pFileRead, NULL);
    }
    if (!m_pDataAcc) {
        return FALSE;
    }
    return Init(TRUE);
}
FX_BOOL CXML_Parser::Init(IFX_BufferRead *pBuffer)
{
    if (!pBuffer) {
        return FALSE;
    }
    m_pDataAcc = pBuffer;
    return Init(FALSE);
}
FX_BOOL CXML_Parser::Init(FX_BOOL bOwndedStream)
{
    m_bOwnedStream = bOwndedStream;
    m_nOffset = 0;
    return ReadNextBlock();
}
FX_BOOL CXML_Parser::ReadNextBlock()
{
    if (!m_pDataAcc->ReadNextBlock()) {
        return FALSE;
    }
    m_pBuffer = m_pDataAcc->GetBlockBuffer();
    m_dwBufferSize = m_pDataAcc->GetBlockSize();
    m_nBufferOffset = m_pDataAcc->GetBlockOffset();
    m_dwIndex = 0;
    return m_dwBufferSize > 0;
}
FX_BOOL CXML_Parser::IsEOF()
{
    if (!m_pDataAcc->IsEOF()) {
        return FALSE;
    }
    return m_dwIndex >= m_dwBufferSize;
}
#define FXCRTM_XML_CHARTYPE_Normal			0x00
#define FXCRTM_XML_CHARTYPE_SpaceChar		0x01
#define FXCRTM_XML_CHARTYPE_Letter			0x02
#define FXCRTM_XML_CHARTYPE_Digital			0x04
#define FXCRTM_XML_CHARTYPE_NameIntro		0x08
#define FXCRTM_XML_CHARTYPE_NameChar		0x10
#define FXCRTM_XML_CHARTYPE_HexDigital		0x20
#define FXCRTM_XML_CHARTYPE_HexLowerLetter	0x40
#define FXCRTM_XML_CHARTYPE_HexUpperLetter	0x60
#define FXCRTM_XML_CHARTYPE_HexChar			0x60
FX_BYTE g_FXCRT_XML_ByteTypes[256] = {
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
    0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x18,
    0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x01, 0x01,
};
FX_BOOL g_FXCRT_XML_IsWhiteSpace(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_SpaceChar) != 0;
}
FX_BOOL g_FXCRT_XML_IsLetter(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Letter) != 0;
}
FX_BOOL g_FXCRT_XML_IsDigital(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Digital) != 0;
}
FX_BOOL g_FXCRT_XML_IsNameIntro(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameIntro) != 0;
}
FX_BOOL g_FXCRT_XML_IsNameChar(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameChar) != 0;
}
FX_BOOL g_FXCRT_XML_IsHexChar(FX_BYTE ch)
{
    return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar) != 0;
}
void CXML_Parser::SkipWhiteSpaces()
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return;
    }
    do {
        while (m_dwIndex < m_dwBufferSize && g_FXCRT_XML_IsWhiteSpace(m_pBuffer[m_dwIndex])) {
            m_dwIndex ++;
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
}
void CXML_Parser::GetName(CFX_ByteStringL &space, CFX_ByteStringL &name)
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return;
    }
    CFX_ByteTextBuf buf(m_pAllocator);
    FX_BYTE ch;
    do {
        while (m_dwIndex < m_dwBufferSize) {
            ch = m_pBuffer[m_dwIndex];
            if (ch == ':') {
                buf.GetByteStringL(space);
                buf.Clear();
            } else if (g_FXCRT_XML_IsNameChar(ch)) {
                buf.AppendChar(ch);
            } else {
                break;
            }
            m_dwIndex ++;
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    buf.GetByteStringL(name);
}
void CXML_Parser::SkipLiterals(FX_BSTR str)
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return;
    }
    FX_INT32 i = 0, iLen = str.GetLength();
    do {
        while (m_dwIndex < m_dwBufferSize) {
            if (str.GetAt(i) != m_pBuffer[m_dwIndex ++]) {
                i = 0;
            } else {
                i ++;
                if (i == iLen) {
                    break;
                }
            }
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (i == iLen) {
            return;
        }
        if (m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    while (!m_pDataAcc->IsEOF()) {
        ReadNextBlock();
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwBufferSize;
    }
    m_dwIndex = m_dwBufferSize;
}
FX_DWORD CXML_Parser::GetCharRef()
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return 0;
    }
    FX_BYTE ch;
    FX_INT32 iState = 0;
    CFX_ByteTextBuf buf(m_pAllocator);
    FX_DWORD code = 0;
    do {
        while (m_dwIndex < m_dwBufferSize) {
            ch = m_pBuffer[m_dwIndex];
            switch (iState) {
                case 0:
                    if (ch == '#') {
                        m_dwIndex ++;
                        iState = 2;
                        break;
                    }
                    iState = 1;
                case 1:
                    m_dwIndex ++;
                    if (ch == ';') {
                        CFX_ByteStringC ref = buf.GetByteString();
                        if (ref == FX_BSTRC("gt")) {
                            code = '>';
                        } else if (ref == FX_BSTRC("lt")) {
                            code = '<';
                        } else if (ref == FX_BSTRC("amp")) {
                            code = '&';
                        } else if (ref == FX_BSTRC("apos")) {
                            code = '\'';
                        } else if (ref == FX_BSTRC("quot")) {
                            code = '"';
                        }
                        iState = 10;
                        break;
                    }
                    buf.AppendByte(ch);
                    break;
                case 2:
                    if (ch == 'x') {
                        m_dwIndex ++;
                        iState = 4;
                        break;
                    }
                    iState = 3;
                case 3:
                    m_dwIndex ++;
                    if (ch == ';') {
                        iState = 10;
                        break;
                    }
                    if (g_FXCRT_XML_IsDigital(ch)) {
                        code = code * 10 + ch - '0';
                    }
                    break;
                case 4:
                    m_dwIndex ++;
                    if (ch == ';') {
                        iState = 10;
                        break;
                    }
                    FX_BYTE nHex = g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar;
                    if (nHex) {
                        if (nHex == FXCRTM_XML_CHARTYPE_HexDigital) {
                            code = (code << 4) + ch - '0';
                        } else if (nHex == FXCRTM_XML_CHARTYPE_HexLowerLetter) {
                            code = (code << 4) + ch - 87;
                        } else {
                            code = (code << 4) + ch - 55;
                        }
                    }
                    break;
            }
            if (iState == 10) {
                break;
            }
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    return code;
}
void CXML_Parser::GetAttrValue(CFX_WideStringL &value)
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return;
    }
    CFX_UTF8Decoder decoder(m_pAllocator);
    FX_BYTE mark = 0, ch;
    do {
        while (m_dwIndex < m_dwBufferSize) {
            ch = m_pBuffer[m_dwIndex];
            if (mark == 0) {
                if (ch != '\'' && ch != '"') {
                    return;
                }
                mark = ch;
                m_dwIndex ++;
                ch = 0;
                continue;
            }
            m_dwIndex ++;
            if (ch == mark) {
                break;
            }
            if (ch == '&') {
                decoder.AppendChar(GetCharRef());
                if (IsEOF()) {
                    decoder.GetResult(value);
                    return;
                }
            } else {
                decoder.Input(ch);
            }
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (ch == mark || m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    decoder.GetResult(value);
}
void CXML_Parser::GetTagName(CFX_ByteStringL &space, CFX_ByteStringL &name, FX_BOOL &bEndTag, FX_BOOL bStartTag)
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return;
    }
    bEndTag = FALSE;
    FX_BYTE ch;
    FX_INT32 iState = bStartTag ? 1 : 0;
    do {
        while (m_dwIndex < m_dwBufferSize) {
            ch = m_pBuffer[m_dwIndex];
            switch (iState) {
                case 0:
                    m_dwIndex ++;
                    if (ch != '<') {
                        break;
                    }
                    iState = 1;
                    break;
                case 1:
                    if (ch == '?') {
                        m_dwIndex ++;
                        SkipLiterals(FX_BSTRC("?>"));
                        iState = 0;
                        break;
                    } else if (ch == '!') {
                        m_dwIndex ++;
                        SkipLiterals(FX_BSTRC("-->"));
                        iState = 0;
                        break;
                    }
                    if (ch == '/') {
                        m_dwIndex ++;
                        GetName(space, name);
                        bEndTag = TRUE;
                    } else {
                        GetName(space, name);
                        bEndTag = FALSE;
                    }
                    return;
            }
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
}
CXML_Element* CXML_Parser::ParseElement(CXML_Element* pParent, FX_BOOL bStartTag)
{
    m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
    if (IsEOF()) {
        return NULL;
    }
    CFX_ByteStringL tag_name, tag_space;
    FX_BOOL bEndTag;
    GetTagName(tag_space, tag_name, bEndTag, bStartTag);
    if (tag_name.IsEmpty() || bEndTag) {
        tag_space.Empty(m_pAllocator);
        return NULL;
    }
    CXML_Element* pElement;
    if (m_pAllocator) {
        pElement = FX_NewAtAllocator(m_pAllocator)CXML_Element(m_pAllocator);
    } else {
        pElement = FX_NEW CXML_Element;
    }
    if (pElement) {
        pElement->m_pParent = pParent;
        pElement->SetTag(tag_space, tag_name);
    }
    tag_space.Empty(m_pAllocator);
    tag_name.Empty(m_pAllocator);
    if (!pElement) {
        return NULL;
    }
    do {
        CFX_ByteStringL attr_space, attr_name;
        while (m_dwIndex < m_dwBufferSize) {
            SkipWhiteSpaces();
            if (IsEOF()) {
                break;
            }
            if (!g_FXCRT_XML_IsNameIntro(m_pBuffer[m_dwIndex])) {
                break;
            }
            attr_space.Empty(m_pAllocator);
            attr_name.Empty(m_pAllocator);
            GetName(attr_space, attr_name);
            SkipWhiteSpaces();
            if (IsEOF()) {
                break;
            }
            if (m_pBuffer[m_dwIndex] != '=') {
                break;
            }
            m_dwIndex ++;
            SkipWhiteSpaces();
            if (IsEOF()) {
                break;
            }
            CFX_WideStringL attr_value;
            GetAttrValue(attr_value);
            pElement->m_AttrMap.SetAt(attr_space, attr_name, attr_value, m_pAllocator);
            attr_value.Empty(m_pAllocator);
        }
        attr_space.Empty(m_pAllocator);
        attr_name.Empty(m_pAllocator);
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    SkipWhiteSpaces();
    if (IsEOF()) {
        return pElement;
    }
    FX_BYTE ch = m_pBuffer[m_dwIndex ++];
    if (ch == '/') {
        m_dwIndex ++;
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        return pElement;
    }
    if (ch != '>') {
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (m_pAllocator) {
            FX_DeleteAtAllocator(pElement, m_pAllocator, CXML_Element);
        } else {
            delete pElement;
        }
        return NULL;
    }
    SkipWhiteSpaces();
    if (IsEOF()) {
        return pElement;
    }
    CFX_UTF8Decoder decoder(m_pAllocator);
    CFX_WideTextBuf content(m_pAllocator);
    FX_BOOL bCDATA = FALSE;
    FX_INT32 iState = 0;
    do {
        while (m_dwIndex < m_dwBufferSize) {
            ch = m_pBuffer[m_dwIndex ++];
            switch (iState) {
                case 0:
                    if (ch == '<') {
                        iState = 1;
                    } else if (ch == '&') {
                        decoder.ClearStatus();
                        decoder.AppendChar(GetCharRef());
                    } else {
                        decoder.Input(ch);
                    }
                    break;
                case 1:
                    if (ch == '!') {
                        iState = 2;
                    } else if (ch == '?') {
                        SkipLiterals(FX_BSTRC("?>"));
                        SkipWhiteSpaces();
                        iState = 0;
                    } else if (ch == '/') {
                        CFX_ByteStringL space, name;
                        GetName(space, name);
                        space.Empty(m_pAllocator);
                        name.Empty(m_pAllocator);
                        SkipWhiteSpaces();
                        m_dwIndex ++;
                        iState = 10;
                    } else {
                        content << decoder.GetResult();
                        CFX_WideStringL dataStr;
                        content.GetWideStringL(dataStr);
                        if (!bCDATA && !m_bSaveSpaceChars) {
                            dataStr.TrimRight((FX_LPCWSTR)L" \t\r\n");
                        }
                        InsertContentSegment(bCDATA, dataStr, pElement);
                        dataStr.Empty(m_pAllocator);
                        content.Clear();
                        decoder.Clear();
                        bCDATA = FALSE;
                        iState = 0;
                        m_dwIndex --;
                        CXML_Element* pSubElement = ParseElement(pElement, TRUE);
                        if (pSubElement == NULL) {
                            break;
                        }
                        pSubElement->m_pParent = pElement;
                        pElement->m_Children.Add((FX_LPVOID)CXML_Element::Element);
                        pElement->m_Children.Add(pSubElement);
                        SkipWhiteSpaces();
                    }
                    break;
                case 2:
                    if (ch == '[') {
                        SkipLiterals(FX_BSTRC("]]>"));
                    } else if (ch == '-') {
                        m_dwIndex ++;
                        SkipLiterals(FX_BSTRC("-->"));
                    } else {
                        SkipLiterals(FX_BSTRC(">"));
                    }
                    decoder.Clear();
                    SkipWhiteSpaces();
                    iState = 0;
                    break;
            }
            if (iState == 10) {
                break;
            }
        }
        m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
        if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
            break;
        }
    } while (ReadNextBlock());
    content << decoder.GetResult();
    CFX_WideStringL dataStr;
    content.GetWideStringL(dataStr);
    if (!m_bSaveSpaceChars) {
        dataStr.TrimRight((FX_LPCWSTR)L" \t\r\n");
    }
    InsertContentSegment(bCDATA, dataStr, pElement);
    dataStr.Empty(m_pAllocator);
    content.Clear();
    decoder.Clear();
    bCDATA = FALSE;
    return pElement;
}
void CXML_Parser::InsertContentSegment(FX_BOOL bCDATA, FX_WSTR content, CXML_Element* pElement)
{
    if (content.IsEmpty()) {
        return;
    }
    CXML_Content* pContent;
    if (m_pAllocator) {
        pContent = FX_NewAtAllocator(m_pAllocator)CXML_Content;
    } else {
        pContent = FX_NEW CXML_Content;
    }
    if (!pContent) {
        return;
    }
    pContent->Set(bCDATA, content, m_pAllocator);
    pElement->m_Children.Add((FX_LPVOID)CXML_Element::Content);
    pElement->m_Children.Add(pContent);
}
static CXML_Element* XML_ContinueParse(CXML_Parser &parser, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize)
{
    parser.m_bSaveSpaceChars = bSaveSpaceChars;
    CXML_Element* pElement = parser.ParseElement(NULL, FALSE);
    if (pParsedSize) {
        *pParsedSize = parser.m_nOffset;
    }
    return pElement;
}
CXML_Element* CXML_Element::Parse(const void* pBuffer, size_t size, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize, IFX_Allocator* pAllocator)
{
    CXML_Parser parser(pAllocator);
    if (!parser.Init((FX_LPBYTE)pBuffer, size)) {
        return NULL;
    }
    return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
}
CXML_Element* CXML_Element::Parse(IFX_FileRead *pFile, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize, IFX_Allocator* pAllocator)
{
    CXML_Parser parser(pAllocator);
    if (!parser.Init(pFile)) {
        return NULL;
    }
    return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
}
CXML_Element* CXML_Element::Parse(IFX_BufferRead *pBuffer, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize, IFX_Allocator* pAllocator)
{
    CXML_Parser parser(pAllocator);
    if (!parser.Init(pBuffer)) {
        return NULL;
    }
    return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
}
CXML_Element::CXML_Element(IFX_Allocator* pAllocator)
    : m_pParent(NULL)
    , m_QSpaceName()
    , m_TagName()
    , m_AttrMap()
    , m_Children(pAllocator)
{
}
CXML_Element::CXML_Element(FX_BSTR qSpace, FX_BSTR tagName, IFX_Allocator* pAllocator)
    : m_pParent(NULL)
    , m_QSpaceName()
    , m_TagName()
    , m_AttrMap()
    , m_Children(pAllocator)
{
    m_QSpaceName.Set(qSpace, pAllocator);
    m_TagName.Set(tagName, pAllocator);
}
CXML_Element::CXML_Element(FX_BSTR qTagName, IFX_Allocator* pAllocator)
    : m_pParent(NULL)
    , m_QSpaceName()
    , m_TagName()
    , m_AttrMap()
    , m_Children(pAllocator)
{
    SetTag(qTagName);
}
CXML_Element::~CXML_Element()
{
    Empty();
}
void CXML_Element::Empty()
{
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    m_QSpaceName.Empty(pAllocator);
    m_TagName.Empty(pAllocator);
    m_AttrMap.RemoveAll(pAllocator);
    RemoveChildren();
}
void CXML_Element::RemoveChildren()
{
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    for (int i = 0; i < m_Children.GetSize(); i += 2) {
        ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
        if (type == Content) {
            CXML_Content* content = (CXML_Content*)m_Children.GetAt(i + 1);
            if (pAllocator) {
                FX_DeleteAtAllocator(content, pAllocator, CXML_Content);
            } else {
                delete content;
            }
        } else if (type == Element) {
            CXML_Element* child = (CXML_Element*)m_Children.GetAt(i + 1);
            child->RemoveChildren();
            if (pAllocator) {
                FX_DeleteAtAllocator(child, pAllocator, CXML_Element);
            } else {
                delete child;
            }
        }
    }
    m_Children.RemoveAll();
}
CFX_ByteString CXML_Element::GetTagName(FX_BOOL bQualified) const
{
    if (!bQualified || m_QSpaceName.IsEmpty()) {
        return m_TagName;
    }
    CFX_ByteString bsTag = m_QSpaceName;
    bsTag += ":";
    bsTag += m_TagName;
    return bsTag;
}
void CXML_Element::GetTagName(CFX_ByteStringL &tagName, FX_BOOL bQualified) const
{
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    if (!bQualified || m_QSpaceName.IsEmpty()) {
        tagName.Set(m_TagName, pAllocator);
        return;
    }
    FX_LPSTR str = tagName.AllocBuffer(m_QSpaceName.GetLength() + m_TagName.GetLength() + 2, pAllocator);
    if (!str) {
        return;
    }
    FXSYS_memcpy32(str, m_QSpaceName.GetCStr(), m_QSpaceName.GetLength());
    str += m_QSpaceName.GetLength();
    *str = ':';
    str ++;
    FXSYS_memcpy32(str, m_TagName.GetCStr(), m_TagName.GetLength());
    str += m_TagName.GetLength();
    *str = '\0';
}
CFX_ByteString CXML_Element::GetNamespace(FX_BOOL bQualified) const
{
    if (bQualified) {
        return m_QSpaceName;
    }
    return GetNamespaceURI(m_QSpaceName);
}
void CXML_Element::GetNamespace(CFX_ByteStringL &nameSpace, FX_BOOL bQualified) const
{
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    if (bQualified) {
        nameSpace.Set(m_QSpaceName, pAllocator);
        return;
    }
    GetNamespaceURI(m_QSpaceName, nameSpace);
}
CFX_ByteString CXML_Element::GetNamespaceURI(FX_BSTR qName) const
{
    const CFX_WideStringL* pwsSpace;
    const CXML_Element *pElement = this;
    do {
        if (qName.IsEmpty()) {
            pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC(""), FX_BSTRC("xmlns"));
        } else {
            pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC("xmlns"), qName);
        }
        if (pwsSpace) {
            break;
        }
        pElement = pElement->GetParent();
    } while(pElement);
    return pwsSpace ? FX_UTF8Encode(*pwsSpace) : CFX_ByteString();
}
void CXML_Element::GetNamespaceURI(FX_BSTR qName, CFX_ByteStringL &uri) const
{
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    const CFX_WideStringL* pwsSpace;
    const CXML_Element *pElement = this;
    do {
        if (qName.IsEmpty()) {
            pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC(""), FX_BSTRC("xmlns"));
        } else {
            pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC("xmlns"), qName);
        }
        if (pwsSpace) {
            break;
        }
        pElement = pElement->GetParent();
    } while(pElement);
    if (pwsSpace) {
        FX_UTF8Encode(pwsSpace->GetPtr(), pwsSpace->GetLength(), uri, pAllocator);
    }
}
void CXML_Element::GetAttrByIndex(int index, CFX_ByteString& space, CFX_ByteString& name, CFX_WideString& value) const
{
    if (index < 0 || index >= m_AttrMap.GetSize()) {
        return;
    }
    CXML_AttrItem& item = m_AttrMap.GetAt(index);
    space = item.m_QSpaceName;
    name = item.m_AttrName;
    value = item.m_Value;
}
void CXML_Element::GetAttrByIndex(int index, CFX_ByteStringL &space, CFX_ByteStringL &name, CFX_WideStringL &value) const
{
    if (index < 0 || index >= m_AttrMap.GetSize()) {
        return;
    }
    IFX_Allocator* pAllocator = m_Children.m_pAllocator;
    CXML_AttrItem& item = m_AttrMap.GetAt(index);
    space.Set(item.m_QSpaceName, pAllocator);
    name.Set(item.m_AttrName, pAllocator);
    value.Set(item.m_Value, pAllocator);
}
FX_BOOL CXML_Element::HasAttr(FX_BSTR name) const
{
    CFX_ByteStringC bsSpace, bsName;
    FX_XML_SplitQualifiedName(name, bsSpace, bsName);
    return m_AttrMap.Lookup(bsSpace, bsName) != NULL;
}
FX_BOOL CXML_Element::GetAttrValue(FX_BSTR name, CFX_WideString& attribute) const
{
    CFX_ByteStringC bsSpace, bsName;
    FX_XML_SplitQualifiedName(name, bsSpace, bsName);
    const CFX_WideStringL* pValue = m_AttrMap.Lookup(bsSpace, bsName);
    if (pValue) {
        attribute = CFX_WideString(pValue->GetPtr(), pValue->GetLength());
        return TRUE;
    }
    return FALSE;
}
const CFX_WideStringL* CXML_Element::GetAttrValuePtr(FX_BSTR name) const
{
    CFX_ByteStringC bsSpace, bsName;
    FX_XML_SplitQualifiedName(name, bsSpace, bsName);
    return m_AttrMap.Lookup(bsSpace, bsName);
}
FX_BOOL CXML_Element::GetAttrValue(FX_BSTR space, FX_BSTR name, CFX_WideString& attribute) const
{
    const CFX_WideStringL* pValue = m_AttrMap.Lookup(space, name);
    if (pValue) {
        attribute = CFX_WideString(pValue->GetPtr(), pValue->GetLength());
        return TRUE;
    }
    return FALSE;
}
const CFX_WideStringL* CXML_Element::GetAttrValuePtr(FX_BSTR space, FX_BSTR name) const
{
    return m_AttrMap.Lookup(space, name);
}
FX_BOOL CXML_Element::GetAttrInteger(FX_BSTR name, int& attribute) const
{
    CFX_ByteStringC bsSpace, bsName;
    FX_XML_SplitQualifiedName(name, bsSpace, bsName);
    const CFX_WideStringL* pwsValue = m_AttrMap.Lookup(bsSpace, bsName);
    if (pwsValue) {
        attribute = pwsValue->GetInteger();
        return TRUE;
    }
    return FALSE;
}
FX_BOOL	CXML_Element::GetAttrInteger(FX_BSTR space, FX_BSTR name, int& attribute) const
{
    const CFX_WideStringL* pwsValue = m_AttrMap.Lookup(space, name);
    if (pwsValue) {
        attribute = pwsValue->GetInteger();
        return TRUE;
    }
    return FALSE;
}
FX_BOOL CXML_Element::GetAttrFloat(FX_BSTR name, FX_FLOAT& attribute) const
{
    CFX_ByteStringC bsSpace, bsName;
    FX_XML_SplitQualifiedName(name, bsSpace, bsName);
    return GetAttrFloat(bsSpace, bsName, attribute);
}
FX_BOOL CXML_Element::GetAttrFloat(FX_BSTR space, FX_BSTR name, FX_FLOAT& attribute) const
{
    CFX_WideString value;
    const CFX_WideStringL* pValue = m_AttrMap.Lookup(space, name);
    if (pValue) {
        attribute = pValue->GetFloat();
        return TRUE;
    }
    return FALSE;
}
FX_DWORD CXML_Element::CountChildren() const
{
    return m_Children.GetSize() / 2;
}
CXML_Element::ChildType CXML_Element::GetChildType(FX_DWORD index) const
{
    index <<= 1;
    if (index >= (FX_DWORD)m_Children.GetSize()) {
        return Invalid;
    }
    return (ChildType)(FX_UINTPTR)m_Children.GetAt(index);
}
CFX_WideString CXML_Element::GetContent(FX_DWORD index) const
{
    index <<= 1;
    if (index >= (FX_DWORD)m_Children.GetSize() ||
            (ChildType)(FX_UINTPTR)m_Children.GetAt(index) != Content) {
        return CFX_WideString();
    }
    CXML_Content* pContent = (CXML_Content*)m_Children.GetAt(index + 1);
    if (pContent) {
        return pContent->m_Content;
    }
    return CFX_WideString();
}
const CFX_WideStringL* CXML_Element::GetContentPtr(FX_DWORD index) const
{
    index <<= 1;
    if (index >= (FX_DWORD)m_Children.GetSize() ||
            (ChildType)(FX_UINTPTR)m_Children.GetAt(index) != Content) {
        return NULL;
    }
    CXML_Content* pContent = (CXML_Content*)m_Children.GetAt(index + 1);
    if (pContent) {
        return &pContent->m_Content;
    }
    return NULL;
}
CXML_Element* CXML_Element::GetElement(FX_DWORD index) const
{
    index <<= 1;
    if (index >= (FX_DWORD)m_Children.GetSize() ||
            (ChildType)(FX_UINTPTR)m_Children.GetAt(index) != Element) {
        return NULL;
    }
    return (CXML_Element*)m_Children.GetAt(index + 1);
}
FX_DWORD CXML_Element::CountElements(FX_BSTR space, FX_BSTR tag) const
{
    int count = 0;
    for (int i = 0; i < m_Children.GetSize(); i += 2) {
        ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
        if (type != Element) {
            continue;
        }
        CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
        if ((space.IsEmpty() || pKid->m_QSpaceName == space) && pKid->m_TagName == tag) {
            count ++;
        }
    }
    return count;
}
CXML_Element* CXML_Element::GetElement(FX_BSTR space, FX_BSTR tag, int index) const
{
    if (index < 0) {
        return NULL;
    }
    for (int i = 0; i < m_Children.GetSize(); i += 2) {
        ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
        if (type != Element) {
            continue;
        }
        CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
        if ((!space.IsEmpty() && pKid->m_QSpaceName != space) || pKid->m_TagName != tag) {
            continue;
        }
        if (index -- == 0) {
            return pKid;
        }
    }
    return NULL;
}
FX_DWORD CXML_Element::FindElement(CXML_Element *pChild) const
{
    for (int i = 0; i < m_Children.GetSize(); i += 2) {
        if ((ChildType)(FX_UINTPTR)m_Children.GetAt(i) == Element &&
                (CXML_Element*)m_Children.GetAt(i + 1) == pChild) {
            return (FX_DWORD)(i >> 1);
        }
    }
    return (FX_DWORD) - 1;
}
const CFX_WideStringL* CXML_AttrMap::Lookup(FX_BSTR space, FX_BSTR name) const
{
    if (m_pMap == NULL) {
        return NULL;
    }
    for (int i = 0; i < m_pMap->GetSize(); i ++) {
        CXML_AttrItem& item = GetAt(i);
        if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
            return &item.m_Value;
        }
    }
    return NULL;
}
void CXML_AttrMap::SetAt(FX_BSTR space, FX_BSTR name, FX_WSTR value, IFX_Allocator* pAllocator)
{
    for (int i = 0; i < GetSize(); i ++) {
        CXML_AttrItem& item = GetAt(i);
        if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
            item.m_Value.Set(value, pAllocator);
            return;
        }
    }
    if (!m_pMap) {
        if (pAllocator) {
            m_pMap = FX_NewAtAllocator(pAllocator)CFX_ObjectArray<CXML_AttrItem>(pAllocator);
        } else {
            m_pMap = FX_NEW CFX_ObjectArray<CXML_AttrItem>;
        }
    }
    if (!m_pMap) {
        return;
    }
    CXML_AttrItem* pItem = (CXML_AttrItem*)m_pMap->AddSpace();
    if (!pItem) {
        return;
    }
    pItem->m_QSpaceName.Set(space, pAllocator);
    pItem->m_AttrName.Set(name, pAllocator);
    pItem->m_Value.Set(value, pAllocator);
}
void CXML_AttrMap::RemoveAt(FX_BSTR space, FX_BSTR name, IFX_Allocator* pAllocator)
{
    if (m_pMap == NULL) {
        return;
    }
    for (int i = 0; i < m_pMap->GetSize(); i ++) {
        CXML_AttrItem& item = GetAt(i);
        if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
            item.Empty(pAllocator);
            m_pMap->RemoveAt(i);
            return;
        }
    }
}
int CXML_AttrMap::GetSize() const
{
    return m_pMap == NULL ? 0 : m_pMap->GetSize();
}
CXML_AttrItem& CXML_AttrMap::GetAt(int index) const
{
    ASSERT(m_pMap != NULL);
    return (*m_pMap)[index];
}
void CXML_AttrMap::RemoveAll(IFX_Allocator* pAllocator)
{
    if (!m_pMap) {
        return;
    }
    for (int i = 0; i < m_pMap->GetSize(); i ++) {
        CXML_AttrItem& item = (*m_pMap)[i];
        item.Empty(pAllocator);
    }
    m_pMap->RemoveAll();
    if (pAllocator) {
        FX_DeleteAtAllocator(m_pMap, pAllocator, CFX_ObjectArray<CXML_AttrItem>);
    } else {
        delete m_pMap;
    }
    m_pMap = NULL;
}