1 /*****************************************************************
3 | Neptune - Xml Support
5 | Copyright (c) 2002-2008, Axiomatic Systems, LLC.
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.
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.
30 ****************************************************************/
32 /*----------------------------------------------------------------------
34 +---------------------------------------------------------------------*/
35 #include "NptConfig.h"
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)
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)
60 /*----------------------------------------------------------------------
62 +---------------------------------------------------------------------*/
63 static const NPT_String
64 NPT_XmlNamespaceUri_Xml("http://www.w3.org/XML/1998/namespace");
66 /*----------------------------------------------------------------------
67 | NPT_XmlAttributeFinder
68 +---------------------------------------------------------------------*/
69 class NPT_XmlAttributeFinder
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
,
77 const char* namespc
) :
78 m_Element(element
), m_Name(name
), m_Namespace(namespc
) {}
80 bool operator()(const NPT_XmlAttribute
* const & attribute
) const {
81 if (attribute
->m_Name
== m_Name
) {
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();
88 // match if the attribute has the SPECIFIC namespace
90 if (prefix
.IsEmpty()) {
91 // attributes without a prefix don't have a namespace
94 const NPT_String
* namespc
= m_Element
.GetNamespaceUri(prefix
);
95 return namespc
&& *namespc
== m_Namespace
;
99 // ANY namespace will match
108 const NPT_XmlElementNode
& m_Element
;
110 const char* m_Namespace
;
113 /*----------------------------------------------------------------------
114 | NPT_XmlAttributeFinderWithPrefix
115 +---------------------------------------------------------------------*/
116 class NPT_XmlAttributeFinderWithPrefix
119 NPT_XmlAttributeFinderWithPrefix(const char* prefix
, const char* name
) :
120 m_Prefix(prefix
?prefix
:""), m_Name(name
) {}
122 bool operator()(const NPT_XmlAttribute
* const & attribute
) const {
123 return attribute
->m_Prefix
== m_Prefix
&& attribute
->m_Name
== m_Name
;
127 const char* m_Prefix
;
131 /*----------------------------------------------------------------------
133 +---------------------------------------------------------------------*/
134 class NPT_XmlTagFinder
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
) {}
143 bool operator()(const NPT_XmlNode
* const & node
) const {
144 const NPT_XmlElementNode
* element
= node
->AsElementNode();
145 if (element
&& element
->m_Tag
== m_Tag
) {
147 // look for a SPECIFIC namespace or NO namespace
148 const NPT_String
* namespc
= element
->GetNamespace();
150 // the element has a namespace, match if it is equal to
151 // what we're looking for
152 return *namespc
== m_Namespace
;
154 // the element does not have a namespace, match if we're
155 // looking for NO namespace
156 return m_Namespace
[0] == '\0';
159 // ANY namespace will match
169 const char* m_Namespace
;
172 /*----------------------------------------------------------------------
174 +---------------------------------------------------------------------*/
175 class NPT_XmlTextFinder
178 bool operator()(const NPT_XmlNode
* const & node
) const {
179 return node
->AsTextNode() != NULL
;
183 /*----------------------------------------------------------------------
184 | NPT_XmlNamespaceCollapser
185 +---------------------------------------------------------------------*/
186 class NPT_XmlNamespaceCollapser
189 NPT_XmlNamespaceCollapser(NPT_XmlElementNode
* element
) :
192 void operator()(NPT_XmlNode
*& node
) const {
193 NPT_XmlElementNode
* element
= node
->AsElementNode();
194 if (element
== NULL
) return;
196 // collapse the namespace for this element
197 CollapseNamespace(element
, element
->GetPrefix());
199 // collapse the namespaces for the attributes
200 NPT_List
<NPT_XmlAttribute
*>::Iterator item
= element
->GetAttributes().GetFirstItem();
202 NPT_XmlAttribute
* attribute
= *item
;
203 CollapseNamespace(element
, attribute
->GetPrefix());
207 // recurse to the children
208 element
->GetChildren().Apply(*this);
213 void CollapseNamespace(NPT_XmlElementNode
* element
, const NPT_String
& prefix
) const;
216 NPT_XmlElementNode
* m_Root
;
219 /*----------------------------------------------------------------------
220 | NPT_XmlNamespaceCollapser::CollapseNamespace
221 +---------------------------------------------------------------------*/
223 NPT_XmlNamespaceCollapser::CollapseNamespace(NPT_XmlElementNode
* element
,
224 const NPT_String
& prefix
) const
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());
234 /*----------------------------------------------------------------------
235 | NPT_XmlAttribute::NPT_XmlAttribute
236 +---------------------------------------------------------------------*/
237 NPT_XmlAttribute::NPT_XmlAttribute(const char* name
, const char* value
) :
240 const char* cursor
= name
;
241 while (char c
= *cursor
++) {
243 unsigned int prefix_length
= (unsigned int)(cursor
-name
)-1;
244 m_Prefix
.Assign(name
, prefix_length
);
252 /*----------------------------------------------------------------------
253 | NPT_XmlElementNode::NPT_XmlElementNode
254 +---------------------------------------------------------------------*/
255 NPT_XmlElementNode::NPT_XmlElementNode(const char* prefix
, const char* tag
) :
256 NPT_XmlNode(ELEMENT
),
259 m_NamespaceMap(NULL
),
260 m_NamespaceParent(NULL
)
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
)
272 const char* cursor
= tag
;
273 while (char c
= *cursor
++) {
275 unsigned int prefix_length
= (unsigned int)(cursor
-tag
)-1;
276 m_Prefix
.Assign(tag
, prefix_length
);
284 /*----------------------------------------------------------------------
285 | NPT_XmlElementNode::~NPT_XmlElementNode
286 +---------------------------------------------------------------------*/
287 NPT_XmlElementNode::~NPT_XmlElementNode()
289 m_Children
.Apply(NPT_ObjectDeleter
<NPT_XmlNode
>());
290 m_Attributes
.Apply(NPT_ObjectDeleter
<NPT_XmlAttribute
>());
291 delete m_NamespaceMap
;
294 /*----------------------------------------------------------------------
295 | NPT_XmlElementNode::SetParent
296 +---------------------------------------------------------------------*/
298 NPT_XmlElementNode::SetParent(NPT_XmlNode
* parent
)
303 // update out namespace linkage
304 NPT_XmlElementNode
* parent_element
=
305 parent
?parent
->AsElementNode():NULL
;
306 NPT_XmlElementNode
* namespace_parent
;
307 if (parent_element
) {
309 parent_element
->m_NamespaceMap
?
311 parent_element
->m_NamespaceParent
;
313 namespace_parent
= NULL
;
315 if (namespace_parent
!= m_NamespaceParent
) {
316 m_NamespaceParent
= namespace_parent
;
317 RelinkNamespaceMaps();
321 /*----------------------------------------------------------------------
322 | NPT_XmlElementNode::AddChild
323 +---------------------------------------------------------------------*/
325 NPT_XmlElementNode::AddChild(NPT_XmlNode
* child
)
327 if (child
== NULL
) return NPT_ERROR_INVALID_PARAMETERS
;
328 child
->SetParent(this);
329 return m_Children
.Add(child
);
332 /*----------------------------------------------------------------------
333 | NPT_XmlElementNode::GetChild
334 +---------------------------------------------------------------------*/
336 NPT_XmlElementNode::GetChild(const char* tag
, const char* namespc
, NPT_Ordinal n
) const
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
347 NPT_List
<NPT_XmlNode
*>::Iterator item
;
348 item
= m_Children
.Find(NPT_XmlTagFinder(tag
, namespc
), n
);
349 return item
?(*item
)->AsElementNode():NULL
;
352 /*----------------------------------------------------------------------
353 | NPT_XmlElementNode::AddAttribute
354 +---------------------------------------------------------------------*/
356 NPT_XmlElementNode::AddAttribute(const char* name
,
359 if (name
== NULL
|| value
== NULL
) return NPT_ERROR_INVALID_PARAMETERS
;
360 return m_Attributes
.Add(new NPT_XmlAttribute(name
, value
));
363 /*----------------------------------------------------------------------
364 | NPT_XmlElementNode::SetAttribute
365 +---------------------------------------------------------------------*/
367 NPT_XmlElementNode::SetAttribute(const char* prefix
,
371 if (name
== NULL
|| value
== NULL
) return NPT_ERROR_INVALID_PARAMETERS
;
373 /* see if this attribute is already set */
374 NPT_List
<NPT_XmlAttribute
*>::Iterator attribute
;
375 attribute
= m_Attributes
.Find(NPT_XmlAttributeFinderWithPrefix(prefix
, name
));
377 // an attribute with this name and prefix already exists,
379 (*attribute
)->SetValue(value
);
382 return m_Attributes
.Add(new NPT_XmlAttribute(prefix
, name
, value
));
385 /*----------------------------------------------------------------------
386 | NPT_XmlElementNode::SetAttribute
387 +---------------------------------------------------------------------*/
389 NPT_XmlElementNode::SetAttribute(const char* name
, const char* value
)
391 return SetAttribute(NULL
, name
, value
);
394 /*----------------------------------------------------------------------
395 | NPT_XmlElementNode::GetAttribute
396 +---------------------------------------------------------------------*/
398 NPT_XmlElementNode::GetAttribute(const char* name
, const char* namespc
) const
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
408 // find the attribute
409 NPT_List
<NPT_XmlAttribute
*>::Iterator attribute
;
410 attribute
= m_Attributes
.Find(NPT_XmlAttributeFinder(*this, name
, namespc
));
412 return &(*attribute
)->GetValue();
418 /*----------------------------------------------------------------------
419 | NPT_XmlElementNode::AddText
420 +---------------------------------------------------------------------*/
422 NPT_XmlElementNode::AddText(const char* text
)
424 return AddChild(new NPT_XmlTextNode(NPT_XmlTextNode::CHARACTER_DATA
, text
));
427 /*----------------------------------------------------------------------
428 | NPT_XmlElementNode::GetText
429 +---------------------------------------------------------------------*/
431 NPT_XmlElementNode::GetText(NPT_Ordinal n
) const
433 NPT_List
<NPT_XmlNode
*>::Iterator node
;
434 node
= m_Children
.Find(NPT_XmlTextFinder(), n
);
435 return node
?&(*node
)->AsTextNode()->GetString():NULL
;
438 /*----------------------------------------------------------------------
439 | NPT_XmlElementNode::MakeStandalone
440 +---------------------------------------------------------------------*/
442 NPT_XmlElementNode::MakeStandalone()
444 NPT_XmlNamespaceCollapser
collapser(this);
445 NPT_XmlNode
* node_pointer
= this;
446 collapser(node_pointer
);
451 /*----------------------------------------------------------------------
452 | NPT_XmlElementNode::RelinkNamespaceMaps
453 +---------------------------------------------------------------------*/
455 NPT_XmlElementNode::RelinkNamespaceMaps()
457 // update our children so that they can inherit the right
459 NPT_List
<NPT_XmlNode
*>::Iterator item
= m_Children
.GetFirstItem();
461 NPT_XmlElementNode
* element
= (*item
)->AsElementNode();
463 if (m_NamespaceMap
) {
464 // we have a map, so our children point to us
465 element
->SetNamespaceParent(this);
467 // we don't have a map, so our children point to
468 // where we also point
469 element
->SetNamespaceParent(m_NamespaceParent
);
476 /*----------------------------------------------------------------------
477 | NPT_XmlElementNode::SetNamespaceParent
478 +---------------------------------------------------------------------*/
480 NPT_XmlElementNode::SetNamespaceParent(NPT_XmlElementNode
* parent
)
482 m_NamespaceParent
= parent
;
483 RelinkNamespaceMaps();
486 /*----------------------------------------------------------------------
487 | NPT_XmlElementNode::SetNamespaceUri
488 +---------------------------------------------------------------------*/
490 NPT_XmlElementNode::SetNamespaceUri(const char* prefix
, const char* uri
)
492 // ensure that we have a namespace map
493 if (m_NamespaceMap
== NULL
) {
494 m_NamespaceMap
= new NPT_XmlNamespaceMap();
495 RelinkNamespaceMaps();
498 return m_NamespaceMap
->SetNamespaceUri(prefix
, uri
);
501 /*----------------------------------------------------------------------
502 | NPT_XmlElementNode::GetNamespaceUri
503 +---------------------------------------------------------------------*/
505 NPT_XmlElementNode::GetNamespaceUri(const char* prefix
) const
507 if (m_NamespaceMap
) {
508 // look in our namespace map first
509 const NPT_String
* namespc
= m_NamespaceMap
->GetNamespaceUri(prefix
);
511 if (namespc
->IsEmpty()) {
519 // look into our parent's namespace map
520 if (m_NamespaceParent
) {
521 return m_NamespaceParent
->GetNamespaceUri(prefix
);
523 // check if this is a well-known namespace
524 if (prefix
[0] == 'x' &&
528 return &NPT_XmlNamespaceUri_Xml
;
536 /*----------------------------------------------------------------------
537 | NPT_XmlElementNode::GetNamespace
538 +---------------------------------------------------------------------*/
540 NPT_XmlElementNode::GetNamespace() const
542 return GetNamespaceUri(m_Prefix
);
545 /*----------------------------------------------------------------------
546 | NPT_XmlElementNode::GetNamespacePrefix
547 +---------------------------------------------------------------------*/
549 NPT_XmlElementNode::GetNamespacePrefix(const char* uri
) const
551 NPT_XmlNamespaceMap
* namespace_map
=
555 m_NamespaceParent
->m_NamespaceMap
:
559 return namespace_map
->GetNamespacePrefix(uri
);
565 /*----------------------------------------------------------------------
566 | NPT_XmlTextNode::NPT_XmlTextNode
567 +---------------------------------------------------------------------*/
568 NPT_XmlTextNode::NPT_XmlTextNode(TokenType token_type
, const char* text
) :
570 m_TokenType(token_type
),
575 /*----------------------------------------------------------------------
577 +---------------------------------------------------------------------*/
578 class NPT_XmlAccumulator
{
580 NPT_XmlAccumulator();
581 ~NPT_XmlAccumulator();
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
; }
592 void Allocate(NPT_Size size
);
595 unsigned char* m_Buffer
;
596 NPT_Size m_Allocated
;
600 /*----------------------------------------------------------------------
601 | NPT_XmlAccumulator::NPT_XmlAccumulator
602 +---------------------------------------------------------------------*/
603 NPT_XmlAccumulator::NPT_XmlAccumulator() :
610 /*----------------------------------------------------------------------
611 | NPT_XmlAccumulator::~NPT_XmlAccumulator
612 +---------------------------------------------------------------------*/
613 NPT_XmlAccumulator::~NPT_XmlAccumulator()
618 /*----------------------------------------------------------------------
619 | NPT_XmlAccumulator::Allocate
620 +---------------------------------------------------------------------*/
622 NPT_XmlAccumulator::Allocate(NPT_Size size
)
624 // check if we have enough
625 if (m_Allocated
>= size
) return;
629 m_Allocated
= m_Allocated
? m_Allocated
* 2 : 32;
630 } while (m_Allocated
< size
);
633 unsigned char* new_buffer
= new unsigned char[m_Allocated
];
634 NPT_CopyMemory(new_buffer
, m_Buffer
, m_Valid
);
636 m_Buffer
= new_buffer
;
639 /*----------------------------------------------------------------------
640 | NPT_XmlAccumulator::Append
641 +---------------------------------------------------------------------*/
643 NPT_XmlAccumulator::Append(char c
)
645 NPT_Size needed
= m_Valid
+1;
646 if (needed
> m_Allocated
) Allocate(needed
);
647 m_Buffer
[m_Valid
++] = c
;
650 /*----------------------------------------------------------------------
651 | NPT_XmlAccumulator::Append
652 +---------------------------------------------------------------------*/
654 NPT_XmlAccumulator::Append(const char* s
)
657 while ((c
= *s
++)) Append(c
);
660 /*----------------------------------------------------------------------
661 | NPT_XmlAccumulator::AppendUTF8
662 +---------------------------------------------------------------------*/
664 NPT_XmlAccumulator::AppendUTF8(unsigned int c
)
666 NPT_Size needed
= m_Valid
+4; // allocate 4 more chars
667 if (needed
> m_Allocated
) Allocate(needed
);
670 // 000000-00007F -> 1 char = 0xxxxxxx
671 m_Buffer
[m_Valid
++] = (char)c
;
672 } else if (c
<= 0x7FF) {
673 // 000080-0007FF -> 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-00FFFF -> 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-10FFFF -> 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 );
690 /*----------------------------------------------------------------------
691 | NPT_XmlAccumulator::GetString
692 +---------------------------------------------------------------------*/
694 NPT_XmlAccumulator::GetString()
696 // ensure that the buffer is NULL terminated
698 m_Buffer
[m_Valid
] = '\0';
699 return (const char*)m_Buffer
;
702 /*----------------------------------------------------------------------
703 | NPT_XmlNamespaceMap::~NPT_XmlNamespaceMap
704 +---------------------------------------------------------------------*/
705 NPT_XmlNamespaceMap::~NPT_XmlNamespaceMap()
707 m_Entries
.Apply(NPT_ObjectDeleter
<Entry
>());
710 /*----------------------------------------------------------------------
711 | NPT_XmlNamespaceMap::SetNamespaceUri
712 +---------------------------------------------------------------------*/
714 NPT_XmlNamespaceMap::SetNamespaceUri(const char* prefix
, const char* uri
)
716 NPT_List
<Entry
*>::Iterator item
= m_Entries
.GetFirstItem();
718 if ((*item
)->m_Prefix
== prefix
) {
719 // the prefix is already in the map, update the value
720 (*item
)->m_Uri
= uri
;
726 // the prefix is not in the map, add it
727 return m_Entries
.Add(new Entry(prefix
, uri
));
730 /*----------------------------------------------------------------------
731 | NPT_XmlNamespaceMap::GetNamespaceUri
732 +---------------------------------------------------------------------*/
734 NPT_XmlNamespaceMap::GetNamespaceUri(const char* prefix
)
736 NPT_List
<Entry
*>::Iterator item
= m_Entries
.GetFirstItem();
738 if ((*item
)->m_Prefix
== prefix
) {
740 return &(*item
)->m_Uri
;
745 // the prefix is not in the map
749 /*----------------------------------------------------------------------
750 | NPT_XmlNamespaceMap::GetNamespacePrefix
751 +---------------------------------------------------------------------*/
753 NPT_XmlNamespaceMap::GetNamespacePrefix(const char* uri
)
755 NPT_List
<Entry
*>::Iterator item
= m_Entries
.GetFirstItem();
757 if ((*item
)->m_Uri
== uri
) {
759 return &(*item
)->m_Prefix
;
764 // the uri is not in the map
768 /*----------------------------------------------------------------------
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] = {
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 '%'
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 ';'
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 '~'
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
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
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
1039 #endif // defined(NPT_XML_USE_CHAR_MAP)
1041 /*----------------------------------------------------------------------
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)
1052 #define NPT_XML_CHAR_IS_WHITESPACE(c) \
1053 ((c) == ' ' || (c) == '\t' || (c) == 0x0D || (c) == 0x0A)
1055 #define NPT_XML_CHAR_IS_ANY_CHAR(c) \
1056 (NPT_XML_CHAR_IS_WHITESPACE((c)) || ((c) >= 0x20))
1058 #define NPT_XML_CHAR_IS_DIGIT(c) \
1059 ((c) >= '0' && (c) <= '9')
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))
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) == ':')
1067 #define NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c) \
1068 (NPT_XML_CHAR_IS_NAME_CHAR((c)) || ((c) == '#'))
1070 #define NPT_XML_CHAR_IS_CONTENT_CHAR(c) \
1071 (NPT_XML_CHAR_IS_ANY_CHAR((c)) && ((c) != '&') && ((c) != '<'))
1073 #define NPT_XML_CHAR_IS_VALUE_CHAR(c) \
1074 (NPT_XML_CHAR_IS_ANY_CHAR((c)) && ((c) != '&') && ((c) != '<'))
1076 #endif // defined(NPT_XML_USE_CHAR_MAP)
1078 /*----------------------------------------------------------------------
1079 | NPT_XmlStringIsWhitespace
1080 +---------------------------------------------------------------------*/
1082 NPT_XmlStringIsWhitespace(const char* s
, NPT_Size size
)
1084 for (NPT_Size x
=0; x
<size
; x
++) {
1085 if (!NPT_XML_CHAR_IS_WHITESPACE((int)s
[x
])) {
1093 /*----------------------------------------------------------------------
1094 | NPT_XmlProcessor class
1095 +---------------------------------------------------------------------*/
1096 class NPT_XmlProcessor
{
1098 // constructor and destructor
1099 NPT_XmlProcessor(NPT_XmlParser
* parser
);
1102 NPT_Result
ProcessBuffer(const char* buffer
, NPT_Size size
);
1112 CONTEXT_VALUE_SINGLE_QUOTE
,
1113 CONTEXT_VALUE_DOUBLE_QUOTE
1120 STATE_IN_WHITESPACE
,
1122 STATE_IN_NAME_SPECIAL
,
1123 STATE_IN_VALUE_START
,
1126 STATE_IN_EMPTY_TAG_END
,
1128 STATE_IN_PROCESSING_INSTRUCTION_START
,
1129 STATE_IN_PROCESSING_INSTRUCTION
,
1130 STATE_IN_PROCESSING_INSTRUCTION_END
,
1132 STATE_IN_COMMENT_END_1
,
1133 STATE_IN_COMMENT_END_2
,
1135 STATE_IN_DTD_MARKUP_DECL
,
1136 STATE_IN_DTD_MARKUP_DECL_END
,
1138 STATE_IN_CDATA_END_1
,
1139 STATE_IN_CDATA_END_2
,
1145 NPT_XmlParser
* m_Parser
;
1149 NPT_XmlAccumulator m_Name
;
1150 NPT_XmlAccumulator m_Value
;
1151 NPT_XmlAccumulator m_Text
;
1152 NPT_XmlAccumulator m_Entity
;
1155 #ifdef NPT_XML_PARSER_DEBUG
1156 const char* StateName(State state
) {
1158 case STATE_IN_INIT
: return "IN_INIT";
1159 case STATE_IN_BOM_EF
: return "IN_BOM_EF";
1160 case STATE_IN_BOM_BB
: return "IN_BOM_BB";
1161 case STATE_IN_WHITESPACE
: return "IN_WHITESPACE";
1162 case STATE_IN_NAME
: return "IN_NAME";
1163 case STATE_IN_NAME_SPECIAL
: return "IN_NAME_SPECIAL";
1164 case STATE_IN_VALUE_START
: return "IN_VALUE_START";
1165 case STATE_IN_VALUE
: return "IN_VALUE";
1166 case STATE_IN_TAG_START
: return "IN_TAG_START";
1167 case STATE_IN_EMPTY_TAG_END
: return "IN_EMPTY_TAG_END";
1168 case STATE_IN_CONTENT
: return "IN_CONTENT";
1169 case STATE_IN_PROCESSING_INSTRUCTION_START
: return "IN_PROCESSING_INSTRUCTION_START";
1170 case STATE_IN_PROCESSING_INSTRUCTION
: return "IN_PROCESSING_INSTRUCTION";
1171 case STATE_IN_PROCESSING_INSTRUCTION_END
: return "IN_PROCESSING_INSTRUCTION_END";
1172 case STATE_IN_COMMENT
: return "IN_COMMENT";
1173 case STATE_IN_COMMENT_END_1
: return "IN_COMMENT_END_1";
1174 case STATE_IN_COMMENT_END_2
: return "IN_COMMENT_END_2";
1175 case STATE_IN_DTD
: return "IN_DTD";
1176 case STATE_IN_DTD_MARKUP_DECL
: return "IN_DTD_MARKUP_DECL";
1177 case STATE_IN_DTD_MARKUP_DECL_END
: return "IN_DTD_MARKUP_DECL_END";
1178 case STATE_IN_CDATA
: return "IN_CDATA";
1179 case STATE_IN_CDATA_END_1
: return "IN_CDATA_END_1";
1180 case STATE_IN_CDATA_END_2
: return "IN_CDATA_END_2";
1181 case STATE_IN_SPECIAL
: return "IN_SPECIAL";
1182 case STATE_IN_ENTITY_REF
: return "IN_ENTITY_REF";
1187 const char* ContextName(Context context
) {
1189 case CONTEXT_NONE
: return "NONE";
1190 case CONTEXT_OPEN_TAG
: return "OPEN_TAG";
1191 case CONTEXT_CLOSE_TAG
: return "CLOSE_TAG";
1192 case CONTEXT_ATTRIBUTE
: return "ATTRIBUTE";
1193 case CONTEXT_VALUE_SINGLE_QUOTE
: return "VALUE_SINGLE_QUOTE";
1194 case CONTEXT_VALUE_DOUBLE_QUOTE
: return "VALUE_DOUBLE_QUOTE";
1198 #endif /* NPT_XML_PARSER_DEBUG */
1200 inline void SetState(State state
) {
1201 NPT_XML_Debug_3("\nstate transition: %s to %s [ctx=%s]\n",
1204 ContextName(m_Context
));
1208 inline void SetState(State state
, Context context
) {
1209 NPT_XML_Debug_4("\nstate transition: %s [ctx=%s] to %s [ctx=%s]\n",
1211 ContextName(m_Context
),
1213 ContextName(context
));
1215 m_Context
= context
;
1218 NPT_Result
ResolveEntity(NPT_XmlAccumulator
& source
,
1219 NPT_XmlAccumulator
& destination
);
1220 NPT_Result
FlushPendingText();
1223 /*----------------------------------------------------------------------
1224 | NPT_XmlProcessor::NPT_XmlProcessor
1225 +---------------------------------------------------------------------*/
1226 NPT_XmlProcessor::NPT_XmlProcessor(NPT_XmlParser
* parser
) :
1228 m_State(STATE_IN_INIT
),
1229 m_Context(CONTEXT_NONE
),
1230 m_SkipNewline(false)
1234 /*----------------------------------------------------------------------
1235 | NPT_XmlProcessor::Reset
1236 +---------------------------------------------------------------------*/
1238 NPT_XmlProcessor::Reset()
1240 m_State
= STATE_IN_INIT
;
1241 m_Context
= CONTEXT_NONE
;
1242 m_SkipNewline
= false;
1245 /*----------------------------------------------------------------------
1246 | NPT_XmlProcessor::ResolveEntity
1247 +---------------------------------------------------------------------*/
1249 NPT_XmlProcessor::ResolveEntity(NPT_XmlAccumulator
& source
,
1250 NPT_XmlAccumulator
& destination
)
1252 const char* entity
= (const char*)source
.GetString();
1254 if (NPT_StringsEqual(entity
, "lt")) {
1255 destination
.Append('<');
1256 } else if (NPT_StringsEqual(entity
, "gt")) {
1257 destination
.Append('>');
1258 } else if (NPT_StringsEqual(entity
, "amp")) {
1259 destination
.Append('&');
1260 } else if (NPT_StringsEqual(entity
, "quot")) {
1261 destination
.Append('"');
1262 } else if (NPT_StringsEqual(entity
, "apos")) {
1263 destination
.Append('\'');
1264 } else if (entity
[0] == '#') {
1267 if (entity
[1] == 'x') {
1272 while (char c
= entity
[i
++]) {
1274 if (c
>='0' && c
<='9') {
1276 } else if (base
== 16) {
1277 if (c
>= 'a' && c
<= 'f') {
1279 } else if (c
>= 'A' && c
<= 'F') {
1284 // invalid char, leave the entity unparsed
1285 destination
.Append(source
.GetString());
1286 return NPT_ERROR_INVALID_SYNTAX
;
1288 parsed
= base
*parsed
+digit
;
1290 destination
.AppendUTF8(parsed
);
1292 // unknown entity, leave as-is
1293 destination
.Append(source
.GetString());
1299 /*----------------------------------------------------------------------
1300 | NPT_XmlProcessor::FlushPendingText
1301 +---------------------------------------------------------------------*/
1303 NPT_XmlProcessor::FlushPendingText()
1305 if (m_Text
.GetSize() > 0) {
1306 NPT_CHECK(m_Parser
->OnCharacterData(m_Text
.GetString(),
1313 /*----------------------------------------------------------------------
1314 | NPT_XmlProcessor::ProcessBuffer
1315 +---------------------------------------------------------------------*/
1317 NPT_XmlProcessor::ProcessBuffer(const char* buffer
, NPT_Size size
)
1321 while (size
-- && (c
= *buffer
++)) {
1322 NPT_XML_Debug_1("[%c]", (c
== '\n' || c
== '\r') ? '#' : c
);
1324 // normalize line ends
1325 if (m_SkipNewline
) {
1326 m_SkipNewline
= false;
1327 if (c
== '\n') continue;
1330 m_SkipNewline
= true;
1334 // process the character
1337 if (NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1338 SetState(STATE_IN_WHITESPACE
);
1340 } else if (c
== '<') {
1341 SetState(STATE_IN_TAG_START
);
1343 } else if (c
== 0xEF) {
1344 SetState(STATE_IN_BOM_EF
);
1347 return NPT_ERROR_INVALID_SYNTAX
;
1349 case STATE_IN_BOM_EF
:
1351 SetState(STATE_IN_BOM_BB
);
1354 return NPT_ERROR_INVALID_SYNTAX
;
1356 case STATE_IN_BOM_BB
:
1358 SetState(STATE_IN_WHITESPACE
);
1361 return NPT_ERROR_INVALID_SYNTAX
;
1363 case STATE_IN_WHITESPACE
:
1364 if (NPT_XML_CHAR_IS_WHITESPACE(c
)) break;
1365 switch (m_Context
) {
1368 SetState(STATE_IN_TAG_START
);
1370 return NPT_ERROR_INVALID_SYNTAX
;
1374 case CONTEXT_ATTRIBUTE
:
1376 SetState(STATE_IN_EMPTY_TAG_END
, CONTEXT_NONE
);
1377 } else if (c
== '>') {
1378 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1379 } else if (NPT_XML_CHAR_IS_NAME_CHAR(c
)) {
1382 SetState(STATE_IN_NAME
);
1384 return NPT_ERROR_INVALID_SYNTAX
;
1388 case CONTEXT_CLOSE_TAG
:
1390 NPT_CHECK(FlushPendingText());
1391 NPT_CHECK(m_Parser
->OnEndElement(m_Name
.GetString()));
1392 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1394 return NPT_ERROR_INVALID_SYNTAX
;
1399 return NPT_ERROR_INVALID_SYNTAX
;
1404 if (NPT_XML_CHAR_IS_NAME_CHAR(c
)) {
1408 switch (m_Context
) {
1409 case CONTEXT_ATTRIBUTE
:
1412 SetState(STATE_IN_VALUE_START
);
1413 } else if (!NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1414 return NPT_ERROR_INVALID_SYNTAX
;
1418 case CONTEXT_OPEN_TAG
:
1419 if (c
== '>' || c
== '/' || NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1420 NPT_CHECK(FlushPendingText());
1421 NPT_CHECK(m_Parser
->OnStartElement(m_Name
.GetString()));
1424 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1425 } else if (c
== '/') {
1426 SetState(STATE_IN_EMPTY_TAG_END
);
1428 SetState(STATE_IN_WHITESPACE
, CONTEXT_ATTRIBUTE
);
1431 return NPT_ERROR_INVALID_SYNTAX
;
1435 case CONTEXT_CLOSE_TAG
:
1437 NPT_CHECK(FlushPendingText());
1438 NPT_CHECK(m_Parser
->OnEndElement(m_Name
.GetString()));
1439 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1440 } else if (NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1441 SetState(STATE_IN_WHITESPACE
);
1443 return NPT_ERROR_INVALID_SYNTAX
;
1448 return NPT_ERROR_INVALID_SYNTAX
;
1452 case STATE_IN_NAME_SPECIAL
:
1453 if (NPT_XML_CHAR_IS_NAME_CHAR(c
) || (c
== '[')) {
1456 const unsigned char* nb
= m_Name
.GetBuffer();
1457 if (m_Name
.GetSize() == 2) {
1461 SetState(STATE_IN_COMMENT
, CONTEXT_NONE
);
1464 } else if (m_Name
.GetSize() == 7) {
1473 SetState(STATE_IN_CDATA
, CONTEXT_NONE
);
1479 if (NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1480 const char* special
= m_Name
.GetString();
1481 if (special
&& NPT_StringsEqual(special
, "DOCTYPE")) {
1482 SetState(STATE_IN_DTD
, CONTEXT_NONE
);
1484 SetState(STATE_IN_SPECIAL
, CONTEXT_NONE
);
1488 return NPT_ERROR_INVALID_SYNTAX
;
1492 case STATE_IN_VALUE_START
:
1493 if (NPT_XML_CHAR_IS_WHITESPACE(c
)) break;
1496 SetState(STATE_IN_VALUE
, CONTEXT_VALUE_DOUBLE_QUOTE
);
1497 } else if (c
== '\'') {
1499 SetState(STATE_IN_VALUE
, CONTEXT_VALUE_SINGLE_QUOTE
);
1501 return NPT_ERROR_INVALID_SYNTAX
;
1505 case STATE_IN_VALUE
:
1506 if ((c
== '"' && m_Context
== CONTEXT_VALUE_DOUBLE_QUOTE
) ||
1507 (c
== '\'' && m_Context
== CONTEXT_VALUE_SINGLE_QUOTE
)) {
1508 NPT_CHECK(m_Parser
->OnElementAttribute(m_Name
.GetString(),
1509 m_Value
.GetString()));
1510 SetState(STATE_IN_WHITESPACE
, CONTEXT_ATTRIBUTE
);
1511 } else if (c
== '&') {
1513 SetState(STATE_IN_ENTITY_REF
);
1514 } else if (NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1515 m_Value
.Append(' ');
1516 } else if (NPT_XML_CHAR_IS_VALUE_CHAR(c
)) {
1519 return NPT_ERROR_INVALID_SYNTAX
;
1523 case STATE_IN_TAG_START
:
1526 SetState(STATE_IN_NAME_SPECIAL
, CONTEXT_NONE
);
1527 } else if (c
== '?') {
1528 SetState(STATE_IN_PROCESSING_INSTRUCTION
, CONTEXT_NONE
);
1529 } else if (c
== '/') {
1530 SetState(STATE_IN_NAME
, CONTEXT_CLOSE_TAG
);
1531 } else if (NPT_XML_CHAR_IS_NAME_CHAR(c
)) {
1533 SetState(STATE_IN_NAME
, CONTEXT_OPEN_TAG
);
1535 return NPT_ERROR_INVALID_SYNTAX
;
1539 case STATE_IN_EMPTY_TAG_END
:
1541 NPT_CHECK(FlushPendingText());
1542 NPT_CHECK(m_Parser
->OnEndElement(NULL
));
1543 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1545 return NPT_ERROR_INVALID_SYNTAX
;
1549 case STATE_IN_ENTITY_REF
:
1550 switch (m_Context
) {
1551 case CONTEXT_VALUE_SINGLE_QUOTE
:
1552 case CONTEXT_VALUE_DOUBLE_QUOTE
:
1554 NPT_CHECK(ResolveEntity(m_Entity
, m_Value
));
1555 SetState(STATE_IN_VALUE
);
1556 } else if (NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c
)) {
1559 return NPT_ERROR_INVALID_SYNTAX
;
1565 NPT_CHECK(ResolveEntity(m_Entity
, m_Text
));
1566 SetState(STATE_IN_CONTENT
);
1567 } else if (NPT_XML_CHAR_IS_ENTITY_REF_CHAR(c
)) {
1570 return NPT_ERROR_INVALID_SYNTAX
;
1575 return NPT_ERROR_INVALID_SYNTAX
;
1579 case STATE_IN_COMMENT
:
1581 SetState(STATE_IN_COMMENT_END_1
);
1582 } else if (!NPT_XML_CHAR_IS_ANY_CHAR(c
)) {
1583 return NPT_ERROR_INVALID_SYNTAX
;
1587 case STATE_IN_COMMENT_END_1
:
1589 SetState(STATE_IN_COMMENT_END_2
);
1590 } else if (NPT_XML_CHAR_IS_ANY_CHAR(c
)) {
1591 SetState(STATE_IN_COMMENT
);
1593 return NPT_ERROR_INVALID_SYNTAX
;
1597 case STATE_IN_COMMENT_END_2
:
1599 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1601 return NPT_ERROR_INVALID_SYNTAX
;
1605 case STATE_IN_CONTENT
:
1607 SetState(STATE_IN_TAG_START
, CONTEXT_NONE
);
1608 } else if (c
== '&') {
1610 SetState(STATE_IN_ENTITY_REF
);
1616 case STATE_IN_PROCESSING_INSTRUCTION_START
:
1619 case STATE_IN_PROCESSING_INSTRUCTION_END
:
1621 SetState(STATE_IN_WHITESPACE
, CONTEXT_NONE
);
1623 return NPT_ERROR_INVALID_SYNTAX
;
1627 case STATE_IN_PROCESSING_INSTRUCTION
:
1629 SetState(STATE_IN_PROCESSING_INSTRUCTION_END
);
1634 if (NPT_XML_CHAR_IS_WHITESPACE(c
)) break;
1636 SetState(STATE_IN_DTD_MARKUP_DECL
);
1637 } else if (c
== '>') {
1638 SetState(STATE_IN_WHITESPACE
, CONTEXT_NONE
);
1642 case STATE_IN_DTD_MARKUP_DECL
:
1644 SetState(STATE_IN_DTD_MARKUP_DECL_END
);
1648 case STATE_IN_DTD_MARKUP_DECL_END
:
1650 SetState(STATE_IN_WHITESPACE
, CONTEXT_NONE
);
1651 } else if (!NPT_XML_CHAR_IS_WHITESPACE(c
)) {
1652 return NPT_ERROR_INVALID_SYNTAX
;
1656 case STATE_IN_CDATA
:
1658 SetState(STATE_IN_CDATA_END_1
);
1664 case STATE_IN_CDATA_END_1
:
1666 SetState(STATE_IN_CDATA_END_2
);
1670 SetState(STATE_IN_CDATA
);
1674 case STATE_IN_CDATA_END_2
:
1676 SetState(STATE_IN_CONTENT
, CONTEXT_NONE
);
1678 m_Text
.Append("]]");
1680 SetState(STATE_IN_CDATA
);
1684 case STATE_IN_SPECIAL
:
1686 SetState(STATE_IN_WHITESPACE
, CONTEXT_NONE
);
1695 /*----------------------------------------------------------------------
1696 | NPT_XmlParser::NPT_XmlParser
1697 +---------------------------------------------------------------------*/
1698 NPT_XmlParser::NPT_XmlParser(bool keep_whitespace
/* = false */) :
1700 m_CurrentElement(NULL
),
1701 m_KeepWhitespace(keep_whitespace
)
1703 m_Processor
= new NPT_XmlProcessor(this);
1706 /*----------------------------------------------------------------------
1707 | NPT_XmlParser::~NPT_XmlParser
1708 +---------------------------------------------------------------------*/
1709 NPT_XmlParser::~NPT_XmlParser()
1712 delete m_CurrentElement
;
1716 /*----------------------------------------------------------------------
1717 | NPT_XmlParser::Reset
1718 +---------------------------------------------------------------------*/
1720 NPT_XmlParser::Reset()
1722 // delete anything that has been created
1723 NPT_XmlNode
* walker
= m_CurrentElement
;
1724 while (walker
&& walker
->GetParent()) {
1725 walker
= walker
->GetParent();
1728 m_CurrentElement
= NULL
;
1730 m_Processor
->Reset();
1735 /*----------------------------------------------------------------------
1736 | NPT_XmlParser::Parse
1737 +---------------------------------------------------------------------*/
1739 NPT_XmlParser::Parse(NPT_InputStream
& stream
,
1742 bool incremental
/* = false */)
1746 // start with a known state
1753 // use a buffer on the stack
1756 // read a buffer and parse it until the end of the stream
1757 NPT_Size max_bytes_to_read
= size
;
1760 NPT_Size bytes_read
;
1761 NPT_Size bytes_to_read
= sizeof(buffer
);
1762 if (max_bytes_to_read
!= 0 &&
1763 size
+bytes_to_read
> max_bytes_to_read
) {
1764 bytes_to_read
= max_bytes_to_read
-size
;
1766 result
= stream
.Read(buffer
, bytes_to_read
, &bytes_read
);
1767 if (NPT_SUCCEEDED(result
)) {
1768 // update the counter
1772 result
= m_Processor
->ProcessBuffer(buffer
, bytes_read
);
1773 if (NPT_FAILED(result
)) break;
1777 } while(NPT_SUCCEEDED(result
) &&
1778 (max_bytes_to_read
== 0 || size
< max_bytes_to_read
));
1780 // return a tree if we have one
1785 if (NPT_FAILED(result
) && result
!= NPT_ERROR_EOS
) {
1791 return m_Root
?NPT_SUCCESS
:NPT_ERROR_XML_NO_ROOT
;
1796 /*----------------------------------------------------------------------
1797 | NPT_XmlParser::Parse
1798 +---------------------------------------------------------------------*/
1800 NPT_XmlParser::Parse(NPT_InputStream
& stream
,
1802 bool incremental
/* = false */)
1804 NPT_Size max_read
= 0; // no limit
1805 return Parse(stream
, max_read
, node
, incremental
);
1808 /*----------------------------------------------------------------------
1809 | NPT_XmlParser::Parse
1810 +---------------------------------------------------------------------*/
1812 NPT_XmlParser::Parse(const char* xml
,
1814 bool incremental
/* = false */)
1816 NPT_Size size
= NPT_StringLength(xml
);
1818 return Parse(xml
, size
, node
, incremental
);
1821 /*----------------------------------------------------------------------
1822 | NPT_XmlParser::Parse
1823 +---------------------------------------------------------------------*/
1825 NPT_XmlParser::Parse(const char* xml
,
1828 bool incremental
/* = false */)
1830 // start with a known state
1838 NPT_Result result
= m_Processor
->ProcessBuffer(xml
, size
);
1840 // return a tree if we have one
1845 if (NPT_FAILED(result
)) {
1851 return m_Root
?NPT_SUCCESS
:NPT_ERROR_XML_NO_ROOT
;
1856 /*----------------------------------------------------------------------
1857 | NPT_XmlParser::OnStartElement
1858 +---------------------------------------------------------------------*/
1860 NPT_XmlParser::OnStartElement(const char* name
)
1862 NPT_XML_Debug_1("\nNPT_XmlParser::OnStartElement: %s\n", name
);
1864 // we cannot start an element if we already have a root
1866 return NPT_ERROR_XML_MULTIPLE_ROOTS
;
1870 NPT_XmlElementNode
* node
= new NPT_XmlElementNode(name
);
1873 if (m_CurrentElement
) {
1875 m_CurrentElement
->AddChild(node
);
1877 m_CurrentElement
= node
;
1882 /*----------------------------------------------------------------------
1883 | NPT_XmlParser::OnElementAttribute
1884 +---------------------------------------------------------------------*/
1886 NPT_XmlParser::OnElementAttribute(const char* name
, const char* value
)
1888 NPT_XML_Debug_2("\nNPT_XmlParser::OnElementAttribute: name=%s, value='%s'\n",
1891 if (m_CurrentElement
== NULL
) {
1892 return NPT_ERROR_INVALID_SYNTAX
;
1895 // check if this is a namespace attribute
1896 if (name
[0] == 'x' &&
1901 (name
[5] == '\0' || name
[5] == ':')) {
1902 // namespace definition
1903 m_CurrentElement
->SetNamespaceUri((name
[5] == ':')?name
+6:"", value
);
1905 m_CurrentElement
->AddAttribute(name
, value
);
1911 /*----------------------------------------------------------------------
1912 | NPT_XmlParser::OnEndElement
1913 +---------------------------------------------------------------------*/
1915 NPT_XmlParser::OnEndElement(const char* name
)
1917 NPT_XML_Debug_1("\nNPT_XmlParser::OnEndElement: %s\n", name
? name
: "NULL");
1919 if (m_CurrentElement
== NULL
) return NPT_ERROR_XML_TAG_MISMATCH
;
1921 // check that the name matches (if there is a name)
1923 const char* prefix
= name
;
1924 unsigned int prefix_length
= 0;
1925 const char* tag
= name
;
1926 const char* cursor
= name
;
1927 while (char c
= *cursor
++) {
1929 prefix_length
= (unsigned int)(cursor
-name
)-1;
1933 // check that the name and prefix length match
1934 if (m_CurrentElement
->GetTag() != tag
||
1935 m_CurrentElement
->GetPrefix().GetLength() != prefix_length
) {
1936 return NPT_ERROR_XML_TAG_MISMATCH
;
1940 const char* current_prefix
= m_CurrentElement
->GetPrefix().GetChars();
1941 for (unsigned int i
=0; i
<prefix_length
; i
++) {
1942 if (current_prefix
[i
] != prefix
[i
]) {
1943 return NPT_ERROR_XML_TAG_MISMATCH
;
1949 NPT_XmlNode
* parent
= m_CurrentElement
->GetParent();
1951 m_CurrentElement
= parent
->AsElementNode();
1954 // this should never happen
1955 delete m_CurrentElement
;
1956 m_CurrentElement
= NULL
;
1957 return NPT_ERROR_XML_MULTIPLE_ROOTS
;
1959 m_Root
= m_CurrentElement
;
1960 m_CurrentElement
= NULL
;
1967 /*----------------------------------------------------------------------
1968 | NPT_XmlParser::OnCharacterData
1969 +---------------------------------------------------------------------*/
1971 NPT_XmlParser::OnCharacterData(const char* data
, NPT_Size size
)
1973 NPT_XML_Debug_1("\nNPT_XmlParser::OnCharacterData: %s\n", data
);
1975 // check that we have a current element
1976 if (m_CurrentElement
== NULL
) {
1977 // we do not allow non-whitespace outside an element content
1978 if (!NPT_XmlStringIsWhitespace(data
, size
)) {
1979 return NPT_ERROR_XML_INVALID_NESTING
;
1982 // ignore whitespace
1986 // ignore whitespace if applicable
1987 if (m_KeepWhitespace
|| !NPT_XmlStringIsWhitespace(data
, size
)) {
1988 // add the text to the current element
1989 m_CurrentElement
->AddText(data
);
1995 /*----------------------------------------------------------------------
1996 | NPT_XmlAttributeWriter
1997 +---------------------------------------------------------------------*/
1998 class NPT_XmlAttributeWriter
2001 NPT_XmlAttributeWriter(NPT_XmlSerializer
& serializer
) : m_Serializer(serializer
) {}
2002 void operator()(NPT_XmlAttribute
*& attribute
) const {
2003 m_Serializer
.Attribute(attribute
->GetPrefix(),
2004 attribute
->GetName(),
2005 attribute
->GetValue());
2010 NPT_XmlSerializer
& m_Serializer
;
2013 /*----------------------------------------------------------------------
2015 +---------------------------------------------------------------------*/
2016 class NPT_XmlNodeWriter
2019 NPT_XmlNodeWriter(NPT_XmlSerializer
& serializer
) :
2020 m_Serializer(serializer
), m_AttributeWriter(serializer
) {
2021 m_Serializer
.StartDocument();
2023 void operator()(NPT_XmlNode
*& node
) const {
2024 if (NPT_XmlElementNode
* element
= node
->AsElementNode()) {
2025 const NPT_String
& prefix
= element
->GetPrefix();
2026 const NPT_String
& tag
= element
->GetTag();
2027 m_Serializer
.StartElement(prefix
, tag
);
2028 element
->GetAttributes().Apply(m_AttributeWriter
);
2030 // emit namespace attributes
2031 if (element
->m_NamespaceMap
) {
2032 NPT_List
<NPT_XmlNamespaceMap::Entry
*>::Iterator item
=
2033 element
->m_NamespaceMap
->m_Entries
.GetFirstItem();
2035 if ((*item
)->m_Prefix
.IsEmpty()) {
2036 // default namespace
2037 m_Serializer
.Attribute(NULL
, "xmlns", (*item
)->m_Uri
);
2039 // namespace with prefix
2040 m_Serializer
.Attribute("xmlns", (*item
)->m_Prefix
, (*item
)->m_Uri
);
2046 element
->GetChildren().Apply(*this);
2047 m_Serializer
.EndElement(prefix
, tag
);
2048 } else if (NPT_XmlTextNode
* text
= node
->AsTextNode()) {
2049 m_Serializer
.Text(text
->GetString());
2055 NPT_XmlSerializer
& m_Serializer
;
2056 NPT_XmlAttributeWriter m_AttributeWriter
;
2059 /*----------------------------------------------------------------------
2060 | NPT_XmlNodeCanonicalWriter
2061 +---------------------------------------------------------------------*/
2062 class NPT_XmlNodeCanonicalWriter
2066 struct MapChainLink
{
2067 MapChainLink(MapChainLink
* parent
) : m_Parent(parent
) {}
2068 MapChainLink
* m_Parent
;
2069 NPT_Map
<NPT_String
, NPT_String
> m_RenderedNamespaces
;
2073 NPT_XmlNodeCanonicalWriter(NPT_XmlSerializer
& serializer
,
2074 MapChainLink
* map_chain
= NULL
) :
2075 m_MapChain(map_chain
),
2076 m_Serializer(serializer
) {
2077 m_Serializer
.StartDocument();
2079 void operator()(NPT_XmlNode
*& node
) const;
2083 struct SortedAttributeList
{
2086 const NPT_String
* m_NamespaceUri
;
2087 const NPT_XmlAttribute
* m_Attribute
;
2091 void Add(const NPT_String
* namespace_uri
,
2092 const NPT_XmlAttribute
* attribute
);
2093 void Emit(NPT_XmlSerializer
& serializer
);
2096 NPT_List
<Entry
> m_Entries
;
2099 struct SortedNamespaceList
{
2102 const NPT_String
* m_NamespacePrefix
;
2103 const NPT_String
* m_NamespaceUri
;
2107 void Add(const NPT_String
* prefix
, const NPT_String
* uri
);
2108 void Emit(NPT_XmlSerializer
& serializer
);
2111 NPT_List
<Entry
> m_Entries
;
2115 const NPT_String
* GetNamespaceRenderedForPrefix(const NPT_String
& prefix
) const;
2118 MapChainLink
* m_MapChain
;
2119 NPT_XmlSerializer
& m_Serializer
;
2122 /*----------------------------------------------------------------------
2123 | NPT_XmlNodeCanonicalWriter::SortedAttributeList::Add
2124 +---------------------------------------------------------------------*/
2126 NPT_XmlNodeCanonicalWriter::SortedAttributeList::Add(
2127 const NPT_String
* namespace_uri
,
2128 const NPT_XmlAttribute
* attribute
)
2130 // transform empty strings into NULL pointers
2131 if (namespace_uri
&& namespace_uri
->IsEmpty()) namespace_uri
= NULL
;
2133 // find the namespace insertion position
2134 NPT_List
<Entry
>::Iterator entry
= m_Entries
.GetFirstItem();
2135 for (; entry
; ++entry
) {
2136 // decide if we insert now or move on
2137 const NPT_String
* other_namespace_uri
= entry
->m_NamespaceUri
;
2138 if (namespace_uri
&&
2139 (other_namespace_uri
== NULL
|| *namespace_uri
> *other_namespace_uri
)) {
2140 // this namespace uri is greater than the other, skip
2142 } else if ((namespace_uri
== NULL
&& other_namespace_uri
== NULL
) ||
2143 (namespace_uri
&& other_namespace_uri
&&
2144 *namespace_uri
== *other_namespace_uri
)) {
2145 // namespace uris match, compare the names
2146 const NPT_XmlAttribute
* other_attribute
= entry
->m_Attribute
;
2147 if (attribute
->GetName() > other_attribute
->GetName()) continue;
2152 Entry new_entry
= {namespace_uri
, attribute
};
2153 m_Entries
.Insert(entry
, new_entry
);
2156 /*----------------------------------------------------------------------
2157 | NPT_XmlNodeCanonicalWriter::SortedAttributeList::Emit
2158 +---------------------------------------------------------------------*/
2160 NPT_XmlNodeCanonicalWriter::SortedAttributeList::Emit(NPT_XmlSerializer
& serializer
)
2162 for (NPT_List
<Entry
>::Iterator i
= m_Entries
.GetFirstItem(); i
; ++i
) {
2163 serializer
.Attribute(i
->m_Attribute
->GetPrefix(),
2164 i
->m_Attribute
->GetName(),
2165 i
->m_Attribute
->GetValue());
2169 /*----------------------------------------------------------------------
2170 | NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Add
2171 +---------------------------------------------------------------------*/
2173 NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Add(const NPT_String
* prefix
,
2174 const NPT_String
* uri
)
2176 // find the namespace insertion position
2177 NPT_List
<Entry
>::Iterator entry
= m_Entries
.GetFirstItem();
2178 if (prefix
&& !prefix
->IsEmpty()) {
2179 for (; entry
; ++entry
) {
2180 // decide if we insert now or move on
2181 if (entry
->m_NamespacePrefix
&& *prefix
<= *entry
->m_NamespacePrefix
) {
2189 Entry new_entry
= {prefix
, uri
};
2190 m_Entries
.Insert(entry
, new_entry
);
2193 /*----------------------------------------------------------------------
2194 | NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Emit
2195 +---------------------------------------------------------------------*/
2197 NPT_XmlNodeCanonicalWriter::SortedNamespaceList::Emit(NPT_XmlSerializer
& serializer
)
2199 for (NPT_List
<Entry
>::Iterator i
= m_Entries
.GetFirstItem(); i
; ++i
) {
2200 const NPT_String
* key
= i
->m_NamespacePrefix
;
2201 const NPT_String
* value
= i
->m_NamespaceUri
;
2203 serializer
.Attribute(NULL
, "xmlns", *value
);
2204 } else if (*key
!= "xml" || *value
!= NPT_XmlNamespaceUri_Xml
) {
2205 serializer
.Attribute("xmlns", *key
, *value
);
2210 /*----------------------------------------------------------------------
2211 | NPT_XmlNodeCanonicalWriter::GetNamespaceRenderedForPrefix
2212 +---------------------------------------------------------------------*/
2214 NPT_XmlNodeCanonicalWriter::GetNamespaceRenderedForPrefix(const NPT_String
& prefix
) const
2216 for (MapChainLink
* link
= m_MapChain
;
2218 link
= link
->m_Parent
) {
2220 if (NPT_SUCCEEDED(link
->m_RenderedNamespaces
.Get(prefix
, uri
))) {
2228 /*----------------------------------------------------------------------
2229 | NPT_XmlNodeCanonicalWriter::operator()
2230 +---------------------------------------------------------------------*/
2232 NPT_XmlNodeCanonicalWriter::operator()(NPT_XmlNode
*& node
) const
2234 MapChainLink
map_link(m_MapChain
);
2236 if (NPT_XmlElementNode
* element
= node
->AsElementNode()) {
2237 const NPT_String
& prefix
= element
->GetPrefix();
2238 const NPT_String
& tag
= element
->GetTag();
2240 // process namespaces
2241 const NPT_String
* namespace_uri
= element
->GetNamespace();
2242 const NPT_String
* rendered
= GetNamespaceRenderedForPrefix(prefix
);
2243 if (namespace_uri
&& namespace_uri
->IsEmpty()) namespace_uri
= NULL
;
2244 if (prefix
.IsEmpty()) {
2245 // default namespace
2246 if (rendered
== NULL
) {
2247 // default namespace not rendered
2248 if (namespace_uri
) {
2249 map_link
.m_RenderedNamespaces
.Put("", *namespace_uri
);
2252 // default namespace already rendered
2253 const char* compare
;
2254 if (namespace_uri
) {
2255 compare
= namespace_uri
->GetChars();
2259 if (*rendered
!= compare
) {
2260 // the rendered default namespace had a different uri
2261 map_link
.m_RenderedNamespaces
.Put("", compare
);
2265 // explicit namespace
2266 // NOTE: namespace_uri should not be an empty string, but we test just
2267 // in case the XML document is not compliant
2268 if (namespace_uri
&& (rendered
== NULL
|| *rendered
!= *namespace_uri
)) {
2269 // namespace prefix not rendered or rendered with a different value
2270 map_link
.m_RenderedNamespaces
.Put(prefix
, *namespace_uri
);
2274 // process attributes
2275 SortedAttributeList prefixed_attributes
;
2276 SortedAttributeList naked_attributes
;
2277 for (NPT_List
<NPT_XmlAttribute
*>::Iterator attribute
= element
->GetAttributes().GetFirstItem();
2280 const NPT_String
& a_prefix
= (*attribute
)->GetPrefix();
2281 if (a_prefix
.IsEmpty()) {
2283 naked_attributes
.Add(NULL
, *attribute
);
2285 // decide if we need to render this namespace declaration
2286 namespace_uri
= element
->GetNamespaceUri(a_prefix
);
2287 if (namespace_uri
) {
2288 rendered
= GetNamespaceRenderedForPrefix(a_prefix
);;
2289 if (rendered
== NULL
|| *rendered
!= *namespace_uri
) {
2290 // namespace not rendered or rendered with a different value
2291 map_link
.m_RenderedNamespaces
.Put(a_prefix
, *namespace_uri
);
2293 prefixed_attributes
.Add(namespace_uri
, *attribute
);
2299 m_Serializer
.StartElement(prefix
, tag
);
2301 // namespace declarations
2302 if (map_link
.m_RenderedNamespaces
.GetEntryCount()) {
2303 SortedNamespaceList namespaces
;
2304 NPT_List
<NPT_Map
<NPT_String
, NPT_String
>::Entry
*>::Iterator entry
=
2305 map_link
.m_RenderedNamespaces
.GetEntries().GetFirstItem();
2307 const NPT_String
& key
= (*entry
)->GetKey();
2308 const NPT_String
& value
= (*entry
)->GetValue();
2309 namespaces
.Add(&key
, &value
);
2312 namespaces
.Emit(m_Serializer
);
2316 naked_attributes
.Emit(m_Serializer
);
2317 prefixed_attributes
.Emit(m_Serializer
);
2320 MapChainLink
* chain
;
2321 if (map_link
.m_RenderedNamespaces
.GetEntryCount()) {
2326 element
->GetChildren().Apply(NPT_XmlNodeCanonicalWriter(m_Serializer
, chain
));
2329 m_Serializer
.EndElement(prefix
, tag
);
2330 } else if (NPT_XmlTextNode
* text
= node
->AsTextNode()) {
2331 m_Serializer
.Text(text
->GetString());
2335 /*----------------------------------------------------------------------
2336 | NPT_XmlSerializer::NPT_XmlSerializer
2337 +---------------------------------------------------------------------*/
2338 NPT_XmlSerializer::NPT_XmlSerializer(NPT_OutputStream
* output
,
2339 NPT_Cardinal indentation
,
2340 bool shrink_empty_elements
,
2341 bool add_xml_decl
) :
2343 m_ElementPending(false),
2345 m_Indentation(indentation
),
2346 m_ElementHasText(false),
2347 m_ShrinkEmptyElements(shrink_empty_elements
),
2348 m_AddXmlDecl(add_xml_decl
)
2352 /*----------------------------------------------------------------------
2353 | NPT_XmlSerializer::~NPT_XmlSerializer
2354 +---------------------------------------------------------------------*/
2355 NPT_XmlSerializer::~NPT_XmlSerializer()
2359 /*----------------------------------------------------------------------
2360 | NPT_XmlSerializer::StartDocument
2361 +---------------------------------------------------------------------*/
2363 NPT_XmlSerializer::StartDocument()
2365 if (!m_AddXmlDecl
) return NPT_SUCCESS
;
2367 return m_Output
->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
2370 /*----------------------------------------------------------------------
2371 | NPT_XmlSerializer::EndDocument
2372 +---------------------------------------------------------------------*/
2374 NPT_XmlSerializer::EndDocument()
2376 return m_ElementPending
?NPT_ERROR_INVALID_STATE
:NPT_SUCCESS
;
2379 /*----------------------------------------------------------------------
2380 | NPT_XmlSerializer::EscapeChar
2381 +---------------------------------------------------------------------*/
2383 NPT_XmlSerializer::EscapeChar(unsigned char c
, char* text
)
2391 *text
++ = c0
>= 10 ? 'A'+(c0
-10) : '0'+c0
;
2393 *text
++ = c1
>= 10 ? 'A'+(c1
-10) : '0'+c1
;
2398 /*----------------------------------------------------------------------
2399 | NPT_XmlSerializer::ProcessPending
2400 +---------------------------------------------------------------------*/
2402 NPT_XmlSerializer::ProcessPending()
2404 if (!m_ElementPending
) return NPT_SUCCESS
;
2405 m_ElementPending
= false;
2406 return m_Output
->Write(">", 1);
2409 /*----------------------------------------------------------------------
2410 | NPT_XmlSerializer::OutputEscapedString
2411 +---------------------------------------------------------------------*/
2413 NPT_XmlSerializer::OutputEscapedString(const char* text
, bool attribute
)
2415 const char* start
= text
;
2417 while (char c
= *text
) {
2418 const char* insert
= NULL
;
2421 EscapeChar(c
, escaped
);
2428 EscapeChar(c
, escaped
);
2433 case '&' : insert
= "&"; break;
2434 case '<' : insert
= "<"; break;
2435 case '>' : if (!attribute
) insert
= ">"; break;
2436 case '"' : if (attribute
) insert
= """; break;
2441 // output pending chars
2442 if (start
!= text
) m_Output
->WriteFully(start
, (NPT_Size
)(text
-start
));
2443 m_Output
->WriteString(insert
);
2449 if (start
!= text
) {
2450 m_Output
->WriteFully(start
, (NPT_Size
)(text
-start
));
2456 /*----------------------------------------------------------------------
2457 | NPT_XmlSerializer::OutputIndentation
2458 +---------------------------------------------------------------------*/
2460 NPT_XmlSerializer::OutputIndentation(bool start
)
2462 if (m_Depth
|| !start
) m_Output
->Write("\r\n", 2);
2464 // ensure we have enough chars in the prefix string
2465 unsigned int prefix_length
= m_Indentation
*m_Depth
;
2466 if (m_IndentationPrefix
.GetLength() < prefix_length
) {
2467 unsigned int needed
= prefix_length
-m_IndentationPrefix
.GetLength();
2468 for (unsigned int i
=0; i
<needed
; i
+=16) {
2469 m_IndentationPrefix
.Append(" ", 16);
2473 // print the indentation prefix
2474 m_Output
->WriteFully(m_IndentationPrefix
.GetChars(), prefix_length
);
2477 /*----------------------------------------------------------------------
2478 | NPT_XmlSerializer::StartElement
2479 +---------------------------------------------------------------------*/
2481 NPT_XmlSerializer::StartElement(const char* prefix
, const char* name
)
2484 if (m_Indentation
) OutputIndentation(true);
2485 m_ElementPending
= true;
2486 m_ElementHasText
= false;
2488 m_Output
->Write("<", 1);
2489 if (prefix
&& prefix
[0]) {
2490 m_Output
->WriteString(prefix
);
2491 m_Output
->Write(":", 1);
2493 return m_Output
->WriteString(name
);
2496 /*----------------------------------------------------------------------
2497 | NPT_XmlSerializer::EndElement
2498 +---------------------------------------------------------------------*/
2500 NPT_XmlSerializer::EndElement(const char* prefix
, const char* name
)
2504 if (m_ElementPending
) {
2505 // this element has no children
2506 m_ElementPending
= false;
2507 if (m_ShrinkEmptyElements
) {
2508 return m_Output
->WriteFully("/>", 2);
2510 m_Output
->Write(">",1);
2514 if (m_Indentation
&& !m_ElementHasText
) OutputIndentation(false);
2515 m_ElementHasText
= false;
2516 m_Output
->WriteFully("</", 2);
2517 if (prefix
&& prefix
[0]) {
2518 m_Output
->WriteString(prefix
);
2519 m_Output
->Write(":", 1);
2521 m_Output
->WriteString(name
);
2522 return m_Output
->Write(">", 1);
2525 /*----------------------------------------------------------------------
2526 | NPT_XmlSerializer::Attribute
2527 +---------------------------------------------------------------------*/
2529 NPT_XmlSerializer::Attribute(const char* prefix
, const char* name
, const char* value
)
2531 m_Output
->Write(" ", 1);
2532 if (prefix
&& prefix
[0]) {
2533 m_Output
->WriteString(prefix
);
2534 m_Output
->Write(":", 1);
2536 m_Output
->WriteString(name
);
2537 m_Output
->WriteFully("=\"", 2);
2538 OutputEscapedString(value
, true);
2539 return m_Output
->Write("\"", 1);
2542 /*----------------------------------------------------------------------
2543 | NPT_XmlSerializer::Text
2544 +---------------------------------------------------------------------*/
2546 NPT_XmlSerializer::Text(const char* text
)
2549 m_ElementHasText
= true;
2550 return OutputEscapedString(text
, false);
2553 /*----------------------------------------------------------------------
2554 | NPT_XmlSerializer::CdataSection
2555 +---------------------------------------------------------------------*/
2557 NPT_XmlSerializer::CdataSection(const char* data
)
2560 m_ElementHasText
= true;
2561 m_Output
->WriteFully("<![CDATA[", 9);
2562 m_Output
->WriteString(data
);
2563 return m_Output
->WriteFully("]]>", 3);
2566 /*----------------------------------------------------------------------
2567 | NPT_XmlSerializer::Comment
2568 +---------------------------------------------------------------------*/
2570 NPT_XmlSerializer::Comment(const char* comment
)
2573 m_Output
->WriteFully("<!--", 4);
2574 m_Output
->WriteString(comment
);
2575 return m_Output
->WriteFully("-->", 3);
2578 /*----------------------------------------------------------------------
2579 | NPT_XmlWriter::Serialize
2580 +---------------------------------------------------------------------*/
2582 NPT_XmlWriter::Serialize(NPT_XmlNode
& node
,
2583 NPT_OutputStream
& output
,
2586 NPT_XmlSerializer
serializer(&output
, m_Indentation
, true, add_xml_decl
);
2587 NPT_XmlNodeWriter
node_writer(serializer
);
2588 NPT_XmlNode
* node_pointer
= &node
;
2589 node_writer(node_pointer
);
2594 /*----------------------------------------------------------------------
2595 | NPT_XmlCanonicalizer::Serialize
2596 +---------------------------------------------------------------------*/
2598 NPT_XmlCanonicalizer::Serialize(NPT_XmlNode
& node
,
2599 NPT_OutputStream
& output
,
2602 // create a serializer with no indentation and no shrinking of empty elements
2603 NPT_XmlSerializer
serializer(&output
, 0, false, add_xml_decl
);
2605 // serialize the node
2606 NPT_XmlNodeCanonicalWriter
node_writer(serializer
);
2607 NPT_XmlNode
* node_pointer
= &node
;
2608 node_writer(node_pointer
);