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