merged: upnp from branch 16970
[xbmc:xbmc-antiquated.git] / xbmc / lib / libUPnP / Platinum / ThirdParty / Neptune / Source / Core / NptXml.cpp
1 /*****************************************************************
2 |
3 |   Neptune - Xml Support
4 |
5 | Copyright (c) 2002-2008, Axiomatic Systems, LLC.
6 | All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions are met:
10 |     * Redistributions of source code must retain the above copyright
11 |       notice, this list of conditions and the following disclaimer.
12 |     * Redistributions in binary form must reproduce the above copyright
13 |       notice, this list of conditions and the following disclaimer in the
14 |       documentation and/or other materials provided with the distribution.
15 |     * Neither the name of Axiomatic Systems nor the
16 |       names of its contributors may be used to endorse or promote products
17 |       derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
30  ****************************************************************/
31
32 /*----------------------------------------------------------------------
33 |   includes
34 +---------------------------------------------------------------------*/
35 #include "NptConfig.h"
36 #include "NptTypes.h"
37 #include "NptXml.h"
38 #include "NptUtils.h"
39 #include "NptMap.h"
40 #include "NptDebug.h"
41
42 /*----------------------------------------------------------------------
43 |   local compilation flags
44 +---------------------------------------------------------------------*/
45 //#define NPT_XML_PARSER_DEBUG
46 #ifdef NPT_XML_PARSER_DEBUG
47 #define NPT_XML_Debug_0(s) NPT_Debug(s)
48 #define NPT_XML_Debug_1(s,x0) NPT_Debug(s,x0)
49 #define NPT_XML_Debug_2(s,x0,x1) NPT_Debug(s,x0,x1)
50 #define NPT_XML_Debug_3(s,x0,x1,x2) NPT_Debug(s,x0,x1,x2)
51 #define NPT_XML_Debug_4(s,x0,x1,x2,x3) NPT_Debug(s,x0,x1,x2,x3)
52 #else
53 #define NPT_XML_Debug_0(s)
54 #define NPT_XML_Debug_1(s,x0)
55 #define NPT_XML_Debug_2(s,x0,x1)
56 #define NPT_XML_Debug_3(s,x0,x1,x2)
57 #define NPT_XML_Debug_4(s,x0,x1,x2,x3)
58 #endif
59
60 /*----------------------------------------------------------------------
61 |   constants
62 +---------------------------------------------------------------------*/
63 static const NPT_String 
64 NPT_XmlNamespaceUri_Xml("http://www.w3.org/XML/1998/namespace");
65
66 /*----------------------------------------------------------------------
67 |   NPT_XmlAttributeFinder
68 +---------------------------------------------------------------------*/
69 class NPT_XmlAttributeFinder
70 {
71 public:
72     // if 'namespc' is NULL, we're looking for ANY namespace
73     // if 'namespc' is '\0', we're looking for NO namespace
74     // if 'namespc' is non-empty, look for that SPECIFIC namespace
75     NPT_XmlAttributeFinder(const NPT_XmlElementNode& element, 
76                            const char*               name, 
77                            const char*               namespc) : 
78       m_Element(element), m_Name(name), m_Namespace(namespc) {}
79
80     bool operator()(const NPT_XmlAttribute* const & attribute) const {
81         if (attribute->m_Name == m_Name) {
82             if (m_Namespace) {
83                 const NPT_String& prefix = attribute->GetPrefix();
84                 if (m_Namespace[0] == '\0') {
85                     // match if the attribute has NO namespace
86                     return prefix.IsEmpty();
87                 } else {
88                     // match if the attribute has the SPECIFIC namespace
89                     // we're looking for
90                     if (prefix.IsEmpty()) {
91                         // attributes without a prefix don't have a namespace
92                         return false;
93                     } else {
94                         const NPT_String* namespc = m_Element.GetNamespaceUri(prefix);
95                         return namespc && *namespc == m_Namespace;
96                     }
97                 }
98             } else {
99                 // ANY namespace will match
100                 return true;
101             }
102         } else {
103             return false;
104         }
105     }
106
107 private:
108     const NPT_XmlElementNode& m_Element;
109     const char*               m_Name;
110     const char*               m_Namespace;
111 };
112
113 /*----------------------------------------------------------------------
114 |   NPT_XmlAttributeFinderWithPrefix
115 +---------------------------------------------------------------------*/
116 class NPT_XmlAttributeFinderWithPrefix
117 {
118 public:
119     NPT_XmlAttributeFinderWithPrefix(const char* prefix, const char* name) : 
120       m_Prefix(prefix?prefix:""), m_Name(name) {}
121
122     bool operator()(const NPT_XmlAttribute* const & attribute) const {
123         return attribute->m_Prefix == m_Prefix && attribute->m_Name == m_Name;
124     }
125
126 private:
127     const char* m_Prefix;
128     const char* m_Name;
129 };
130
131 /*----------------------------------------------------------------------
132 |   NPT_XmlTagFinder
133 +---------------------------------------------------------------------*/
134 class NPT_XmlTagFinder
135 {
136 public:
137     // if 'namespc' is NULL, we're looking for ANY namespace
138     // if 'namespc' is '\0', we're looking for NO namespace
139     // if 'namespc' is non-empty, look for that SPECIFIC namespace
140     NPT_XmlTagFinder(const char* tag, const char* namespc) : 
141       m_Tag(tag), m_Namespace(namespc) {}
142
143     bool operator()(const NPT_XmlNode* const & node) const {
144         const NPT_XmlElementNode* element = node->AsElementNode();
145         if (element && element->m_Tag == m_Tag) {
146             if (m_Namespace) {
147                 // look for a SPECIFIC namespace or NO namespace
148                 const NPT_String* namespc = element->GetNamespace();
149                 if (namespc) {
150                     // the element has a namespace, match if it is equal to
151                     // what we're looking for
152                     return *namespc == m_Namespace;
153                 } else {
154                     // the element does not have a namespace, match if we're
155                     // looking for NO namespace
156                     return m_Namespace[0] == '\0';
157                 }
158             } else {
159                 // ANY namespace will match
160                 return true;
161             }
162         } else {
163             return false;
164         }
165     }
166
167 private:
168     const char* m_Tag;
169     const char* m_Namespace;
170 };
171
172 /*----------------------------------------------------------------------
173 |   NPT_XmlTextFinder
174 +---------------------------------------------------------------------*/
175 class NPT_XmlTextFinder
176 {
177 public:
178     bool operator()(const NPT_XmlNode* const & node) const {
179         return node->AsTextNode() != NULL;
180     }
181 };
182
183 /*----------------------------------------------------------------------
184 |   NPT_XmlNamespaceCollapser
185 +---------------------------------------------------------------------*/
186 class NPT_XmlNamespaceCollapser
187 {
188 public:
189     NPT_XmlNamespaceCollapser(NPT_XmlElementNode* element) : 
190       m_Root(element) {}
191
192     void operator()(NPT_XmlNode*& node) const {
193         NPT_XmlElementNode* element = node->AsElementNode();
194         if (element == NULL) return;
195
196         // collapse the namespace for this element
197         CollapseNamespace(element, element->GetPrefix());
198
199         // collapse the namespaces for the attributes
200         NPT_List<NPT_XmlAttribute*>::Iterator item = element->GetAttributes().GetFirstItem();
201         while (item) {
202             NPT_XmlAttribute* attribute = *item;
203             CollapseNamespace(element, attribute->GetPrefix());
204             ++item;
205         }
206
207         // recurse to the children
208         element->GetChildren().Apply(*this);
209     }
210
211 private:
212     // methods
213     void CollapseNamespace(NPT_XmlElementNode* element, const NPT_String& prefix) const;
214
215     // members
216     NPT_XmlElementNode* m_Root;
217 };
218
219 /*----------------------------------------------------------------------
220 |   NPT_XmlNamespaceCollapser::CollapseNamespace
221 +---------------------------------------------------------------------*/
222 void
223 NPT_XmlNamespaceCollapser::CollapseNamespace(NPT_XmlElementNode* element, 
224                                              const NPT_String&   prefix) const
225 {
226     if (m_Root->m_NamespaceMap == NULL ||
227         (m_Root->m_NamespaceMap->GetNamespaceUri(prefix) == NULL && prefix != "xml")) {
228         // the root element does not have that prefix in the map
229         const NPT_String* uri = element->GetNamespaceUri(prefix);
230         if (uri) m_Root->SetNamespaceUri(prefix, uri->GetChars());
231     }
232 }
233
234 /*----------------------------------------------------------------------
235 |   NPT_XmlAttribute::NPT_XmlAttribute
236 +---------------------------------------------------------------------*/
237 NPT_XmlAttribute::NPT_XmlAttribute(const char* name, const char* value) :
238     m_Value(value)
239 {
240     const char* cursor = name;
241     while (char c = *cursor++) {
242         if (c == ':') {
243             unsigned int prefix_length = (unsigned int)(cursor-name)-1;
244             m_Prefix.Assign(name, prefix_length);
245             name = cursor;
246             break;
247         }
248     }
249     m_Name = name;
250 }
251
252 /*----------------------------------------------------------------------
253 |   NPT_XmlElementNode::NPT_XmlElementNode
254 +---------------------------------------------------------------------*/
255 NPT_XmlElementNode::NPT_XmlElementNode(const char* prefix, const char* tag) :
256     NPT_XmlNode(ELEMENT),
257     m_Prefix(prefix),
258     m_Tag(tag),
259     m_NamespaceMap(NULL),
260     m_NamespaceParent(NULL)
261 {
262 }
263
264 /*----------------------------------------------------------------------
265 |   NPT_XmlElementNode::NPT_XmlElementNode
266 +---------------------------------------------------------------------*/
267 NPT_XmlElementNode::NPT_XmlElementNode(const char* tag) :
268     NPT_XmlNode(ELEMENT),
269     m_NamespaceMap(NULL),
270     m_NamespaceParent(NULL)
271 {
272     const char* cursor = tag;
273     while (char c = *cursor++) {
274         if (c == ':') {
275             unsigned int prefix_length = (unsigned int)(cursor-tag)-1;
276             m_Prefix.Assign(tag, prefix_length);
277             tag = cursor;
278             break;
279         }
280     }
281     m_Tag = tag;
282 }
283
284 /*----------------------------------------------------------------------
285 |   NPT_XmlElementNode::~NPT_XmlElementNode
286 +---------------------------------------------------------------------*/
287 NPT_XmlElementNode::~NPT_XmlElementNode()
288 {
289     m_Children.Apply(NPT_ObjectDeleter<NPT_XmlNode>());
290     m_Attributes.Apply(NPT_ObjectDeleter<NPT_XmlAttribute>());
291     delete m_NamespaceMap;
292 }
293
294 /*----------------------------------------------------------------------
295 |   NPT_XmlElementNode::SetParent
296 +---------------------------------------------------------------------*/
297 void
298 NPT_XmlElementNode::SetParent(NPT_XmlNode* parent)
299 {
300     // update our parent
301     m_Parent = parent;
302
303     // update out namespace linkage
304     NPT_XmlElementNode* parent_element =
305         parent?parent->AsElementNode():NULL;
306     NPT_XmlElementNode* namespace_parent;
307     if (parent_element) {
308         namespace_parent = 
309             parent_element->m_NamespaceMap ? 
310             parent_element:
311             parent_element->m_NamespaceParent;
312     } else {
313         namespace_parent = NULL;
314     }
315     if (namespace_parent != m_NamespaceParent) {
316         m_NamespaceParent = namespace_parent;
317         RelinkNamespaceMaps();
318     }
319 }
320
321 /*----------------------------------------------------------------------
322 |   NPT_XmlElementNode::AddChild
323 +---------------------------------------------------------------------*/
324 NPT_Result
325 NPT_XmlElementNode::AddChild(NPT_XmlNode* child)
326 {
327     if (child == NULL) return NPT_ERROR_INVALID_PARAMETERS;
328     child->SetParent(this);
329     return m_Children.Add(child);
330 }
331
332 /*----------------------------------------------------------------------
333 |   NPT_XmlElementNode::GetChild
334 +---------------------------------------------------------------------*/
335 NPT_XmlElementNode*
336 NPT_XmlElementNode::GetChild(const char* tag, const char* namespc, NPT_Ordinal n) const
337 {
338     // remap the requested namespace to match the semantics of the finder
339     // and allow for "" to also mean NO namespace
340     if (namespc == NULL || namespc[0] == '\0') {
341         namespc = ""; // for the finder, empty string means NO namespace
342     } else if (namespc[0] == '*' && namespc[1] == '\0') {
343         namespc = NULL; // for the finder, NULL means ANY namespace
344     }
345
346     // find the child
347     NPT_List<NPT_XmlNode*>::Iterator item;
348     item = m_Children.Find(NPT_XmlTagFinder(tag, namespc), n);
349     return item?(*item)->AsElementNode():NULL;
350 }
351
352 /*----------------------------------------------------------------------
353 |   NPT_XmlElementNode::AddAttribute
354 +---------------------------------------------------------------------*/
355 NPT_Result
356 NPT_XmlElementNode::AddAttribute(const char* name, 
357                                  const char* value)
358 {
359     if (name == NULL || value == NULL) return NPT_ERROR_INVALID_PARAMETERS;
360     return m_Attributes.Add(new NPT_XmlAttribute(name, value));
361 }
362
363 /*----------------------------------------------------------------------
364 |   NPT_XmlElementNode::SetAttribute
365 +---------------------------------------------------------------------*/
366 NPT_Result
367 NPT_XmlElementNode::SetAttribute(const char* prefix,
368                                  const char* name, 
369                                  const char* value)
370 {
371     if (name == NULL || value == NULL) return NPT_ERROR_INVALID_PARAMETERS;
372
373     /* see if this attribute is already set */
374     NPT_List<NPT_XmlAttribute*>::Iterator attribute;
375     attribute = m_Attributes.Find(NPT_XmlAttributeFinderWithPrefix(prefix, name));
376     if (attribute) {
377         // an attribute with this name and prefix already exists, 
378         // change its value
379         (*attribute)->SetValue(value); 
380         return NPT_SUCCESS;
381     }
382     return m_Attributes.Add(new NPT_XmlAttribute(prefix, name, value));
383 }
384
385 /*----------------------------------------------------------------------
386 |   NPT_XmlElementNode::SetAttribute
387 +---------------------------------------------------------------------*/
388 NPT_Result
389 NPT_XmlElementNode::SetAttribute(const char* name, const char* value)
390 {
391     return SetAttribute(NULL, name, value);
392 }
393
394 /*----------------------------------------------------------------------
395 |   NPT_XmlElementNode::GetAttribute
396 +---------------------------------------------------------------------*/
397 const NPT_String*
398 NPT_XmlElementNode::GetAttribute(const char* name, const char* namespc) const
399 {
400     // remap the requested namespace to match the semantics of the finder
401     // and allow for "" to also mean NO namespace
402     if (namespc == NULL || namespc[0] == '\0') {
403         namespc = ""; // for the finder, empty string means NO namespace
404     } else if (namespc[0] == '*' && namespc[1] == '\0') {
405         namespc = NULL; // for the finder, NULL means ANY namespace
406     }
407
408     // find the attribute
409     NPT_List<NPT_XmlAttribute*>::Iterator attribute;
410     attribute = m_Attributes.Find(NPT_XmlAttributeFinder(*this, name, namespc));
411     if (attribute) { 
412         return &(*attribute)->GetValue();
413     } else {
414         return NULL;
415     }
416 }
417
418 /*----------------------------------------------------------------------
419 |   NPT_XmlElementNode::AddText
420 +---------------------------------------------------------------------*/
421 NPT_Result
422 NPT_XmlElementNode::AddText(const char* text)
423 {
424     return AddChild(new NPT_XmlTextNode(NPT_XmlTextNode::CHARACTER_DATA, text));
425 }
426
427 /*----------------------------------------------------------------------
428 |   NPT_XmlElementNode::GetText
429 +---------------------------------------------------------------------*/
430 const NPT_String*
431 NPT_XmlElementNode::GetText(NPT_Ordinal n) const
432 {
433     NPT_List<NPT_XmlNode*>::Iterator node;
434     node = m_Children.Find(NPT_XmlTextFinder(), n);
435     return node?&(*node)->AsTextNode()->GetString():NULL;
436 }
437
438 /*----------------------------------------------------------------------
439 |   NPT_XmlElementNode::MakeStandalone
440 +---------------------------------------------------------------------*/
441 NPT_Result
442 NPT_XmlElementNode::MakeStandalone()
443 {
444     NPT_XmlNamespaceCollapser collapser(this);
445     NPT_XmlNode* node_pointer = this;
446     collapser(node_pointer);
447
448     return NPT_SUCCESS;
449 }
450
451 /*----------------------------------------------------------------------
452 |   NPT_XmlElementNode::RelinkNamespaceMaps
453 +---------------------------------------------------------------------*/
454 void
455 NPT_XmlElementNode::RelinkNamespaceMaps()
456 {
457     // update our children so that they can inherit the right
458     // namespace map
459     NPT_List<NPT_XmlNode*>::Iterator item = m_Children.GetFirstItem();
460     while (item) {
461         NPT_XmlElementNode* element = (*item)->AsElementNode();
462         if (element) {
463             if (m_NamespaceMap) {
464                 // we have a map, so our children point to us
465                 element->SetNamespaceParent(this);
466             } else {
467                 // we don't have a map, so our children point to
468                 // where we also point
469                 element->SetNamespaceParent(m_NamespaceParent);
470             }
471         }
472         ++item;
473     }
474 }
475
476 /*----------------------------------------------------------------------
477 |   NPT_XmlElementNode::SetNamespaceParent
478 +---------------------------------------------------------------------*/
479 void
480 NPT_XmlElementNode::SetNamespaceParent(NPT_XmlElementNode* parent)
481 {
482     m_NamespaceParent = parent;
483     RelinkNamespaceMaps();
484 }
485
486 /*----------------------------------------------------------------------
487 |   NPT_XmlElementNode::SetNamespaceUri
488 +---------------------------------------------------------------------*/
489 NPT_Result
490 NPT_XmlElementNode::SetNamespaceUri(const char* prefix, const char* uri)
491 {
492     // ensure that we have a namespace map
493     if (m_NamespaceMap == NULL) {
494         m_NamespaceMap = new NPT_XmlNamespaceMap();
495         RelinkNamespaceMaps();
496     }
497
498     return m_NamespaceMap->SetNamespaceUri(prefix, uri);
499 }
500
501 /*----------------------------------------------------------------------
502 |   NPT_XmlElementNode::GetNamespaceUri
503 +---------------------------------------------------------------------*/
504 const NPT_String*
505 NPT_XmlElementNode::GetNamespaceUri(const char* prefix) const
506 {
507     if (m_NamespaceMap) {
508         // look in our namespace map first
509         const NPT_String* namespc = m_NamespaceMap->GetNamespaceUri(prefix);
510         if (namespc) {
511             if (namespc->IsEmpty()) {
512                 return NULL;
513             } else {
514                 return namespc;
515             }
516         }
517     } 
518
519     // look into our parent's namespace map
520     if (m_NamespaceParent) {
521         return m_NamespaceParent->GetNamespaceUri(prefix);
522     } else {
523         // check if this is a well-known namespace
524         if (prefix[0] == 'x' && 
525             prefix[1] == 'm' && 
526             prefix[2] == 'l' && 
527             prefix[3] == '\0') {
528             return &NPT_XmlNamespaceUri_Xml;
529         }
530
531         // not found
532         return NULL;
533     }
534 }
535
536 /*----------------------------------------------------------------------
537 |   NPT_XmlElementNode::GetNamespace
538 +---------------------------------------------------------------------*/
539 const NPT_String*
540 NPT_XmlElementNode::GetNamespace() const
541 {
542     return GetNamespaceUri(m_Prefix);
543 }
544
545 /*----------------------------------------------------------------------
546 |   NPT_XmlElementNode::GetNamespacePrefix
547 +---------------------------------------------------------------------*/
548 const NPT_String*
549 NPT_XmlElementNode::GetNamespacePrefix(const char* uri) const
550 {
551     NPT_XmlNamespaceMap* namespace_map = 
552         m_NamespaceMap? 
553         m_NamespaceMap:
554         (m_NamespaceParent?
555          m_NamespaceParent->m_NamespaceMap:
556          NULL);
557
558     if (namespace_map) {
559         return namespace_map->GetNamespacePrefix(uri);
560     } else {
561         return NULL;
562     }
563 }
564
565 /*----------------------------------------------------------------------
566 |   NPT_XmlTextNode::NPT_XmlTextNode
567 +---------------------------------------------------------------------*/
568 NPT_XmlTextNode::NPT_XmlTextNode(TokenType token_type, const char* text) :
569     NPT_XmlNode(TEXT),
570     m_TokenType(token_type),
571     m_Text(text)
572 {
573 }
574
575 /*----------------------------------------------------------------------
576 |   NPT_XmlAccumulator
577 +---------------------------------------------------------------------*/
578 class NPT_XmlAccumulator {
579 public:
580              NPT_XmlAccumulator();
581             ~NPT_XmlAccumulator();
582     void     Append(char c);
583     void     Append(const char* s);
584     void     AppendUTF8(unsigned int c);
585     void     Reset() { m_Valid = 0; }
586     const char*          GetString();
587     NPT_Size             GetSize() const   { return m_Valid;  }
588     const unsigned char* GetBuffer() const { return m_Buffer; }
589
590 private:
591     // methods
592     void Allocate(NPT_Size size);
593
594     // members
595     unsigned char* m_Buffer;
596     NPT_Size       m_Allocated;
597     NPT_Size       m_Valid;
598 };
599
600 /*----------------------------------------------------------------------
601 |   NPT_XmlAccumulator::NPT_XmlAccumulator
602 +---------------------------------------------------------------------*/
603 NPT_XmlAccumulator::NPT_XmlAccumulator() :
604     m_Buffer(NULL),
605     m_Allocated(0),
606     m_Valid(0)
607 {
608 }
609
610 /*----------------------------------------------------------------------
611 |   NPT_XmlAccumulator::~NPT_XmlAccumulator
612 +---------------------------------------------------------------------*/
613 NPT_XmlAccumulator::~NPT_XmlAccumulator()
614 {
615     delete[] m_Buffer;
616 }
617
618 /*----------------------------------------------------------------------
619 |   NPT_XmlAccumulator::Allocate
620 +---------------------------------------------------------------------*/
621 void
622 NPT_XmlAccumulator::Allocate(NPT_Size size)
623 {
624     // check if we have enough
625     if (m_Allocated >= size) return;
626
627     // compute new size
628     do {
629         m_Allocated = m_Allocated ? m_Allocated * 2 : 32;
630     } while (m_Allocated < size);
631
632     // reallocate
633     unsigned char* new_buffer = new unsigned char[m_Allocated];
634     NPT_CopyMemory(new_buffer, m_Buffer, m_Valid);
635     delete[] m_Buffer;
636     m_Buffer = new_buffer;
637 }
638
639 /*----------------------------------------------------------------------
640 |   NPT_XmlAccumulator::Append
641 +---------------------------------------------------------------------*/
642 inline void
643 NPT_XmlAccumulator::Append(char c)
644 {
645     NPT_Size needed = m_Valid+1;
646     if (needed > m_Allocated) Allocate(needed);
647     m_Buffer[m_Valid++] = c;
648 }
649
650 /*----------------------------------------------------------------------
651 |   NPT_XmlAccumulator::Append
652 +---------------------------------------------------------------------*/
653 void
654 NPT_XmlAccumulator::Append(const char* s)
655 {
656     char c;
657     while ((c = *s++)) Append(c);
658 }
659
660 /*----------------------------------------------------------------------
661 |   NPT_XmlAccumulator::AppendUTF8
662 +---------------------------------------------------------------------*/
663 inline void
664 NPT_XmlAccumulator::AppendUTF8(unsigned int c)
665 {
666     NPT_Size needed = m_Valid+4; // allocate 4 more chars
667     if (needed > m_Allocated) Allocate(needed);
668
669     if (c <= 0x7F) {
670         // 000000\9600007F -> 1 char = 0xxxxxxx
671         m_Buffer[m_Valid++] = (char)c;
672     } else if (c <= 0x7FF) {
673         // 000080\960007FF -> 2 chars = 110zzzzx 10xxxxxx
674         m_Buffer[m_Valid++] = 0xC0|(c>>6  );
675         m_Buffer[m_Valid++] = 0x80|(c&0x3F);
676     } else if (c <= 0xFFFF) {
677         // 000800\9600FFFF -> 3 chars = 1110zzzz 10zxxxxx 10xxxxxx
678         m_Buffer[m_Valid++] = 0xE0| (c>>12      );
679         m_Buffer[m_Valid++] = 0x80|((c&0xFC0)>>6);
680         m_Buffer[m_Valid++] = 0x80| (c&0x3F     );
681     } else if (c <= 0x10FFFF) {
682         // 010000\9610FFFF -> 4 chars = 11110zzz 10zzxxxx 10xxxxxx 10xxxxxx
683         m_Buffer[m_Valid++] = 0xF0| (c>>18         );
684         m_Buffer[m_Valid++] = 0x80|((c&0x3F000)>>12);
685         m_Buffer[m_Valid++] = 0x80|((c&0xFC0  )>> 6);
686         m_Buffer[m_Valid++] = 0x80| (c&0x3F        );
687     }
688 }
689
690 /*----------------------------------------------------------------------
691 |   NPT_XmlAccumulator::GetString
692 +---------------------------------------------------------------------*/
693 inline const char*
694 NPT_XmlAccumulator::GetString()
695 {
696     // ensure that the buffer is NULL terminated 
697     Allocate(m_Valid+1);
698     m_Buffer[m_Valid] = '\0';
699     return (const char*)m_Buffer;
700 }
701
702 /*----------------------------------------------------------------------
703 |   NPT_XmlNamespaceMap::~NPT_XmlNamespaceMap
704 +---------------------------------------------------------------------*/
705 NPT_XmlNamespaceMap::~NPT_XmlNamespaceMap()
706 {
707     m_Entries.Apply(NPT_ObjectDeleter<Entry>());
708 }
709
710 /*----------------------------------------------------------------------
711 |   NPT_XmlNamespaceMap::SetNamespaceUri
712 +---------------------------------------------------------------------*/
713 NPT_Result
714 NPT_XmlNamespaceMap::SetNamespaceUri(const char* prefix, const char* uri)
715 {
716     NPT_List<Entry*>::Iterator item = m_Entries.GetFirstItem();
717     while (item) {
718         if ((*item)->m_Prefix == prefix) {
719             // the prefix is already in the map, update the value
720             (*item)->m_Uri = uri;
721             return NPT_SUCCESS;
722         }
723         ++item;
724     }
725
726     // the prefix is not in the map, add it
727     return m_Entries.Add(new Entry(prefix, uri));
728 }
729
730 /*----------------------------------------------------------------------
731 |   NPT_XmlNamespaceMap::GetNamespaceUri
732 +---------------------------------------------------------------------*/
733 const NPT_String*
734 NPT_XmlNamespaceMap::GetNamespaceUri(const char* prefix)
735 {
736     NPT_List<Entry*>::Iterator item = m_Entries.GetFirstItem();
737     while (item) {
738         if ((*item)->m_Prefix == prefix) {
739             // match
740             return &(*item)->m_Uri;
741         }
742         ++item;
743     }
744
745     // the prefix is not in the map
746     return NULL;
747 }
748
749 /*----------------------------------------------------------------------
750 |   NPT_XmlNamespaceMap::GetNamespacePrefix
751 +---------------------------------------------------------------------*/
752 const NPT_String*
753 NPT_XmlNamespaceMap::GetNamespacePrefix(const char* uri)
754 {
755     NPT_List<Entry*>::Iterator item = m_Entries.GetFirstItem();
756     while (item) {
757         if ((*item)->m_Uri == uri) {
758             // match
759             return &(*item)->m_Prefix;
760         }
761         ++item;
762     }
763
764     // the uri is not in the map
765     return NULL;
766 }
767
768 /*----------------------------------------------------------------------
769 |   character map
770 |
771 | flags:
772 | 1  --> any char
773 | 2  --> whitespace
774 | 4  --> name
775 | 8  --> content
776 | 16 --> value
777 +---------------------------------------------------------------------*/
778 #define NPT_XML_USE_CHAR_MAP
779 #if defined(NPT_XML_USE_CHAR_MAP)
780 // NOTE: this table is generated by the ruby script 'XmlCharMap.rb'
781 static const unsigned char NPT_XmlCharMap[256] = {
782                    0, //   0 0x00 
783                    0, //   1 0x01 
784                    0, //   2 0x02 
785                    0, //   3 0x03 
786                    0, //   4 0x04 
787                    0, //   5 0x05 
788                    0, //   6 0x06 
789                    0, //   7 0x07 
790                    0, //   8 0x08 
791             1|2|8|16, //   9 0x09 
792             1|2|8|16, //  10 0x0a 
793                    0, //  11 0x0b 
794                    0, //  12 0x0c 
795             1|2|8|16, //  13 0x0d 
796                    0, //  14 0x0e 
797                    0, //  15 0x0f 
798                    0, //  16 0x10 
799                    0, //  17 0x11 
800                    0, //  18 0x12 
801                    0, //  19 0x13 
802                    0, //  20 0x14 
803                    0, //  21 0x15 
804                    0, //  22 0x16 
805                    0, //  23 0x17 
806                    0, //  24 0x18 
807                    0, //  25 0x19 
808                    0, //  26 0x1a 
809                    0, //  27 0x1b 
810                    0, //  28 0x1c 
811                    0, //  29 0x1d 
812                    0, //  30 0x1e 
813                    0, //  31 0x1f 
814             1|2|8|16, //  32 0x20 ' '
815               1|8|16, //  33 0x21 '!'
816               1|8|16, //  34 0x22 '"'
817               1|8|16, //  35 0x23 '#'
818               1|8|16, //  36 0x24 '$'
819               1|8|16, //  37 0x25 '%'
820                    1, //  38 0x26 '&'
821               1|8|16, //  39 0x27 '''
822               1|8|16, //  40 0x28 '('
823               1|8|16, //  41 0x29 ')'
824               1|8|16, //  42 0x2a '*'
825               1|8|16, //  43 0x2b '+'
826               1|8|16, //  44 0x2c ','
827             1|4|8|16, //  45 0x2d '-'
828             1|4|8|16, //  46 0x2e '.'
829               1|8|16, //  47 0x2f '/'
830             1|4|8|16, //  48 0x30 '0'
831             1|4|8|16, //  49 0x31 '1'
832             1|4|8|16, //  50 0x32 '2'
833             1|4|8|16, //  51 0x33 '3'
834             1|4|8|16, //  52 0x34 '4'
835             1|4|8|16, //  53 0x35 '5'
836             1|4|8|16, //  54 0x36 '6'
837             1|4|8|16, //  55 0x37 '7'
838             1|4|8|16, //  56 0x38 '8'
839             1|4|8|16, //  57 0x39 '9'
840             1|4|8|16, //  58 0x3a ':'
841               1|8|16, //  59 0x3b ';'
842                    1, //  60 0x3c '<'
843               1|8|16, //  61 0x3d '='
844               1|8|16, //  62 0x3e '>'
845               1|8|16, //  63 0x3f '?'
846               1|8|16, //  64 0x40 '@'
847             1|4|8|16, //  65 0x41 'A'
848             1|4|8|16, //  66 0x42 'B'
849             1|4|8|16, //  67 0x43 'C'
850             1|4|8|16, //  68 0x44 'D'
851             1|4|8|16, //  69 0x45 'E'
852             1|4|8|16, //  70 0x46 'F'
853             1|4|8|16, //  71 0x47 'G'
854             1|4|8|16, //  72 0x48 'H'
855             1|4|8|16, //  73 0x49 'I'
856             1|4|8|16, //  74 0x4a 'J'
857             1|4|8|16, //  75 0x4b 'K'
858             1|4|8|16, //  76 0x4c 'L'
859             1|4|8|16, //  77 0x4d 'M'
860             1|4|8|16, //  78 0x4e 'N'
861             1|4|8|16, //  79 0x4f 'O'
862             1|4|8|16, //  80 0x50 'P'
863             1|4|8|16, //  81 0x51 'Q'
864             1|4|8|16, //  82 0x52 'R'
865             1|4|8|16, //  83 0x53 'S'
866             1|4|8|16, //  84 0x54 'T'
867             1|4|8|16, //  85 0x55 'U'
868             1|4|8|16, //  86 0x56 'V'
869             1|4|8|16, //  87 0x57 'W'
870             1|4|8|16, //  88 0x58 'X'
871             1|4|8|16, //  89 0x59 'Y'
872             1|4|8|16, //  90 0x5a 'Z'
873               1|8|16, //  91 0x5b '['
874               1|8|16, //  92 0x5c '\'
875               1|8|16, //  93 0x5d ']'
876               1|8|16, //  94 0x5e '^'
877             1|4|8|16, //  95 0x5f '_'
878               1|8|16, //  96 0x60 '`'
879             1|4|8|16, //  97 0x61 'a'
880             1|4|8|16, //  98 0x62 'b'
881             1|4|8|16, //  99 0x63 'c'
882             1|4|8|16, // 100 0x64 'd'
883             1|4|8|16, // 101 0x65 'e'
884             1|4|8|16, // 102 0x66 'f'
885             1|4|8|16, // 103 0x67 'g'
886             1|4|8|16, // 104 0x68 'h'
887             1|4|8|16, // 105 0x69 'i'
888             1|4|8|16, // 106 0x6a 'j'
889             1|4|8|16, // 107 0x6b 'k'
890             1|4|8|16, // 108 0x6c 'l'
891             1|4|8|16, // 109 0x6d 'm'
892             1|4|8|16, // 110 0x6e 'n'
893             1|4|8|16, // 111 0x6f 'o'
894             1|4|8|16, // 112 0x70 'p'
895             1|4|8|16, // 113 0x71 'q'
896             1|4|8|16, // 114 0x72 'r'
897             1|4|8|16, // 115 0x73 's'
898             1|4|8|16, // 116 0x74 't'
899             1|4|8|16, // 117 0x75 'u'
900             1|4|8|16, // 118 0x76 'v'
901             1|4|8|16, // 119 0x77 'w'
902             1|4|8|16, // 120 0x78 'x'
903             1|4|8|16, // 121 0x79 'y'
904             1|4|8|16, // 122 0x7a 'z'
905               1|8|16, // 123 0x7b '{'
906               1|8|16, // 124 0x7c '|'
907               1|8|16, // 125 0x7d '}'
908               1|8|16, // 126 0x7e '~'
909               1|8|16, // 127 0x7f 
910               1|8|16, // 128 0x80 
911               1|8|16, // 129 0x81 
912               1|8|16, // 130 0x82 
913               1|8|16, // 131 0x83 
914               1|8|16, // 132 0x84 
915               1|8|16, // 133 0x85 
916               1|8|16, // 134 0x86 
917               1|8|16, // 135 0x87 
918               1|8|16, // 136 0x88 
919               1|8|16, // 137 0x89 
920               1|8|16, // 138 0x8a 
921               1|8|16, // 139 0x8b 
922               1|8|16, // 140 0x8c 
923               1|8|16, // 141 0x8d 
924               1|8|16, // 142 0x8e 
925               1|8|16, // 143 0x8f 
926               1|8|16, // 144 0x90 
927               1|8|16, // 145 0x91 
928               1|8|16, // 146 0x92 
929               1|8|16, // 147 0x93 
930               1|8|16, // 148 0x94 
931               1|8|16, // 149 0x95 
932               1|8|16, // 150 0x96 
933               1|8|16, // 151 0x97 
934               1|8|16, // 152 0x98 
935               1|8|16, // 153 0x99 
936               1|8|16, // 154 0x9a 
937               1|8|16, // 155 0x9b 
938               1|8|16, // 156 0x9c 
939               1|8|16, // 157 0x9d 
940               1|8|16, // 158 0x9e 
941               1|8|16, // 159 0x9f 
942               1|8|16, // 160 0xa0 
943               1|8|16, // 161 0xa1 
944               1|8|16, // 162 0xa2 
945               1|8|16, // 163 0xa3 
946               1|8|16, // 164 0xa4 
947               1|8|16, // 165 0xa5 
948               1|8|16, // 166 0xa6 
949               1|8|16, // 167 0xa7 
950               1|8|16, // 168 0xa8 
951               1|8|16, // 169 0xa9 
952               1|8|16, // 170 0xaa 
953               1|8|16, // 171 0xab 
954               1|8|16, // 172 0xac 
955               1|8|16, // 173 0xad 
956               1|8|16, // 174 0xae 
957               1|8|16, // 175 0xaf 
958               1|8|16, // 176 0xb0 
959               1|8|16, // 177 0xb1 
960               1|8|16, // 178 0xb2 
961               1|8|16, // 179 0xb3 
962               1|8|16, // 180 0xb4 
963               1|8|16, // 181 0xb5 
964               1|8|16, // 182 0xb6 
965               1|8|16, // 183 0xb7 
966               1|8|16, // 184 0xb8 
967               1|8|16, // 185 0xb9 
968               1|8|16, // 186 0xba 
969               1|8|16, // 187 0xbb 
970               1|8|16, // 188 0xbc 
971               1|8|16, // 189 0xbd 
972               1|8|16, // 190 0xbe 
973               1|8|16, // 191 0xbf 
974             1|4|8|16, // 192 0xc0 
975             1|4|8|16, // 193 0xc1 
976             1|4|8|16, // 194 0xc2 
977             1|4|8|16, // 195 0xc3 
978             1|4|8|16, // 196 0xc4 
979             1|4|8|16, // 197 0xc5 
980             1|4|8|16, // 198 0xc6 
981             1|4|8|16, // 199 0xc7 
982             1|4|8|16, // 200 0xc8 
983             1|4|8|16, // 201 0xc9 
984             1|4|8|16, // 202 0xca 
985             1|4|8|16, // 203 0xcb 
986             1|4|8|16, // 204 0xcc 
987             1|4|8|16, // 205 0xcd 
988             1|4|8|16, // 206 0xce 
989             1|4|8|16, // 207 0xcf 
990             1|4|8|16, // 208 0xd0 
991             1|4|8|16, // 209 0xd1 
992             1|4|8|16, // 210 0xd2 
993             1|4|8|16, // 211 0xd3 
994             1|4|8|16, // 212 0xd4 
995             1|4|8|16, // 213 0xd5 
996             1|4|8|16, // 214 0xd6 
997               1|8|16, // 215 0xd7 
998             1|4|8|16, // 216 0xd8 
999             1|4|8|16, // 217 0xd9 
1000             1|4|8|16, // 218 0xda 
1001             1|4|8|16, // 219 0xdb 
1002             1|4|8|16, // 220 0xdc 
1003             1|4|8|16, // 221 0xdd 
1004             1|4|8|16, // 222 0xde 
1005             1|4|8|16, // 223 0xdf 
1006             1|4|8|16, // 224 0xe0 
1007             1|4|8|16, // 225 0xe1 
1008             1|4|8|16, // 226 0xe2 
1009             1|4|8|16, // 227 0xe3 
1010             1|4|8|16, // 228 0xe4 
1011             1|4|8|16, // 229 0xe5 
1012             1|4|8|16, // 230 0xe6 
1013             1|4|8|16, // 231 0xe7 
1014             1|4|8|16, // 232 0xe8 
1015             1|4|8|16, // 233 0xe9 
1016             1|4|8|16, // 234 0xea 
1017             1|4|8|16, // 235 0xeb 
1018             1|4|8|16, // 236 0xec 
1019             1|4|8|16, // 237 0xed 
1020             1|4|8|16, // 238 0xee 
1021             1|4|8|16, // 239 0xef 
1022             1|4|8|16, // 240 0xf0 
1023             1|4|8|16, // 241 0xf1 
1024             1|4|8|16, // 242 0xf2 
1025             1|4|8|16, // 243 0xf3 
1026             1|4|8|16, // 244 0xf4 
1027             1|4|8|16, // 245 0xf5 
1028             1|4|8|16, // 246 0xf6 
1029               1|8|16, // 247 0xf7 
1030             1|4|8|16, // 248 0xf8 
1031             1|4|8|16, // 249 0xf9 
1032             1|4|8|16, // 250 0xfa 
1033             1|4|8|16, // 251 0xfb 
1034             1|4|8|16, // 252 0xfc 
1035             1|4|8|16, // 253 0xfd 
1036             1|4|8|16, // 254 0xfe 
1037             1|4|8|16  // 255 0xff 
1038 };
1039 #endif // defined(NPT_XML_USE_CHAR_MAP)
1040
1041 /*----------------------------------------------------------------------
1042 |   macros
1043 +---------------------------------------------------------------------*/
1044 #if defined (NPT_XML_USE_CHAR_MAP)
1045 #define NPT_XML_CHAR_IS_ANY_CHAR(c)        (NPT_XmlCharMap[c] & 1)
1046 #define NPT_XML_CHAR_IS_WHITESPACE(c)      (NPT_XmlCharMap[c] & 2)
1047 #define NPT_XML_CHAR_IS_NAME_CHAR(c)       (NPT_XmlCharMap[c] & 4)
1048 #define NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c) (NPT_XML_CHAR_IS_NAME_CHAR((c)) || ((c) == '#'))
1049 #define NPT_XML_CHAR_IS_CONTENT_CHAR(c)    (NPT_XmlCharMap[c] & 8)
1050 #define NPT_XML_CHAR_IS_VALUE_CHAR(c)      (NPT_XmlCharMap[c] & 16)
1051 #else
1052 #define NPT_XML_CHAR_IS_WHITESPACE(c) \
1053 ((c) == ' ' || (c) == '\t' || (c) == 0x0D || (c) == 0x0A)
1054
1055 #define NPT_XML_CHAR_IS_ANY_CHAR(c) \
1056 (NPT_XML_CHAR_IS_WHITESPACE((c)) || ((c) >= 0x20))
1057
1058 #define NPT_XML_CHAR_IS_DIGIT(c) \
1059 ((c) >= '0' && (c) <= '9')
1060
1061 #define NPT_XML_CHAR_IS_LETTER(c) \
1062 (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || ((c) >= 0xC0 && (c) <= 0xD6) || ((c) >= 0xD8 && (c) <= 0xF6) || ((c) >= 0xF8))
1063
1064 #define NPT_XML_CHAR_IS_NAME_CHAR(c) \
1065 (NPT_XML_CHAR_IS_DIGIT((c)) || NPT_XML_CHAR_IS_LETTER((c)) || (c) == '.' || (c) == '-' || (c) == '_' || (c) == ':')
1066
1067 #define NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c) \
1068 (NPT_XML_CHAR_IS_NAME_CHAR((c)) || ((c) == '#'))
1069
1070 #define NPT_XML_CHAR_IS_CONTENT_CHAR(c) \
1071 (NPT_XML_CHAR_IS_ANY_CHAR((c)) && ((c) != '&') && ((c) != '<'))
1072
1073 #define NPT_XML_CHAR_IS_VALUE_CHAR(c) \
1074 (NPT_XML_CHAR_IS_ANY_CHAR((c)) && ((c) != '&') && ((c) != '<'))
1075
1076 #endif // defined(NPT_XML_USE_CHAR_MAP)
1077
1078 /*----------------------------------------------------------------------
1079 |   NPT_XmlStringIsWhitespace
1080 +---------------------------------------------------------------------*/
1081 static bool
1082 NPT_XmlStringIsWhitespace(const char* s, NPT_Size size)
1083 {
1084     for (NPT_Size x=0; x<size; x++) {
1085         if (!NPT_XML_CHAR_IS_WHITESPACE((int)s[x])) {
1086             return false;
1087         }
1088     }
1089
1090     return true;
1091 }
1092
1093 /*----------------------------------------------------------------------
1094 |   NPT_XmlProcessor class
1095 +---------------------------------------------------------------------*/
1096 class NPT_XmlProcessor {
1097 public:
1098     // constructor and destructor
1099    NPT_XmlProcessor(NPT_XmlParser* parser);
1100
1101     // methods
1102     NPT_Result ProcessBuffer(const char* buffer, NPT_Size size);
1103     
1104 private:
1105     // types
1106     typedef enum {
1107         CONTEXT_NONE,
1108         CONTEXT_OPEN_TAG,
1109         CONTEXT_CLOSE_TAG,
1110         CONTEXT_ATTRIBUTE,
1111         CONTEXT_VALUE_SINGLE_QUOTE,
1112         CONTEXT_VALUE_DOUBLE_QUOTE
1113     } Context;
1114
1115     typedef enum {
1116         STATE_IN_WHITESPACE,
1117         STATE_IN_NAME,
1118         STATE_IN_NAME_SPECIAL,
1119         STATE_IN_VALUE_START,
1120         STATE_IN_VALUE,
1121         STATE_IN_TAG_START,
1122         STATE_IN_EMPTY_TAG_END,
1123         STATE_IN_CONTENT,
1124         STATE_IN_PROCESSING_INSTRUCTION_START,
1125         STATE_IN_PROCESSING_INSTRUCTION,
1126         STATE_IN_PROCESSING_INSTRUCTION_END,
1127         STATE_IN_COMMENT,
1128         STATE_IN_COMMENT_END_1,
1129         STATE_IN_COMMENT_END_2,
1130         STATE_IN_DTD,
1131         STATE_IN_DTD_MARKUP_DECL,
1132         STATE_IN_DTD_MARKUP_DECL_END,
1133         STATE_IN_CDATA,
1134         STATE_IN_CDATA_END_1,
1135         STATE_IN_CDATA_END_2,
1136         STATE_IN_SPECIAL,
1137         STATE_IN_ENTITY_REF
1138     } State;
1139
1140     // members
1141     NPT_XmlParser*     m_Parser;
1142     State              m_State;
1143     Context            m_Context;
1144     bool               m_SkipNewline;
1145     NPT_XmlAccumulator m_Name;
1146     NPT_XmlAccumulator m_Value;
1147     NPT_XmlAccumulator m_Text;
1148     NPT_XmlAccumulator m_Entity;
1149
1150     // methods
1151 #ifdef NPT_XML_PARSER_DEBUG
1152     const char* StateName(State state) {
1153         switch (state) {
1154           case STATE_IN_WHITESPACE: return "IN_WHITESPACE";
1155           case STATE_IN_NAME: return "IN_NAME";
1156           case STATE_IN_NAME_SPECIAL: return "IN_NAME_SPECIAL";
1157           case STATE_IN_VALUE_START: return "IN_VALUE_START";
1158           case STATE_IN_VALUE: return "IN_VALUE";
1159           case STATE_IN_TAG_START: return "IN_TAG_START";
1160           case STATE_IN_EMPTY_TAG_END: return "IN_EMPTY_TAG_END";
1161           case STATE_IN_CONTENT: return "IN_CONTENT";
1162           case STATE_IN_PROCESSING_INSTRUCTION_START: return "IN_PROCESSING_INSTRUCTION_START";
1163           case STATE_IN_PROCESSING_INSTRUCTION: return "IN_PROCESSING_INSTRUCTION";
1164           case STATE_IN_PROCESSING_INSTRUCTION_END: return "IN_PROCESSING_INSTRUCTION_END";
1165           case STATE_IN_COMMENT: return "IN_COMMENT";
1166           case STATE_IN_COMMENT_END_1: return "IN_COMMENT_END_1";
1167           case STATE_IN_COMMENT_END_2: return "IN_COMMENT_END_2";
1168           case STATE_IN_DTD: return "IN_DTD";
1169           case STATE_IN_DTD_MARKUP_DECL: return "IN_DTD_MARKUP_DECL";
1170           case STATE_IN_DTD_MARKUP_DECL_END: return "IN_DTD_MARKUP_DECL_END";
1171           case STATE_IN_CDATA: return "IN_CDATA";
1172           case STATE_IN_CDATA_END_1: return "IN_CDATA_END_1";
1173           case STATE_IN_CDATA_END_2: return "IN_CDATA_END_2";
1174           case STATE_IN_SPECIAL: return "IN_SPECIAL";
1175           case STATE_IN_ENTITY_REF: return "IN_ENTITY_REF";
1176         }
1177         return "UNKNOWN";
1178     }
1179
1180     const char* ContextName(Context context) {
1181         switch (context) {
1182           case CONTEXT_NONE: return "NONE";
1183           case CONTEXT_OPEN_TAG: return "OPEN_TAG";
1184           case CONTEXT_CLOSE_TAG: return "CLOSE_TAG";
1185           case CONTEXT_ATTRIBUTE: return "ATTRIBUTE";
1186           case CONTEXT_VALUE_SINGLE_QUOTE: return "VALUE_SINGLE_QUOTE";
1187           case CONTEXT_VALUE_DOUBLE_QUOTE: return "VALUE_DOUBLE_QUOTE";
1188         }
1189         return "UNKNOWN";
1190     }
1191 #endif /* NPT_XML_PARSER_DEBUG */
1192
1193     inline void SetState(State state) {
1194         NPT_XML_Debug_3("\nstate transition: %s to %s [ctx=%s]\n", 
1195                         StateName(m_State), 
1196                         StateName(state),
1197                         ContextName(m_Context));
1198         m_State = state;
1199     }
1200
1201     inline void SetState(State state, Context context) {
1202         NPT_XML_Debug_4("\nstate transition: %s [ctx=%s] to %s [ctx=%s]\n", 
1203                         StateName(m_State), 
1204                         ContextName(m_Context),
1205                         StateName(state), 
1206                         ContextName(context));
1207         m_State = state;
1208         m_Context = context;
1209     }
1210
1211     NPT_Result ResolveEntity(NPT_XmlAccumulator& source,
1212                              NPT_XmlAccumulator& destination);
1213     NPT_Result FlushPendingText();
1214 };
1215
1216 /*----------------------------------------------------------------------
1217 |   NPT_XmlProcessor::NPT_XmlProcessor
1218 +---------------------------------------------------------------------*/
1219 NPT_XmlProcessor::NPT_XmlProcessor(NPT_XmlParser* parser) :
1220     m_Parser(parser),
1221     m_State(STATE_IN_WHITESPACE),
1222     m_Context(CONTEXT_NONE),
1223     m_SkipNewline(false)
1224 {
1225 }
1226
1227 /*----------------------------------------------------------------------
1228 |   NPT_XmlProcessor::ResolveEntity
1229 +---------------------------------------------------------------------*/
1230 NPT_Result 
1231 NPT_XmlProcessor::ResolveEntity(NPT_XmlAccumulator& source,
1232                                 NPT_XmlAccumulator& destination)
1233 {
1234     const char* entity = (const char*)source.GetString();
1235     
1236     if (NPT_StringsEqual(entity, "lt")) {
1237         destination.Append('<');
1238     } else if (NPT_StringsEqual(entity, "gt")) {
1239         destination.Append('>');
1240     } else if (NPT_StringsEqual(entity, "amp")) {
1241         destination.Append('&');
1242     } else if (NPT_StringsEqual(entity, "quot")) {
1243         destination.Append('"');
1244     } else if (NPT_StringsEqual(entity, "apos")) {
1245         destination.Append('\'');
1246     } else if (entity[0] == '#') {
1247         int i=1;
1248         int base = 10;
1249         if (entity[1] == 'x') {
1250             i++;
1251             base = 16;
1252         }
1253         int parsed = 0;
1254         while (char c = entity[i++]) {
1255             int digit = -1;
1256             if (c>='0' && c<='9') {
1257                 digit = c-'0';
1258             } else if (base == 16) {
1259                 if (c >= 'a' && c <= 'f') {
1260                     digit = 10+c-'a';
1261                 } else if (c >= 'A' && c <= 'F') {
1262                     digit = 10+c-'A';
1263                 }
1264             }
1265             if (digit == -1) {
1266                 // invalid char, leave the entity unparsed
1267                 destination.Append(source.GetString());
1268                 return NPT_ERROR_INVALID_SYNTAX;
1269             }
1270             parsed = base*parsed+digit;
1271         }
1272         destination.AppendUTF8(parsed);
1273     } else {
1274         // unknown entity, leave as-is
1275         destination.Append(source.GetString());
1276     }
1277     
1278     return NPT_SUCCESS;
1279 }
1280
1281 /*----------------------------------------------------------------------
1282 |   NPT_XmlProcessor::FlushPendingText
1283 +---------------------------------------------------------------------*/
1284 NPT_Result 
1285 NPT_XmlProcessor::FlushPendingText()
1286 {
1287     if (m_Text.GetSize() > 0) {
1288         NPT_CHECK(m_Parser->OnCharacterData(m_Text.GetString(),
1289                                             m_Text.GetSize()));
1290         m_Text.Reset();
1291     }
1292     return NPT_SUCCESS;
1293 }
1294
1295 /*----------------------------------------------------------------------
1296 |   NPT_XmlProcessor::ProcessBuffer
1297 +---------------------------------------------------------------------*/
1298 NPT_Result
1299 NPT_XmlProcessor::ProcessBuffer(const char* buffer, NPT_Size size)
1300 {
1301     unsigned char c;
1302
1303     while (size-- && (c = *buffer++)) {
1304         NPT_XML_Debug_1("[%c]", (c == '\n' || c == '\r') ? '#' : c);
1305
1306         // normalize line ends
1307         if (m_SkipNewline) {
1308             m_SkipNewline = false;
1309             if (c == '\n') continue;
1310         }
1311         if (c == '\r') {
1312             m_SkipNewline = true;
1313             c = '\n';
1314         }
1315
1316         // process the character
1317         switch (m_State) {
1318           case STATE_IN_WHITESPACE:
1319             if (NPT_XML_CHAR_IS_WHITESPACE(c)) break;
1320             switch (m_Context) {
1321               case CONTEXT_NONE:
1322                 if (c == '<') {
1323                     SetState(STATE_IN_TAG_START);
1324                 } else {
1325                     return NPT_ERROR_INVALID_SYNTAX;
1326                 }
1327                 break;
1328                 
1329               case CONTEXT_ATTRIBUTE:
1330                 if (c == '/') {
1331                     SetState(STATE_IN_EMPTY_TAG_END, CONTEXT_NONE);
1332                 } else if (c == '>') {
1333                     SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1334                 } else if (NPT_XML_CHAR_IS_NAME_CHAR(c)) {
1335                     m_Name.Reset();
1336                     m_Name.Append(c);
1337                     SetState(STATE_IN_NAME);
1338                 } else {
1339                     return NPT_ERROR_INVALID_SYNTAX;
1340                 }
1341                 break;
1342
1343               case CONTEXT_CLOSE_TAG:
1344                 if (c == '>') {
1345                     NPT_CHECK(FlushPendingText());
1346                     NPT_CHECK(m_Parser->OnEndElement(m_Name.GetString()));
1347                     SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1348                 } else {
1349                     return NPT_ERROR_INVALID_SYNTAX;
1350                 }
1351                 break;
1352
1353               default:
1354                 return NPT_ERROR_INVALID_SYNTAX;
1355             }
1356             break;
1357
1358           case STATE_IN_NAME:
1359             if (NPT_XML_CHAR_IS_NAME_CHAR(c)) {
1360                 m_Name.Append(c);
1361                 break;
1362             }
1363             switch (m_Context) {
1364               case CONTEXT_ATTRIBUTE:
1365                 if (c == '=') {
1366                     m_Value.Reset();
1367                     SetState(STATE_IN_VALUE_START);
1368                 } else if (!NPT_XML_CHAR_IS_WHITESPACE(c)) {
1369                     return NPT_ERROR_INVALID_SYNTAX;
1370                 }
1371                 break;
1372
1373               case CONTEXT_OPEN_TAG:
1374                 if (c == '>' || c == '/' || NPT_XML_CHAR_IS_WHITESPACE(c)) {
1375                     NPT_CHECK(FlushPendingText());
1376                     NPT_CHECK(m_Parser->OnStartElement(m_Name.GetString()));
1377                     m_Name.Reset();
1378                     if (c == '>') {
1379                         SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1380                     } else if (c == '/') {
1381                         SetState(STATE_IN_EMPTY_TAG_END);
1382                     } else {
1383                         SetState(STATE_IN_WHITESPACE, CONTEXT_ATTRIBUTE);
1384                     }
1385                 } else {
1386                     return NPT_ERROR_INVALID_SYNTAX;
1387                 }
1388                 break;
1389
1390               case CONTEXT_CLOSE_TAG:
1391                 if (c == '>') {
1392                     NPT_CHECK(FlushPendingText());
1393                     NPT_CHECK(m_Parser->OnEndElement(m_Name.GetString()));
1394                     SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1395                 } else if (NPT_XML_CHAR_IS_WHITESPACE(c)) {
1396                     SetState(STATE_IN_WHITESPACE);
1397                 } else {
1398                     return NPT_ERROR_INVALID_SYNTAX;
1399                 }
1400                 break;
1401
1402               default:
1403                 return NPT_ERROR_INVALID_SYNTAX;
1404             }
1405             break;
1406
1407           case STATE_IN_NAME_SPECIAL:
1408             if (NPT_XML_CHAR_IS_NAME_CHAR(c) || (c == '[')) {
1409                 m_Name.Append(c);
1410
1411                 const unsigned char* nb = m_Name.GetBuffer();
1412                 if (m_Name.GetSize() == 2) {
1413                     if (nb[0] == '-' &&
1414                         nb[1] == '-') {
1415                         m_Name.Reset();
1416                         SetState(STATE_IN_COMMENT, CONTEXT_NONE);
1417                         break;
1418                     }
1419                 } else if (m_Name.GetSize() == 7) {
1420                     if (nb[0] == '[' &&
1421                         nb[1] == 'C' &&
1422                         nb[2] == 'D' &&
1423                         nb[3] == 'A' &&
1424                         nb[4] == 'T' &&
1425                         nb[5] == 'A' &&
1426                         nb[6] == '[') {
1427                         m_Name.Reset();
1428                         SetState(STATE_IN_CDATA, CONTEXT_NONE);
1429                         break;
1430                     }
1431                 }
1432                 break;
1433             }
1434             if (NPT_XML_CHAR_IS_WHITESPACE(c)) {
1435                 const char* special = m_Name.GetString();
1436                 if (special && NPT_StringsEqual(special, "DOCTYPE")) {
1437                     SetState(STATE_IN_DTD, CONTEXT_NONE);
1438                 } else {
1439                     SetState(STATE_IN_SPECIAL, CONTEXT_NONE);
1440                 }
1441                 m_Name.Reset();
1442             } else {
1443                 return NPT_ERROR_INVALID_SYNTAX;
1444             }
1445             break;
1446
1447           case STATE_IN_VALUE_START:
1448             if (NPT_XML_CHAR_IS_WHITESPACE(c)) break;
1449             if (c == '"') {
1450                 m_Value.Reset();
1451                 SetState(STATE_IN_VALUE, CONTEXT_VALUE_DOUBLE_QUOTE);
1452             } else if (c == '\'') {
1453                 m_Value.Reset();
1454                 SetState(STATE_IN_VALUE, CONTEXT_VALUE_SINGLE_QUOTE);
1455             } else {
1456                 return NPT_ERROR_INVALID_SYNTAX;
1457             }
1458             break;
1459
1460           case STATE_IN_VALUE:
1461             if ((c == '"'  && m_Context == CONTEXT_VALUE_DOUBLE_QUOTE) || 
1462                 (c == '\'' && m_Context == CONTEXT_VALUE_SINGLE_QUOTE)) {
1463                 NPT_CHECK(m_Parser->OnElementAttribute(m_Name.GetString(),
1464                                                        m_Value.GetString()));
1465                 SetState(STATE_IN_WHITESPACE, CONTEXT_ATTRIBUTE);
1466             } else if (c == '&') {
1467                 m_Entity.Reset();
1468                 SetState(STATE_IN_ENTITY_REF);
1469             } else if (NPT_XML_CHAR_IS_WHITESPACE(c)) {
1470                 m_Value.Append(' ');
1471             } else if (NPT_XML_CHAR_IS_VALUE_CHAR(c)) {
1472                 m_Value.Append(c);
1473             } else {
1474                 return NPT_ERROR_INVALID_SYNTAX;
1475             }
1476             break;
1477
1478           case STATE_IN_TAG_START:
1479             m_Name.Reset();
1480             if (c == '!') {
1481                 SetState(STATE_IN_NAME_SPECIAL, CONTEXT_NONE);
1482             } else if (c == '?') {
1483                 SetState(STATE_IN_PROCESSING_INSTRUCTION, CONTEXT_NONE);
1484             } else if (c == '/') {
1485                 SetState(STATE_IN_NAME, CONTEXT_CLOSE_TAG);
1486             } else if (NPT_XML_CHAR_IS_NAME_CHAR(c)) {
1487                 m_Name.Append(c);
1488                 SetState(STATE_IN_NAME, CONTEXT_OPEN_TAG);
1489             } else {
1490                 return NPT_ERROR_INVALID_SYNTAX;
1491             }
1492             break;
1493
1494           case STATE_IN_EMPTY_TAG_END:
1495             if (c == '>') {
1496                 NPT_CHECK(FlushPendingText());
1497                 NPT_CHECK(m_Parser->OnEndElement(NULL));
1498                 SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1499             } else {
1500                 return NPT_ERROR_INVALID_SYNTAX;
1501             }
1502             break;
1503
1504           case STATE_IN_ENTITY_REF:
1505             switch (m_Context) {
1506               case CONTEXT_VALUE_SINGLE_QUOTE:
1507               case CONTEXT_VALUE_DOUBLE_QUOTE:
1508                 if (c == ';') {
1509                     NPT_CHECK(ResolveEntity(m_Entity, m_Value));
1510                     SetState(STATE_IN_VALUE);
1511                 } else if (NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c)) {
1512                     m_Entity.Append(c);
1513                 } else {
1514                     return NPT_ERROR_INVALID_SYNTAX;
1515                 }
1516                 break;
1517
1518               case CONTEXT_NONE:
1519                 if (c == ';') {
1520                     NPT_CHECK(ResolveEntity(m_Entity, m_Text));
1521                     SetState(STATE_IN_CONTENT);
1522                 } else if (NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c)) {
1523                     m_Entity.Append(c);
1524                 } else {
1525                     return NPT_ERROR_INVALID_SYNTAX;
1526                 }
1527                 break;
1528                 
1529               default:
1530                 return NPT_ERROR_INVALID_SYNTAX;
1531             }
1532             break;
1533
1534           case STATE_IN_COMMENT:
1535             if (c == '-') {
1536                 SetState(STATE_IN_COMMENT_END_1);
1537             } else if (!NPT_XML_CHAR_IS_ANY_CHAR(c)) {
1538                 return NPT_ERROR_INVALID_SYNTAX;
1539             }
1540             break;
1541
1542           case STATE_IN_COMMENT_END_1:
1543             if (c == '-') {
1544                 SetState(STATE_IN_COMMENT_END_2);
1545             } else if (NPT_XML_CHAR_IS_ANY_CHAR(c)) {
1546                 SetState(STATE_IN_COMMENT);
1547             } else {
1548                 return NPT_ERROR_INVALID_SYNTAX;
1549             }
1550             break;
1551
1552           case STATE_IN_COMMENT_END_2:
1553             if (c == '>') {
1554                 SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1555             } else {
1556                 return NPT_ERROR_INVALID_SYNTAX;
1557             }
1558             break;
1559
1560           case STATE_IN_CONTENT:
1561             if (c == '<') {
1562                 SetState(STATE_IN_TAG_START, CONTEXT_NONE);
1563             } else if (c == '&') {
1564                 m_Entity.Reset();
1565                 SetState(STATE_IN_ENTITY_REF);
1566             } else {
1567                 m_Text.Append(c);
1568             }
1569             break;
1570
1571           case STATE_IN_PROCESSING_INSTRUCTION_START:
1572             break;
1573
1574           case STATE_IN_PROCESSING_INSTRUCTION_END:
1575             if (c == '>') {
1576                 SetState(STATE_IN_WHITESPACE, CONTEXT_NONE);
1577             } else {
1578                 return NPT_ERROR_INVALID_SYNTAX;
1579             }
1580             break;
1581
1582           case STATE_IN_PROCESSING_INSTRUCTION:
1583             if (c == '?') {
1584                 SetState(STATE_IN_PROCESSING_INSTRUCTION_END);
1585             }
1586             break;
1587
1588           case STATE_IN_DTD:
1589             if (NPT_XML_CHAR_IS_WHITESPACE(c)) break;
1590             if (c == '[') {
1591                 SetState(STATE_IN_DTD_MARKUP_DECL);
1592             } else if (c == '>') {
1593                 SetState(STATE_IN_WHITESPACE, CONTEXT_NONE);
1594             }
1595             break;
1596
1597           case STATE_IN_DTD_MARKUP_DECL:
1598             if (c == ']') {
1599                 SetState(STATE_IN_DTD_MARKUP_DECL_END);
1600             }
1601             break;
1602
1603           case STATE_IN_DTD_MARKUP_DECL_END:
1604             if (c == '>') {
1605                 SetState(STATE_IN_WHITESPACE, CONTEXT_NONE);
1606             } else if (!NPT_XML_CHAR_IS_WHITESPACE(c)) {
1607                 return NPT_ERROR_INVALID_SYNTAX;
1608             }
1609             break;
1610
1611           case STATE_IN_CDATA:
1612             if (c == ']') {
1613                 SetState(STATE_IN_CDATA_END_1);
1614             } else  {
1615                 m_Text.Append(c);
1616             }
1617             break;
1618
1619           case STATE_IN_CDATA_END_1:
1620             if (c == ']') {
1621                 SetState(STATE_IN_CDATA_END_2);
1622             } else {
1623                 m_Text.Append(']');
1624                 m_Text.Append(c);
1625                 SetState(STATE_IN_CDATA);
1626             }
1627             break;
1628
1629           case STATE_IN_CDATA_END_2:
1630             if (c == '>') {
1631                 SetState(STATE_IN_CONTENT, CONTEXT_NONE);
1632             } else {
1633                 m_Text.Append("]]");
1634                 m_Text.Append(c);
1635                 SetState(STATE_IN_CDATA);
1636             }
1637             break;
1638
1639           case STATE_IN_SPECIAL:
1640             if (c == '>') {
1641                 SetState(STATE_IN_WHITESPACE, CONTEXT_NONE);
1642             }
1643             break;
1644         }
1645     }
1646
1647     return NPT_SUCCESS;
1648 }       
1649
1650 /*----------------------------------------------------------------------
1651 |   NPT_XmlParser::NPT_XmlParser
1652 +---------------------------------------------------------------------*/
1653 NPT_XmlParser::NPT_XmlParser(bool keep_whitespace /* = false */) :
1654     m_Tree(NULL),
1655     m_CurrentElement(NULL),
1656     m_KeepWhitespace(keep_whitespace)
1657 {
1658     m_Processor = new NPT_XmlProcessor(this);
1659 }
1660
1661 /*----------------------------------------------------------------------
1662 |   NPT_XmlParser::~NPT_XmlParser
1663 +---------------------------------------------------------------------*/
1664 NPT_XmlParser::~NPT_XmlParser()
1665 {
1666     delete m_Processor;
1667 }
1668
1669 /*----------------------------------------------------------------------
1670 |   NPT_XmlParser::Parse
1671 +---------------------------------------------------------------------*/
1672 NPT_Result
1673 NPT_XmlParser::Parse(NPT_InputStream& stream, 
1674                      NPT_Size&        size,
1675                      NPT_XmlNode*&    node,
1676                      bool             incremental /* = false */)
1677 {       
1678     NPT_Result result;
1679
1680     // reset the tree unless we're in incremental mode
1681     if (!incremental) m_Tree = NULL;
1682
1683     // provide a default return node in case of error
1684     node = m_Tree;
1685
1686     // use a  buffer on the stack
1687     char buffer[256];
1688
1689     // read a buffer and parse it until the end of the stream
1690     NPT_Size max_bytes_to_read = size;
1691     size = 0;
1692     do {
1693         NPT_Size bytes_read;
1694         NPT_Size bytes_to_read = sizeof(buffer);
1695         if (max_bytes_to_read != 0 && 
1696             size+bytes_to_read > max_bytes_to_read) {
1697             bytes_to_read = max_bytes_to_read-size;
1698         }
1699         result = stream.Read(buffer, sizeof(buffer), &bytes_read);
1700         if (NPT_SUCCEEDED(result)) {
1701             // update the counter
1702             size += bytes_read;
1703
1704             // parse the buffer
1705             NPT_CHECK(m_Processor->ProcessBuffer(buffer, bytes_read));
1706         } else {
1707             if (result == NPT_ERROR_WOULD_BLOCK && incremental) break;
1708             if (result != NPT_ERROR_EOS) return result;
1709         }
1710     } while(NPT_SUCCEEDED(result) && 
1711             (max_bytes_to_read == 0 || size < max_bytes_to_read));
1712
1713     // return a tree if we have one 
1714     node = m_Tree;
1715
1716     if (incremental) {
1717         return result;
1718     } else {
1719         return m_Tree?NPT_SUCCESS:NPT_FAILURE;
1720     }
1721 }
1722
1723 /*----------------------------------------------------------------------
1724 |   NPT_XmlParser::Parse
1725 +---------------------------------------------------------------------*/
1726 NPT_Result
1727 NPT_XmlParser::Parse(NPT_InputStream& stream, 
1728                      NPT_XmlNode*&    node,
1729                      bool             incremental /* = false */)
1730 {
1731     NPT_Size max_read = 0; // no limit
1732     return Parse(stream, max_read, node, incremental);
1733 }
1734
1735 /*----------------------------------------------------------------------
1736 |   NPT_XmlParser::Parse
1737 +---------------------------------------------------------------------*/
1738 NPT_Result
1739 NPT_XmlParser::Parse(const char*   xml, 
1740                      NPT_XmlNode*& node, 
1741                      bool          incremental /* = false */)
1742 {       
1743     NPT_Size size = NPT_StringLength(xml);
1744
1745     return Parse(xml, size, node, incremental);
1746 }
1747
1748 /*----------------------------------------------------------------------
1749 |   NPT_XmlParser::Parse
1750 +---------------------------------------------------------------------*/
1751 NPT_Result
1752 NPT_XmlParser::Parse(const char*   xml, 
1753                      NPT_Size      size, 
1754                      NPT_XmlNode*& node,
1755                      bool          incremental /* = false */)
1756
1757     // reset the tree unless we're in incremental mode
1758     if (!incremental) m_Tree = NULL;
1759
1760     // provide a default return node in case of error
1761     node = m_Tree;
1762
1763     // parse the buffer
1764     NPT_CHECK(m_Processor->ProcessBuffer(xml, size));
1765
1766     // return a tree if we have one 
1767     node = m_Tree;
1768
1769     if (incremental) {
1770         return NPT_SUCCESS;
1771     } else {
1772         return m_Tree?NPT_SUCCESS:NPT_FAILURE;
1773     }
1774 }
1775
1776 /*----------------------------------------------------------------------
1777 |   NPT_XmlParser::OnStartElement
1778 +---------------------------------------------------------------------*/
1779 NPT_Result 
1780 NPT_XmlParser::OnStartElement(const char* name)
1781 {
1782     NPT_XML_Debug_1("\nNPT_XmlParser::OnStartElement: %s\n", name);
1783
1784     // create new node
1785     NPT_XmlElementNode* node = new NPT_XmlElementNode(name);
1786
1787     // add node to tree
1788     if (m_CurrentElement) {
1789         // add the new node
1790         m_CurrentElement->AddChild(node);
1791     }
1792     m_CurrentElement = node;
1793
1794     return NPT_SUCCESS;
1795 }
1796
1797 /*----------------------------------------------------------------------
1798 |   NPT_XmlParser::OnElementAttribute
1799 +---------------------------------------------------------------------*/
1800 NPT_Result 
1801 NPT_XmlParser::OnElementAttribute(const char* name, const char* value)
1802 {
1803     NPT_XML_Debug_2("\nNPT_XmlParser::OnElementAttribute: name=%s, value='%s'\n", 
1804                     name, value);
1805
1806     if (m_CurrentElement == NULL) {
1807         return NPT_ERROR_INVALID_SYNTAX;
1808     }
1809                               
1810     // check if this is a namespace attribute
1811     if (name[0] == 'x' && 
1812         name[1] == 'm' && 
1813         name[2] == 'l' && 
1814         name[3] == 'n' &&
1815         name[4] == 's' &&
1816         (name[5] == '\0' || name[5] == ':')) {
1817         // namespace definition
1818         m_CurrentElement->SetNamespaceUri((name[5] == ':')?name+6:"", value);
1819     } else {
1820         m_CurrentElement->AddAttribute(name, value);
1821     }
1822
1823     return NPT_SUCCESS;
1824 }
1825
1826 /*----------------------------------------------------------------------
1827 |   NPT_XmlParser::OnEndElement
1828 +---------------------------------------------------------------------*/
1829 NPT_Result 
1830 NPT_XmlParser::OnEndElement(const char* name)
1831 {
1832     NPT_XML_Debug_1("\nNPT_XmlParser::OnEndElement: %s\n", name ? name : "NULL");
1833
1834     if (m_CurrentElement == NULL) return NPT_ERROR_XML_TAG_MISMATCH;
1835
1836     // check that the name matches (if there is a name)
1837     if (name) {
1838         const char*  prefix = name;
1839         unsigned int prefix_length = 0;
1840         const char*  tag    = name;
1841         const char*  cursor = name;
1842         while (char c = *cursor++) {
1843             if (c == ':') {
1844                 prefix_length = (unsigned int)(cursor-name)-1;
1845                 tag = cursor;
1846             }
1847         }
1848         // check that the name and prefix length match
1849         if (m_CurrentElement->GetTag() != tag ||
1850             m_CurrentElement->GetPrefix().GetLength() != prefix_length) {
1851             return NPT_ERROR_XML_TAG_MISMATCH;
1852         }
1853
1854         // check the prefix
1855         const char* current_prefix = m_CurrentElement->GetPrefix().GetChars();
1856         for (unsigned int i=0; i<prefix_length; i++) {
1857             if (current_prefix[i] != prefix[i]) {
1858                 return NPT_ERROR_XML_TAG_MISMATCH;
1859             }
1860         }
1861     }
1862
1863     // pop up the stack
1864     NPT_XmlNode* parent = m_CurrentElement->GetParent();
1865     if (parent == NULL) {
1866         m_Tree = m_CurrentElement;
1867     }
1868     m_CurrentElement = parent ? parent->AsElementNode() : NULL;
1869
1870     return NPT_SUCCESS;
1871 }
1872
1873 /*----------------------------------------------------------------------
1874 |   NPT_XmlParser::OnCharacterData
1875 +---------------------------------------------------------------------*/
1876 NPT_Result
1877 NPT_XmlParser::OnCharacterData(const char* data, unsigned long size)
1878
1879     NPT_XML_Debug_1("\nNPT_XmlParser::OnCharacterData: %s\n", data);
1880     
1881     // check that we have a current element
1882     if (m_CurrentElement == NULL) {
1883         // we do not allow non-whitespace outside an element content
1884         if (!NPT_XmlStringIsWhitespace(data, size)) {
1885             return NPT_ERROR_XML_INVALID_NESTING;
1886         }
1887
1888         // ignore whitespace
1889         return NPT_SUCCESS;
1890     }
1891
1892     // ignore whitespace if applicable
1893     if (m_KeepWhitespace || !NPT_XmlStringIsWhitespace(data, size)) {
1894         // add the text to the current element
1895         m_CurrentElement->AddText(data);
1896     }
1897
1898     return NPT_SUCCESS;
1899 }
1900
1901 /*----------------------------------------------------------------------
1902 |   NPT_XmlAttributeWriter
1903 +---------------------------------------------------------------------*/
1904 class NPT_XmlAttributeWriter
1905 {
1906 public:
1907     NPT_XmlAttributeWriter(NPT_XmlSerializer& serializer) : m_Serializer(serializer) {}
1908     void operator()(NPT_XmlAttribute*& attribute) const {
1909         m_Serializer.Attribute(attribute->GetPrefix(),
1910                                attribute->GetName(),
1911                                attribute->GetValue());
1912     }
1913
1914 private:
1915     // members
1916     NPT_XmlSerializer& m_Serializer;
1917 };
1918
1919 /*----------------------------------------------------------------------
1920 |   NPT_XmlNodeWriter
1921 +---------------------------------------------------------------------*/
1922 class NPT_XmlNodeWriter
1923 {
1924 public:
1925     NPT_XmlNodeWriter(NPT_XmlSerializer& serializer) : 
1926         m_Serializer(serializer), m_AttributeWriter(serializer) {
1927         m_Serializer.StartDocument();
1928     }
1929     void operator()(NPT_XmlNode*& node) const {
1930         if (NPT_XmlElementNode* element = node->AsElementNode()) {
1931             const NPT_String& prefix = element->GetPrefix();
1932             const NPT_String& tag    = element->GetTag();
1933             m_Serializer.StartElement(prefix, tag);
1934             element->GetAttributes().Apply(m_AttributeWriter);
1935
1936             // emit namespace attributes
1937             if (element->m_NamespaceMap) {
1938                 NPT_List<NPT_XmlNamespaceMap::Entry*>::Iterator item = 
1939                     element->m_NamespaceMap->m_Entries.GetFirstItem();
1940                 while (item) {
1941                     if ((*item)->m_Prefix.IsEmpty()) {
1942                         // default namespace
1943                         m_Serializer.Attribute(NULL, "xmlns", (*item)->m_Uri);
1944                     } else {
1945                         // namespace with prefix
1946                         m_Serializer.Attribute("xmlns", (*item)->m_Prefix, (*item)->m_Uri);
1947                     }
1948                     ++item;
1949                 }
1950             }
1951
1952             element->GetChildren().Apply(*this);
1953             m_Serializer.EndElement(prefix, tag);
1954         } else if (NPT_XmlTextNode* text = node->AsTextNode()) {
1955             m_Serializer.Text(text->GetString());
1956         }
1957     }
1958
1959 private:
1960     // members
1961     NPT_XmlSerializer&     m_Serializer;
1962     NPT_XmlAttributeWriter m_AttributeWriter;
1963 };
1964
1965 /*----------------------------------------------------------------------
1966 |   NPT_XmlNodeCanonicalWriter
1967 +---------------------------------------------------------------------*/
1968 class NPT_XmlNodeCanonicalWriter
1969 {
1970 public:
1971     // types
1972     struct MapChainLink {
1973         MapChainLink(MapChainLink* parent) : m_Parent(parent) {}
1974         MapChainLink*                   m_Parent;
1975         NPT_Map<NPT_String, NPT_String> m_RenderedNamespaces;
1976     };
1977
1978     // constructor
1979     NPT_XmlNodeCanonicalWriter(NPT_XmlSerializer& serializer, 
1980                                MapChainLink*      map_chain = NULL) : 
1981         m_MapChain(map_chain),
1982         m_Serializer(serializer) {
1983         m_Serializer.StartDocument();
1984     }
1985     void operator()(NPT_XmlNode*& node) const;
1986
1987 private:
1988     // types
1989     struct SortedAttributeList {
1990         // types
1991         struct Entry {
1992             const NPT_String*       m_NamespaceUri;
1993             const NPT_XmlAttribute* m_Attribute;
1994         };
1995
1996         // methods
1997         void Add(const NPT_String* namespace_uri, 
1998                  const NPT_XmlAttribute* attribute);
1999         void Emit(NPT_XmlSerializer& serializer);
2000
2001         // members
2002         NPT_List<Entry> m_Entries;
2003     };
2004
2005     struct SortedNamespaceList {
2006         // types
2007         struct Entry {
2008             const NPT_String* m_NamespacePrefix;
2009             const NPT_String* m_NamespaceUri;
2010         };
2011
2012         // methods
2013         void Add(const NPT_String* prefix, const NPT_String* uri);
2014         void Emit(NPT_XmlSerializer& serializer);
2015
2016         // members
2017         NPT_List<Entry> m_Entries;
2018     };
2019
2020     // methods
2021     const NPT_String* GetNamespaceRenderedForPrefix(const NPT_String& prefix) const;
2022
2023     // members
2024     MapChainLink*      m_MapChain;
2025     NPT_XmlSerializer& m_Serializer;
2026 };
2027
2028 /*----------------------------------------------------------------------
2029 |   NPT_XmlNodeCanonicalWriter::SortedAttributeList::Add
2030 +---------------------------------------------------------------------*/
2031 void
2032 NPT_XmlNodeCanonicalWriter::SortedAttributeList::Add(
2033     const NPT_String*       namespace_uri,
2034     const NPT_XmlAttribute* attribute)
2035 {
2036     // transform empty strings into NULL pointers
2037     if (namespace_uri && namespace_uri->IsEmpty()) namespace_uri = NULL;
2038
2039     // find the namespace insertion position
2040     NPT_List<Entry>::Iterator entry = m_Entries.GetFirstItem();
2041     for (; entry; ++entry) {
2042         // decide if we insert now or move on
2043         const NPT_String* other_namespace_uri = entry->m_NamespaceUri;
2044         if (namespace_uri &&
2045             (other_namespace_uri == NULL || *namespace_uri > *other_namespace_uri)) {
2046             // this namespace uri is greater than the other, skip
2047             continue;
2048         } else if ((namespace_uri == NULL && other_namespace_uri == NULL) ||
2049                    (namespace_uri && other_namespace_uri && 
2050                    *namespace_uri == *other_namespace_uri)) {
2051             // namespace uris match, compare the names
2052             const NPT_XmlAttribute* other_attribute = entry->m_Attribute;
2053             if (attribute->GetName() > other_attribute->GetName()) continue;
2054         }
2055         break;
2056     }
2057     
2058     Entry new_entry = {namespace_uri, attribute};
2059     m_Entries.Insert(entry, new_entry);
2060 }
2061
2062 /*----------------------------------------------------------------------
2063 |   NPT_XmlNodeCanonicalWriter::SortedAttributeList::Emit
2064 +---------------------------------------------------------------------*/
2065 void
2066 NPT_XmlNodeCanonicalWriter::SortedAttributeList::Emit(NPT_XmlSerializer& serializer)
2067 {
2068     for (NPT_List<Entry>::Iterator i = m_Entries.GetFirstItem(); i; ++i) {
2069         serializer.Attribute(i->m_Attribute->GetPrefix(),
2070                              i->m_Attribute->GetName(),
2071                              i->m_Attribute->GetValue());
2072     }
2073 }
2074
2075 /*----------------------------------------------------------------------
2076 |   NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Add
2077 +---------------------------------------------------------------------*/
2078 void
2079 NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Add(const NPT_String* prefix,
2080                                                      const NPT_String* uri)
2081 {
2082     // find the namespace insertion position
2083     NPT_List<Entry>::Iterator entry = m_Entries.GetFirstItem();
2084     if (prefix && !prefix->IsEmpty()) {
2085         for (; entry; ++entry) {
2086             // decide if we insert now or move on
2087             if (entry->m_NamespacePrefix && *prefix <= *entry->m_NamespacePrefix) {
2088                 break;
2089             }
2090         }
2091     } else {
2092         prefix = NULL;
2093     }
2094
2095     Entry new_entry = {prefix, uri};
2096     m_Entries.Insert(entry, new_entry);
2097 }
2098
2099 /*----------------------------------------------------------------------
2100 |   NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Emit
2101 +---------------------------------------------------------------------*/
2102 void
2103 NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Emit(NPT_XmlSerializer& serializer)
2104 {
2105     for (NPT_List<Entry>::Iterator i = m_Entries.GetFirstItem(); i; ++i) {
2106         const NPT_String* key   = i->m_NamespacePrefix;
2107         const NPT_String* value = i->m_NamespaceUri;
2108         if (key == NULL) {
2109             serializer.Attribute(NULL, "xmlns", *value);
2110         } else if (*key != "xml" || *value != NPT_XmlNamespaceUri_Xml) {
2111             serializer.Attribute("xmlns", *key, *value);
2112         }
2113     }
2114 }
2115
2116 /*----------------------------------------------------------------------
2117 |   NPT_XmlNodeCanonicalWriter::GetNamespaceRenderedForPrefix
2118 +---------------------------------------------------------------------*/
2119 const NPT_String*
2120 NPT_XmlNodeCanonicalWriter::GetNamespaceRenderedForPrefix(const NPT_String& prefix) const
2121 {
2122     for (MapChainLink* link = m_MapChain;
2123          link;
2124          link = link->m_Parent) {
2125         NPT_String* uri;
2126         if (NPT_SUCCEEDED(link->m_RenderedNamespaces.Get(prefix, uri))) {
2127             return uri;
2128         }
2129     }
2130
2131     return NULL;
2132 }
2133
2134 /*----------------------------------------------------------------------
2135 |   NPT_XmlNodeCanonicalWriter::operator()
2136 +---------------------------------------------------------------------*/
2137 void
2138 NPT_XmlNodeCanonicalWriter::operator()(NPT_XmlNode*& node) const
2139 {
2140     MapChainLink map_link(m_MapChain);
2141
2142     if (NPT_XmlElementNode* element = node->AsElementNode()) {
2143         const NPT_String& prefix = element->GetPrefix();
2144         const NPT_String& tag    = element->GetTag();
2145
2146         // process namespaces
2147         const NPT_String* namespace_uri = element->GetNamespace();
2148         const NPT_String* rendered = GetNamespaceRenderedForPrefix(prefix);
2149         if (namespace_uri && namespace_uri->IsEmpty()) namespace_uri = NULL;
2150         if (prefix.IsEmpty()) {
2151             // default namespace
2152             if (rendered == NULL) {
2153                 // default namespace not rendered
2154                 if (namespace_uri) {
2155                     map_link.m_RenderedNamespaces.Put("", *namespace_uri);
2156                 }
2157             } else {
2158                 // default namespace already rendered
2159                 const char* compare;
2160                 if (namespace_uri) {
2161                     compare = namespace_uri->GetChars();
2162                 } else {
2163                     compare = "";
2164                 }
2165                 if (*rendered != compare) {
2166                     // the rendered default namespace had a different uri
2167                     map_link.m_RenderedNamespaces.Put("", compare);
2168                 } 
2169             }
2170         } else {
2171             // explicit namespace
2172             // NOTE: namespace_uri should not be an empty string, but we test just
2173             // in case the XML document is not compliant
2174             if (namespace_uri && (rendered == NULL || *rendered != *namespace_uri)) {
2175                 // namespace prefix not rendered or rendered with a different value
2176                 map_link.m_RenderedNamespaces.Put(prefix, *namespace_uri);
2177             }
2178         }
2179
2180         // process attributes
2181         SortedAttributeList prefixed_attributes;
2182         SortedAttributeList naked_attributes;
2183         for (NPT_List<NPT_XmlAttribute*>::Iterator attribute = element->GetAttributes().GetFirstItem();
2184              attribute;
2185              ++attribute) {
2186              const NPT_String& a_prefix = (*attribute)->GetPrefix();
2187              if (a_prefix.IsEmpty()) {
2188                  // naked attribute
2189                  naked_attributes.Add(NULL, *attribute);
2190              } else {
2191                 // decide if we need to render this namespace declaration
2192                 namespace_uri = element->GetNamespaceUri(a_prefix);
2193                 if (namespace_uri) {
2194                     rendered = GetNamespaceRenderedForPrefix(a_prefix);;
2195                     if (rendered == NULL || *rendered != *namespace_uri) {
2196                         // namespace not rendered or rendered with a different value
2197                         map_link.m_RenderedNamespaces.Put(a_prefix, *namespace_uri);
2198                     }
2199                     prefixed_attributes.Add(namespace_uri, *attribute);
2200                 }
2201              }
2202         }
2203
2204         // start of element
2205         m_Serializer.StartElement(prefix, tag);
2206                     
2207         // namespace declarations
2208         if (map_link.m_RenderedNamespaces.GetEntryCount()) {
2209             SortedNamespaceList namespaces;
2210             NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = 
2211                 map_link.m_RenderedNamespaces.GetEntries().GetFirstItem();
2212             while (entry) {
2213                 const NPT_String& key   = (*entry)->GetKey();
2214                 const NPT_String& value = (*entry)->GetValue();
2215                 namespaces.Add(&key, &value);
2216                 ++entry;
2217             }
2218             namespaces.Emit(m_Serializer);
2219         }
2220
2221         // attributes
2222         naked_attributes.Emit(m_Serializer);
2223         prefixed_attributes.Emit(m_Serializer);
2224
2225         // children
2226         MapChainLink* chain;
2227         if (map_link.m_RenderedNamespaces.GetEntryCount()) {
2228             chain = &map_link;
2229         } else {
2230             chain = m_MapChain;
2231         }
2232         element->GetChildren().Apply(NPT_XmlNodeCanonicalWriter(m_Serializer, chain));
2233
2234         // end of element
2235         m_Serializer.EndElement(prefix, tag);
2236     } else if (NPT_XmlTextNode* text = node->AsTextNode()) {
2237         m_Serializer.Text(text->GetString());
2238     }
2239 }
2240
2241 /*----------------------------------------------------------------------
2242 |   NPT_XmlSerializer::NPT_XmlSerializer
2243 +---------------------------------------------------------------------*/
2244 NPT_XmlSerializer::NPT_XmlSerializer(NPT_OutputStream* output,
2245                                      NPT_Cardinal      indentation,
2246                                      bool              shrink_empty_elements) :
2247     m_Output(output),
2248     m_ElementPending(false),
2249     m_Depth(0),
2250     m_Indentation(indentation),
2251     m_ElementHasText(false),
2252     m_ShrinkEmptyElements(shrink_empty_elements)
2253 {
2254 }
2255
2256 /*----------------------------------------------------------------------
2257 |   NPT_XmlSerializer::~NPT_XmlSerializer
2258 +---------------------------------------------------------------------*/
2259 NPT_XmlSerializer::~NPT_XmlSerializer()
2260 {
2261 }
2262
2263 /*----------------------------------------------------------------------
2264 |   NPT_XmlSerializer::StartDocument
2265 +---------------------------------------------------------------------*/
2266 NPT_Result 
2267 NPT_XmlSerializer::StartDocument()
2268 {
2269     // this is required for some parsers
2270     return m_Output->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2271 }
2272
2273 /*----------------------------------------------------------------------
2274 |   NPT_XmlSerializer::EndDocument
2275 +---------------------------------------------------------------------*/
2276 NPT_Result 
2277 NPT_XmlSerializer::EndDocument()
2278 {
2279     return m_ElementPending?NPT_ERROR_INVALID_STATE:NPT_SUCCESS;
2280 }
2281
2282 /*----------------------------------------------------------------------
2283 |   NPT_XmlSerializer::EscapeChar
2284 +---------------------------------------------------------------------*/
2285 void  
2286 NPT_XmlSerializer::EscapeChar(unsigned char c, char* text)
2287 {
2288     *text++ = '&';
2289     *text++ = '#';
2290     *text++ = 'x';
2291     int c0 = c>>4;
2292     int c1 = c&0xF;
2293     if (c0) {
2294         *text++ = c0 >= 10 ? 'A'+(c0-10) : '0'+c0;
2295     }
2296     *text++ = c1 >= 10 ? 'A'+(c1-10) : '0'+c1;
2297     *text++ = ';';
2298     *text   = '\0';
2299 }
2300
2301 /*----------------------------------------------------------------------
2302 |   NPT_XmlSerializer::ProcessPending
2303 +---------------------------------------------------------------------*/
2304 NPT_Result  
2305 NPT_XmlSerializer::ProcessPending()
2306 {
2307     if (!m_ElementPending) return NPT_SUCCESS;
2308     m_ElementPending = false;
2309     return m_Output->Write(">", 1);
2310 }
2311
2312 /*----------------------------------------------------------------------
2313 |   NPT_XmlSerializer::OutputEscapedString
2314 +---------------------------------------------------------------------*/
2315 NPT_Result  
2316 NPT_XmlSerializer::OutputEscapedString(const char* text, bool attribute)
2317 {
2318     const char* start = text;
2319     char escaped[7];
2320     while (char c = *text) {
2321         const char* insert = NULL;
2322         switch (c) {
2323             case '\r': {
2324                 EscapeChar(c, escaped);
2325                 insert = escaped;
2326                 break;
2327             }
2328             case '\n':
2329             case '\t':
2330                 if (attribute) {
2331                     EscapeChar(c, escaped);
2332                     insert = escaped;
2333                 }
2334                 break;
2335
2336             case '&' : insert = "&amp;"; break;
2337             case '<' : insert = "&lt;";  break;
2338             case '>' : if (!attribute) insert = "&gt;";  break;
2339             case '"' : if (attribute) insert = "&quot;"; break;
2340             default : 
2341                 break;
2342         }
2343         if (insert) {
2344             // output pending chars
2345             if (start != text) m_Output->Write(start, (NPT_Size)(text-start));
2346             m_Output->WriteString(insert);
2347             start = ++text;
2348         } else {
2349             ++text;
2350         }
2351     }
2352     if (start != text) {
2353         m_Output->Write(start, (NPT_Size)(text-start));
2354     }
2355
2356     return NPT_SUCCESS;
2357 }
2358
2359 /*----------------------------------------------------------------------
2360 |   NPT_XmlSerializer::OutputIndentation
2361 +---------------------------------------------------------------------*/
2362 void
2363 NPT_XmlSerializer::OutputIndentation(bool start)
2364 {
2365     if (m_Depth || !start) m_Output->Write("\n", 1);
2366
2367     // ensure we have enough chars in the prefix string
2368     unsigned int prefix_length = m_Indentation*m_Depth;
2369     if (m_IndentationPrefix.GetLength() < prefix_length) {
2370         unsigned int needed = prefix_length-m_IndentationPrefix.GetLength();
2371         for (unsigned int i=0; i<needed; i+=16) {
2372             m_IndentationPrefix.Append("                ", 16);
2373         }
2374     }
2375
2376     // print the indentation prefix
2377     m_Output->Write(m_IndentationPrefix.GetChars(), prefix_length);
2378 }
2379
2380 /*----------------------------------------------------------------------
2381 |   NPT_XmlSerializer::StartElement
2382 +---------------------------------------------------------------------*/
2383 NPT_Result  
2384 NPT_XmlSerializer::StartElement(const char* prefix, const char* name)
2385 {
2386     ProcessPending();
2387     if (m_Indentation) OutputIndentation(true);
2388     m_ElementPending = true;
2389     m_ElementHasText = false;
2390     m_Depth++;
2391     m_Output->Write("<", 1);
2392     if (prefix && prefix[0]) {
2393         m_Output->WriteString(prefix);
2394         m_Output->Write(":", 1);
2395     }
2396     return m_Output->WriteString(name);
2397 }
2398
2399 /*----------------------------------------------------------------------
2400 |   NPT_XmlSerializer::EndElement
2401 +---------------------------------------------------------------------*/
2402 NPT_Result  
2403 NPT_XmlSerializer::EndElement(const char* prefix, const char* name)
2404 {
2405     m_Depth--;
2406
2407     if (m_ElementPending) {
2408         // this element has no children
2409         m_ElementPending = false;
2410         if (m_ShrinkEmptyElements) {
2411             return m_Output->Write("/>", 2);
2412         } else {
2413             m_Output->Write(">",1);
2414         }
2415     } 
2416
2417     if (m_Indentation && !m_ElementHasText) OutputIndentation(false);
2418     m_ElementHasText = false;
2419     m_Output->Write("</", 2);
2420     if (prefix && prefix[0]) {
2421         m_Output->WriteString(prefix);
2422         m_Output->Write(":", 1);
2423     }
2424     m_Output->WriteString(name);
2425     return m_Output->Write(">", 1);
2426 }
2427
2428 /*----------------------------------------------------------------------
2429 |   NPT_XmlSerializer::Attribute
2430 +---------------------------------------------------------------------*/
2431 NPT_Result  
2432 NPT_XmlSerializer::Attribute(const char* prefix, const char* name, const char* value)
2433 {
2434     m_Output->Write(" ", 1);
2435     if (prefix && prefix[0]) {
2436         m_Output->WriteString(prefix);
2437         m_Output->Write(":", 1);
2438     }
2439     m_Output->WriteString(name);
2440     m_Output->Write("=\"", 2);
2441     OutputEscapedString(value, true);
2442     return m_Output->Write("\"", 1);
2443 }
2444
2445 /*----------------------------------------------------------------------
2446 |   NPT_XmlSerializer::Text
2447 +---------------------------------------------------------------------*/
2448 NPT_Result  
2449 NPT_XmlSerializer::Text(const char* text)
2450 {
2451     ProcessPending();
2452     m_ElementHasText = true;
2453     return OutputEscapedString(text, false);
2454 }
2455
2456 /*----------------------------------------------------------------------
2457 |   NPT_XmlSerializer::CdataSection
2458 +---------------------------------------------------------------------*/
2459 NPT_Result  
2460 NPT_XmlSerializer::CdataSection(const char* data)
2461 {
2462     ProcessPending();
2463     m_ElementHasText = true;
2464     m_Output->Write("<![CDATA[", 9);
2465     m_Output->WriteString(data);
2466     return m_Output->Write("]]>", 3);
2467 }
2468
2469 /*----------------------------------------------------------------------
2470 |   NPT_XmlSerializer::Comment
2471 +---------------------------------------------------------------------*/
2472 NPT_Result  
2473 NPT_XmlSerializer::Comment(const char* comment)
2474 {
2475     ProcessPending();
2476     m_Output->Write("<!--", 4);
2477     m_Output->WriteString(comment);
2478     return m_Output->Write("-->", 3);
2479 }
2480
2481 /*----------------------------------------------------------------------
2482 |   NPT_XmlWriter::Serialize
2483 +---------------------------------------------------------------------*/
2484 NPT_Result
2485 NPT_XmlWriter::Serialize(NPT_XmlNode& node, NPT_OutputStream& output)
2486 {
2487     NPT_XmlSerializer serializer(&output, m_Indentation);
2488     NPT_XmlNodeWriter node_writer(serializer);
2489     NPT_XmlNode* node_pointer = &node;
2490     node_writer(node_pointer);
2491
2492     return NPT_SUCCESS;
2493 }
2494
2495 /*----------------------------------------------------------------------
2496 |   NPT_XmlCanonicalizer::Serialize
2497 +---------------------------------------------------------------------*/
2498 NPT_Result
2499 NPT_XmlCanonicalizer::Serialize(NPT_XmlNode& node, NPT_OutputStream& output)
2500 {
2501     // create a serializer with no indentation and no shrinking of empty elements
2502     NPT_XmlSerializer serializer(&output, 0, false);
2503
2504     // serialize the node
2505     NPT_XmlNodeCanonicalWriter node_writer(serializer);
2506     NPT_XmlNo