Remove a few unused variables, functions, and tables.
[pdfium.git] / core / src / fxcrt / fx_xml_parser.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "../../include/fxcrt/fx_xml.h"
8 #include "xml_int.h"
9 CXML_Parser::~CXML_Parser()
10 {
11     if (m_bOwnedStream) {
12         m_pDataAcc->Release();
13     }
14 }
15 FX_BOOL CXML_Parser::Init(FX_LPBYTE pBuffer, size_t size)
16 {
17     m_pDataAcc = FX_NEW CXML_DataBufAcc(pBuffer, size);
18     if (!m_pDataAcc) {
19         return FALSE;
20     }
21     return Init(TRUE);
22 }
23 FX_BOOL CXML_Parser::Init(IFX_FileRead *pFileRead)
24 {
25     m_pDataAcc = FX_NEW CXML_DataStmAcc(pFileRead);
26     if (!m_pDataAcc) {
27         return FALSE;
28     }
29     return Init(TRUE);
30 }
31 FX_BOOL CXML_Parser::Init(IFX_BufferRead *pBuffer)
32 {
33     if (!pBuffer) {
34         return FALSE;
35     }
36     m_pDataAcc = pBuffer;
37     return Init(FALSE);
38 }
39 FX_BOOL CXML_Parser::Init(FX_BOOL bOwndedStream)
40 {
41     m_bOwnedStream = bOwndedStream;
42     m_nOffset = 0;
43     return ReadNextBlock();
44 }
45 FX_BOOL CXML_Parser::ReadNextBlock()
46 {
47     if (!m_pDataAcc->ReadNextBlock()) {
48         return FALSE;
49     }
50     m_pBuffer = m_pDataAcc->GetBlockBuffer();
51     m_dwBufferSize = m_pDataAcc->GetBlockSize();
52     m_nBufferOffset = m_pDataAcc->GetBlockOffset();
53     m_dwIndex = 0;
54     return m_dwBufferSize > 0;
55 }
56 FX_BOOL CXML_Parser::IsEOF()
57 {
58     if (!m_pDataAcc->IsEOF()) {
59         return FALSE;
60     }
61     return m_dwIndex >= m_dwBufferSize;
62 }
63 #define FXCRTM_XML_CHARTYPE_Normal                      0x00
64 #define FXCRTM_XML_CHARTYPE_SpaceChar           0x01
65 #define FXCRTM_XML_CHARTYPE_Letter                      0x02
66 #define FXCRTM_XML_CHARTYPE_Digital                     0x04
67 #define FXCRTM_XML_CHARTYPE_NameIntro           0x08
68 #define FXCRTM_XML_CHARTYPE_NameChar            0x10
69 #define FXCRTM_XML_CHARTYPE_HexDigital          0x20
70 #define FXCRTM_XML_CHARTYPE_HexLowerLetter      0x40
71 #define FXCRTM_XML_CHARTYPE_HexUpperLetter      0x60
72 #define FXCRTM_XML_CHARTYPE_HexChar                     0x60
73 FX_BYTE g_FXCRT_XML_ByteTypes[256] = {
74     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
75     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
76     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
77     0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
78     0x00, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
79     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x18,
80     0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
81     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00,
82     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
83     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
84     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
85     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
86     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
87     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
88     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
89     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x01, 0x01,
90 };
91 FX_BOOL g_FXCRT_XML_IsWhiteSpace(FX_BYTE ch)
92 {
93     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_SpaceChar) != 0;
94 }
95 FX_BOOL g_FXCRT_XML_IsLetter(FX_BYTE ch)
96 {
97     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Letter) != 0;
98 }
99 FX_BOOL g_FXCRT_XML_IsDigital(FX_BYTE ch)
100 {
101     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Digital) != 0;
102 }
103 FX_BOOL g_FXCRT_XML_IsNameIntro(FX_BYTE ch)
104 {
105     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameIntro) != 0;
106 }
107 FX_BOOL g_FXCRT_XML_IsNameChar(FX_BYTE ch)
108 {
109     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameChar) != 0;
110 }
111 FX_BOOL g_FXCRT_XML_IsHexChar(FX_BYTE ch)
112 {
113     return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar) != 0;
114 }
115 void CXML_Parser::SkipWhiteSpaces()
116 {
117     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
118     if (IsEOF()) {
119         return;
120     }
121     do {
122         while (m_dwIndex < m_dwBufferSize && g_FXCRT_XML_IsWhiteSpace(m_pBuffer[m_dwIndex])) {
123             m_dwIndex ++;
124         }
125         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
126         if (m_dwIndex < m_dwBufferSize || IsEOF()) {
127             break;
128         }
129     } while (ReadNextBlock());
130 }
131 void CXML_Parser::GetName(CFX_ByteString &space, CFX_ByteString &name)
132 {
133     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
134     if (IsEOF()) {
135         return;
136     }
137     CFX_ByteTextBuf buf;
138     FX_BYTE ch;
139     do {
140         while (m_dwIndex < m_dwBufferSize) {
141             ch = m_pBuffer[m_dwIndex];
142             if (ch == ':') {
143                 space = buf.GetByteString();
144                 buf.Clear();
145             } else if (g_FXCRT_XML_IsNameChar(ch)) {
146                 buf.AppendChar(ch);
147             } else {
148                 break;
149             }
150             m_dwIndex ++;
151         }
152         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
153         if (m_dwIndex < m_dwBufferSize || IsEOF()) {
154             break;
155         }
156     } while (ReadNextBlock());
157     name = buf.GetByteString();
158 }
159 void CXML_Parser::SkipLiterals(FX_BSTR str)
160 {
161     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
162     if (IsEOF()) {
163         return;
164     }
165     FX_INT32 i = 0, iLen = str.GetLength();
166     do {
167         while (m_dwIndex < m_dwBufferSize) {
168             if (str.GetAt(i) != m_pBuffer[m_dwIndex ++]) {
169                 i = 0;
170             } else {
171                 i ++;
172                 if (i == iLen) {
173                     break;
174                 }
175             }
176         }
177         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
178         if (i == iLen) {
179             return;
180         }
181         if (m_dwIndex < m_dwBufferSize || IsEOF()) {
182             break;
183         }
184     } while (ReadNextBlock());
185     while (!m_pDataAcc->IsEOF()) {
186         ReadNextBlock();
187         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwBufferSize;
188     }
189     m_dwIndex = m_dwBufferSize;
190 }
191 FX_DWORD CXML_Parser::GetCharRef()
192 {
193     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
194     if (IsEOF()) {
195         return 0;
196     }
197     FX_BYTE ch;
198     FX_INT32 iState = 0;
199     CFX_ByteTextBuf buf;
200     FX_DWORD code = 0;
201     do {
202         while (m_dwIndex < m_dwBufferSize) {
203             ch = m_pBuffer[m_dwIndex];
204             switch (iState) {
205                 case 0:
206                     if (ch == '#') {
207                         m_dwIndex ++;
208                         iState = 2;
209                         break;
210                     }
211                     iState = 1;
212                 case 1:
213                     m_dwIndex ++;
214                     if (ch == ';') {
215                         CFX_ByteStringC ref = buf.GetByteString();
216                         if (ref == FX_BSTRC("gt")) {
217                             code = '>';
218                         } else if (ref == FX_BSTRC("lt")) {
219                             code = '<';
220                         } else if (ref == FX_BSTRC("amp")) {
221                             code = '&';
222                         } else if (ref == FX_BSTRC("apos")) {
223                             code = '\'';
224                         } else if (ref == FX_BSTRC("quot")) {
225                             code = '"';
226                         }
227                         iState = 10;
228                         break;
229                     }
230                     buf.AppendByte(ch);
231                     break;
232                 case 2:
233                     if (ch == 'x') {
234                         m_dwIndex ++;
235                         iState = 4;
236                         break;
237                     }
238                     iState = 3;
239                 case 3:
240                     m_dwIndex ++;
241                     if (ch == ';') {
242                         iState = 10;
243                         break;
244                     }
245                     if (g_FXCRT_XML_IsDigital(ch)) {
246                         code = code * 10 + ch - '0';
247                     }
248                     break;
249                 case 4:
250                     m_dwIndex ++;
251                     if (ch == ';') {
252                         iState = 10;
253                         break;
254                     }
255                     FX_BYTE nHex = g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar;
256                     if (nHex) {
257                         if (nHex == FXCRTM_XML_CHARTYPE_HexDigital) {
258                             code = (code << 4) + ch - '0';
259                         } else if (nHex == FXCRTM_XML_CHARTYPE_HexLowerLetter) {
260                             code = (code << 4) + ch - 87;
261                         } else {
262                             code = (code << 4) + ch - 55;
263                         }
264                     }
265                     break;
266             }
267             if (iState == 10) {
268                 break;
269             }
270         }
271         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
272         if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
273             break;
274         }
275     } while (ReadNextBlock());
276     return code;
277 }
278 void CXML_Parser::GetAttrValue(CFX_WideString &value)
279 {
280     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
281     if (IsEOF()) {
282         return;
283     }
284     CFX_UTF8Decoder decoder;
285     FX_BYTE mark = 0, ch;
286     do {
287         while (m_dwIndex < m_dwBufferSize) {
288             ch = m_pBuffer[m_dwIndex];
289             if (mark == 0) {
290                 if (ch != '\'' && ch != '"') {
291                     return;
292                 }
293                 mark = ch;
294                 m_dwIndex ++;
295                 ch = 0;
296                 continue;
297             }
298             m_dwIndex ++;
299             if (ch == mark) {
300                 break;
301             }
302             if (ch == '&') {
303                 decoder.AppendChar(GetCharRef());
304                 if (IsEOF()) {
305                     value = decoder.GetResult();
306                     return;
307                 }
308             } else {
309                 decoder.Input(ch);
310             }
311         }
312         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
313         if (ch == mark || m_dwIndex < m_dwBufferSize || IsEOF()) {
314             break;
315         }
316     } while (ReadNextBlock());
317     value = decoder.GetResult();
318 }
319 void CXML_Parser::GetTagName(CFX_ByteString &space, CFX_ByteString &name, FX_BOOL &bEndTag, FX_BOOL bStartTag)
320 {
321     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
322     if (IsEOF()) {
323         return;
324     }
325     bEndTag = FALSE;
326     FX_BYTE ch;
327     FX_INT32 iState = bStartTag ? 1 : 0;
328     do {
329         while (m_dwIndex < m_dwBufferSize) {
330             ch = m_pBuffer[m_dwIndex];
331             switch (iState) {
332                 case 0:
333                     m_dwIndex ++;
334                     if (ch != '<') {
335                         break;
336                     }
337                     iState = 1;
338                     break;
339                 case 1:
340                     if (ch == '?') {
341                         m_dwIndex ++;
342                         SkipLiterals(FX_BSTRC("?>"));
343                         iState = 0;
344                         break;
345                     } else if (ch == '!') {
346                         m_dwIndex ++;
347                         SkipLiterals(FX_BSTRC("-->"));
348                         iState = 0;
349                         break;
350                     }
351                     if (ch == '/') {
352                         m_dwIndex ++;
353                         GetName(space, name);
354                         bEndTag = TRUE;
355                     } else {
356                         GetName(space, name);
357                         bEndTag = FALSE;
358                     }
359                     return;
360             }
361         }
362         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
363         if (m_dwIndex < m_dwBufferSize || IsEOF()) {
364             break;
365         }
366     } while (ReadNextBlock());
367 }
368 CXML_Element* CXML_Parser::ParseElement(CXML_Element* pParent, FX_BOOL bStartTag)
369 {
370     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
371     if (IsEOF()) {
372         return NULL;
373     }
374     CFX_ByteString tag_name, tag_space;
375     FX_BOOL bEndTag;
376     GetTagName(tag_space, tag_name, bEndTag, bStartTag);
377     if (tag_name.IsEmpty() || bEndTag) {
378         return NULL;
379     }
380     CXML_Element* pElement;
381     pElement = FX_NEW CXML_Element;
382     if (pElement) {
383         pElement->m_pParent = pParent;
384         pElement->SetTag(tag_space, tag_name);
385     }
386     if (!pElement) {
387         return NULL;
388     }
389     do {
390         CFX_ByteString attr_space, attr_name;
391         while (m_dwIndex < m_dwBufferSize) {
392             SkipWhiteSpaces();
393             if (IsEOF()) {
394                 break;
395             }
396             if (!g_FXCRT_XML_IsNameIntro(m_pBuffer[m_dwIndex])) {
397                 break;
398             }
399             GetName(attr_space, attr_name);
400             SkipWhiteSpaces();
401             if (IsEOF()) {
402                 break;
403             }
404             if (m_pBuffer[m_dwIndex] != '=') {
405                 break;
406             }
407             m_dwIndex ++;
408             SkipWhiteSpaces();
409             if (IsEOF()) {
410                 break;
411             }
412             CFX_WideString attr_value;
413             GetAttrValue(attr_value);
414             pElement->m_AttrMap.SetAt(attr_space, attr_name, attr_value);
415         }
416         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
417         if (m_dwIndex < m_dwBufferSize || IsEOF()) {
418             break;
419         }
420     } while (ReadNextBlock());
421     SkipWhiteSpaces();
422     if (IsEOF()) {
423         return pElement;
424     }
425     FX_BYTE ch = m_pBuffer[m_dwIndex ++];
426     if (ch == '/') {
427         m_dwIndex ++;
428         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
429         return pElement;
430     }
431     if (ch != '>') {
432         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
433         delete pElement;
434         return NULL;
435     }
436     SkipWhiteSpaces();
437     if (IsEOF()) {
438         return pElement;
439     }
440     CFX_UTF8Decoder decoder;
441     CFX_WideTextBuf content;
442     FX_BOOL bCDATA = FALSE;
443     FX_INT32 iState = 0;
444     do {
445         while (m_dwIndex < m_dwBufferSize) {
446             ch = m_pBuffer[m_dwIndex ++];
447             switch (iState) {
448                 case 0:
449                     if (ch == '<') {
450                         iState = 1;
451                     } else if (ch == '&') {
452                         decoder.ClearStatus();
453                         decoder.AppendChar(GetCharRef());
454                     } else {
455                         decoder.Input(ch);
456                     }
457                     break;
458                 case 1:
459                     if (ch == '!') {
460                         iState = 2;
461                     } else if (ch == '?') {
462                         SkipLiterals(FX_BSTRC("?>"));
463                         SkipWhiteSpaces();
464                         iState = 0;
465                     } else if (ch == '/') {
466                         CFX_ByteString space, name;
467                         GetName(space, name);
468                         SkipWhiteSpaces();
469                         m_dwIndex ++;
470                         iState = 10;
471                     } else {
472                         content << decoder.GetResult();
473                         CFX_WideString dataStr = content.GetWideString();
474                         if (!bCDATA && !m_bSaveSpaceChars) {
475                             dataStr.TrimRight((FX_LPCWSTR)L" \t\r\n");
476                         }
477                         InsertContentSegment(bCDATA, dataStr, pElement);
478                         content.Clear();
479                         decoder.Clear();
480                         bCDATA = FALSE;
481                         iState = 0;
482                         m_dwIndex --;
483                         CXML_Element* pSubElement = ParseElement(pElement, TRUE);
484                         if (pSubElement == NULL) {
485                             break;
486                         }
487                         pSubElement->m_pParent = pElement;
488                         pElement->m_Children.Add((FX_LPVOID)CXML_Element::Element);
489                         pElement->m_Children.Add(pSubElement);
490                         SkipWhiteSpaces();
491                     }
492                     break;
493                 case 2:
494                     if (ch == '[') {
495                         SkipLiterals(FX_BSTRC("]]>"));
496                     } else if (ch == '-') {
497                         m_dwIndex ++;
498                         SkipLiterals(FX_BSTRC("-->"));
499                     } else {
500                         SkipLiterals(FX_BSTRC(">"));
501                     }
502                     decoder.Clear();
503                     SkipWhiteSpaces();
504                     iState = 0;
505                     break;
506             }
507             if (iState == 10) {
508                 break;
509             }
510         }
511         m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
512         if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
513             break;
514         }
515     } while (ReadNextBlock());
516     content << decoder.GetResult();
517     CFX_WideString dataStr = content.GetWideString();
518     if (!m_bSaveSpaceChars) {
519         dataStr.TrimRight((FX_LPCWSTR)L" \t\r\n");
520     }
521     InsertContentSegment(bCDATA, dataStr, pElement);
522     content.Clear();
523     decoder.Clear();
524     bCDATA = FALSE;
525     return pElement;
526 }
527 void CXML_Parser::InsertContentSegment(FX_BOOL bCDATA, FX_WSTR content, CXML_Element* pElement)
528 {
529     if (content.IsEmpty()) {
530         return;
531     }
532     CXML_Content* pContent;
533     pContent = FX_NEW CXML_Content;
534     if (!pContent) {
535         return;
536     }
537     pContent->Set(bCDATA, content);
538     pElement->m_Children.Add((FX_LPVOID)CXML_Element::Content);
539     pElement->m_Children.Add(pContent);
540 }
541 static CXML_Element* XML_ContinueParse(CXML_Parser &parser, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize)
542 {
543     parser.m_bSaveSpaceChars = bSaveSpaceChars;
544     CXML_Element* pElement = parser.ParseElement(NULL, FALSE);
545     if (pParsedSize) {
546         *pParsedSize = parser.m_nOffset;
547     }
548     return pElement;
549 }
550 CXML_Element* CXML_Element::Parse(const void* pBuffer, size_t size, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize)
551 {
552     CXML_Parser parser;
553     if (!parser.Init((FX_LPBYTE)pBuffer, size)) {
554         return NULL;
555     }
556     return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
557 }
558 CXML_Element* CXML_Element::Parse(IFX_FileRead *pFile, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize)
559 {
560     CXML_Parser parser;
561     if (!parser.Init(pFile)) {
562         return NULL;
563     }
564     return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
565 }
566 CXML_Element* CXML_Element::Parse(IFX_BufferRead *pBuffer, FX_BOOL bSaveSpaceChars, FX_FILESIZE* pParsedSize)
567 {
568     CXML_Parser parser;
569     if (!parser.Init(pBuffer)) {
570         return NULL;
571     }
572     return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
573 }
574 CXML_Element::CXML_Element()
575     : m_QSpaceName()
576     , m_TagName()
577     , m_AttrMap()
578 {
579 }
580 CXML_Element::CXML_Element(FX_BSTR qSpace, FX_BSTR tagName)
581     : m_QSpaceName()
582     , m_TagName()
583     , m_AttrMap()
584 {
585     m_QSpaceName = qSpace;
586     m_TagName = tagName;
587 }
588 CXML_Element::CXML_Element(FX_BSTR qTagName)
589     : m_pParent(NULL)
590     , m_QSpaceName()
591     , m_TagName()
592     , m_AttrMap()
593 {
594     SetTag(qTagName);
595 }
596 CXML_Element::~CXML_Element()
597 {
598     Empty();
599 }
600 void CXML_Element::Empty()
601 {
602     RemoveChildren();
603 }
604 void CXML_Element::RemoveChildren()
605 {
606     for (int i = 0; i < m_Children.GetSize(); i += 2) {
607         ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
608         if (type == Content) {
609             CXML_Content* content = (CXML_Content*)m_Children.GetAt(i + 1);
610             delete content;
611         } else if (type == Element) {
612             CXML_Element* child = (CXML_Element*)m_Children.GetAt(i + 1);
613             child->RemoveChildren();
614             delete child;
615         }
616     }
617     m_Children.RemoveAll();
618 }
619 CFX_ByteString CXML_Element::GetTagName(FX_BOOL bQualified) const
620 {
621     if (!bQualified || m_QSpaceName.IsEmpty()) {
622         return m_TagName;
623     }
624     CFX_ByteString bsTag = m_QSpaceName;
625     bsTag += ":";
626     bsTag += m_TagName;
627     return bsTag;
628 }
629 CFX_ByteString CXML_Element::GetNamespace(FX_BOOL bQualified) const
630 {
631     if (bQualified) {
632         return m_QSpaceName;
633     }
634     return GetNamespaceURI(m_QSpaceName);
635 }
636 CFX_ByteString CXML_Element::GetNamespaceURI(FX_BSTR qName) const
637 {
638     const CFX_WideString* pwsSpace;
639     const CXML_Element *pElement = this;
640     do {
641         if (qName.IsEmpty()) {
642             pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC(""), FX_BSTRC("xmlns"));
643         } else {
644             pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC("xmlns"), qName);
645         }
646         if (pwsSpace) {
647             break;
648         }
649         pElement = pElement->GetParent();
650     } while(pElement);
651     return pwsSpace ? FX_UTF8Encode(*pwsSpace) : CFX_ByteString();
652 }
653 void CXML_Element::GetAttrByIndex(int index, CFX_ByteString& space, CFX_ByteString& name, CFX_WideString& value) const
654 {
655     if (index < 0 || index >= m_AttrMap.GetSize()) {
656         return;
657     }
658     CXML_AttrItem& item = m_AttrMap.GetAt(index);
659     space = item.m_QSpaceName;
660     name = item.m_AttrName;
661     value = item.m_Value;
662 }
663 FX_BOOL CXML_Element::HasAttr(FX_BSTR name) const
664 {
665     CFX_ByteStringC bsSpace, bsName;
666     FX_XML_SplitQualifiedName(name, bsSpace, bsName);
667     return m_AttrMap.Lookup(bsSpace, bsName) != NULL;
668 }
669 FX_BOOL CXML_Element::GetAttrValue(FX_BSTR name, CFX_WideString& attribute) const
670 {
671     CFX_ByteStringC bsSpace, bsName;
672     FX_XML_SplitQualifiedName(name, bsSpace, bsName);
673     const CFX_WideString* pValue = m_AttrMap.Lookup(bsSpace, bsName);
674     if (pValue) {
675         attribute = CFX_WideString((FX_LPCWSTR)pValue, pValue->GetLength());
676         return TRUE;
677     }
678     return FALSE;
679 }
680 FX_BOOL CXML_Element::GetAttrValue(FX_BSTR space, FX_BSTR name, CFX_WideString& attribute) const
681 {
682     const CFX_WideString* pValue = m_AttrMap.Lookup(space, name);
683     if (pValue) {
684         attribute = CFX_WideString((FX_LPCWSTR)pValue, pValue->GetLength());
685         return TRUE;
686     }
687     return FALSE;
688 }
689 FX_BOOL CXML_Element::GetAttrInteger(FX_BSTR name, int& attribute) const
690 {
691     CFX_ByteStringC bsSpace, bsName;
692     FX_XML_SplitQualifiedName(name, bsSpace, bsName);
693     const CFX_WideString* pwsValue = m_AttrMap.Lookup(bsSpace, bsName);
694     if (pwsValue) {
695         attribute = pwsValue->GetInteger();
696         return TRUE;
697     }
698     return FALSE;
699 }
700 FX_BOOL CXML_Element::GetAttrInteger(FX_BSTR space, FX_BSTR name, int& attribute) const
701 {
702     const CFX_WideString* pwsValue = m_AttrMap.Lookup(space, name);
703     if (pwsValue) {
704         attribute = pwsValue->GetInteger();
705         return TRUE;
706     }
707     return FALSE;
708 }
709 FX_BOOL CXML_Element::GetAttrFloat(FX_BSTR name, FX_FLOAT& attribute) const
710 {
711     CFX_ByteStringC bsSpace, bsName;
712     FX_XML_SplitQualifiedName(name, bsSpace, bsName);
713     return GetAttrFloat(bsSpace, bsName, attribute);
714 }
715 FX_BOOL CXML_Element::GetAttrFloat(FX_BSTR space, FX_BSTR name, FX_FLOAT& attribute) const
716 {
717     const CFX_WideString* pValue = m_AttrMap.Lookup(space, name);
718     if (pValue) {
719         attribute = pValue->GetFloat();
720         return TRUE;
721     }
722     return FALSE;
723 }
724 FX_DWORD CXML_Element::CountChildren() const
725 {
726     return m_Children.GetSize() / 2;
727 }
728 CXML_Element::ChildType CXML_Element::GetChildType(FX_DWORD index) const
729 {
730     index <<= 1;
731     if (index >= (FX_DWORD)m_Children.GetSize()) {
732         return Invalid;
733     }
734     return (ChildType)(FX_UINTPTR)m_Children.GetAt(index);
735 }
736 CFX_WideString CXML_Element::GetContent(FX_DWORD index) const
737 {
738     index <<= 1;
739     if (index >= (FX_DWORD)m_Children.GetSize() ||
740             (ChildType)(FX_UINTPTR)m_Children.GetAt(index) != Content) {
741         return CFX_WideString();
742     }
743     CXML_Content* pContent = (CXML_Content*)m_Children.GetAt(index + 1);
744     if (pContent) {
745         return pContent->m_Content;
746     }
747     return CFX_WideString();
748 }
749 CXML_Element* CXML_Element::GetElement(FX_DWORD index) const
750 {
751     index <<= 1;
752     if (index >= (FX_DWORD)m_Children.GetSize() ||
753             (ChildType)(FX_UINTPTR)m_Children.GetAt(index) != Element) {
754         return NULL;
755     }
756     return (CXML_Element*)m_Children.GetAt(index + 1);
757 }
758 FX_DWORD CXML_Element::CountElements(FX_BSTR space, FX_BSTR tag) const
759 {
760     int count = 0;
761     for (int i = 0; i < m_Children.GetSize(); i += 2) {
762         ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
763         if (type != Element) {
764             continue;
765         }
766         CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
767         if ((space.IsEmpty() || pKid->m_QSpaceName == space) && pKid->m_TagName == tag) {
768             count ++;
769         }
770     }
771     return count;
772 }
773 CXML_Element* CXML_Element::GetElement(FX_BSTR space, FX_BSTR tag, int index) const
774 {
775     if (index < 0) {
776         return NULL;
777     }
778     for (int i = 0; i < m_Children.GetSize(); i += 2) {
779         ChildType type = (ChildType)(FX_UINTPTR)m_Children.GetAt(i);
780         if (type != Element) {
781             continue;
782         }
783         CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
784         if ((!space.IsEmpty() && pKid->m_QSpaceName != space) || pKid->m_TagName != tag) {
785             continue;
786         }
787         if (index -- == 0) {
788             return pKid;
789         }
790     }
791     return NULL;
792 }
793 FX_DWORD CXML_Element::FindElement(CXML_Element *pChild) const
794 {
795     for (int i = 0; i < m_Children.GetSize(); i += 2) {
796         if ((ChildType)(FX_UINTPTR)m_Children.GetAt(i) == Element &&
797                 (CXML_Element*)m_Children.GetAt(i + 1) == pChild) {
798             return (FX_DWORD)(i >> 1);
799         }
800     }
801     return (FX_DWORD) - 1;
802 }
803 const CFX_WideString* CXML_AttrMap::Lookup(FX_BSTR space, FX_BSTR name) const
804 {
805     if (m_pMap == NULL) {
806         return NULL;
807     }
808     for (int i = 0; i < m_pMap->GetSize(); i ++) {
809         CXML_AttrItem& item = GetAt(i);
810         if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
811             return &item.m_Value;
812         }
813     }
814     return NULL;
815 }
816 void CXML_AttrMap::SetAt(FX_BSTR space, FX_BSTR name, FX_WSTR value)
817 {
818     for (int i = 0; i < GetSize(); i++) {
819         CXML_AttrItem& item = GetAt(i);
820         if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
821             item.m_Value = value;
822             return;
823         }
824     }
825     if (!m_pMap) {
826         m_pMap = FX_NEW CFX_ObjectArray < CXML_AttrItem > ;
827     }
828     if (!m_pMap) {
829         return;
830     }
831     CXML_AttrItem* pItem = (CXML_AttrItem*)m_pMap->AddSpace();
832     if (!pItem) {
833         return;
834     }
835     pItem->m_QSpaceName = space;
836     pItem->m_AttrName = name;
837     pItem->m_Value = value;
838 }
839 void CXML_AttrMap::RemoveAt(FX_BSTR space, FX_BSTR name)
840 {
841     if (m_pMap == NULL) {
842         return;
843     }
844     for (int i = 0; i < m_pMap->GetSize(); i ++) {
845         CXML_AttrItem& item = GetAt(i);
846         if ((space.IsEmpty() || item.m_QSpaceName == space) && item.m_AttrName == name) {
847             m_pMap->RemoveAt(i);
848             return;
849         }
850     }
851 }
852 int CXML_AttrMap::GetSize() const
853 {
854     return m_pMap == NULL ? 0 : m_pMap->GetSize();
855 }
856 CXML_AttrItem& CXML_AttrMap::GetAt(int index) const
857 {
858     ASSERT(m_pMap != NULL);
859     return (*m_pMap)[index];
860 }
861 void CXML_AttrMap::RemoveAll()
862 {
863     if (!m_pMap) {
864         return;
865     }
866     m_pMap->RemoveAll();
867     delete m_pMap;
868     m_pMap = NULL;
869 }