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