Bump patch version post tag.
[asdcplib.git] / src / KM_xml.cpp
1 /*
2 Copyright (c) 2005-2015, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    KM_xml.cpp
28     \version $Id$
29     \brief   XML writer
30 */
31
32 #include <KM_xml.h>
33 #include <KM_log.h>
34 #include <KM_mutex.h>
35 #include <stack>
36 #include <map>
37
38 #ifdef HAVE_EXPAT
39 # ifdef HAVE_XERCES_C
40 #  error "Both HAVE_EXPAT and HAVE_XERCES_C defined"
41 # endif
42 #include <expat.h>
43 #endif
44
45 #ifdef HAVE_XERCES_C
46 # ifdef HAVE_EXPAT
47 #  error "Both HAVE_EXPAT and HAVE_XERCES_C defined"
48 # endif
49
50 #include <xercesc/util/PlatformUtils.hpp>
51 #include <xercesc/util/XMLString.hpp>
52 #include <xercesc/util/TransService.hpp>
53 #include <xercesc/sax/AttributeList.hpp>
54 #include <xercesc/sax/HandlerBase.hpp>
55 #include <xercesc/sax/ErrorHandler.hpp>
56 #include <xercesc/sax/SAXParseException.hpp>
57 #include <xercesc/parsers/SAXParser.hpp>
58 #include <xercesc/framework/MemBufInputSource.hpp>
59 #include <xercesc/framework/XMLPScanToken.hpp>
60
61
62 XERCES_CPP_NAMESPACE_USE 
63
64 extern "C"
65 {
66   void kumu_init_xml_dom();
67   bool kumu_UTF_8_to_XercesString(const std::string& in_str, std::basic_string<XMLCh>& out_str);
68   bool kumu_UTF_8_to_XercesString_p(const char* in_str, std::basic_string<XMLCh>& out_str);
69   bool kumu_XercesString_to_UTF_8(const std::basic_string<XMLCh>& in_str, std::string& out_str);
70   bool kumu_XercesString_to_UTF_8_p(const XMLCh* in_str, std::string& out_str);
71 }
72
73 #endif
74
75 using namespace Kumu;
76
77
78 class ns_map : public std::map<std::string, XMLNamespace*>
79 {
80 public:
81   ~ns_map()
82   {
83     while ( ! empty() )
84       {
85         ns_map::iterator ni = begin();
86         delete ni->second;
87         erase(ni);
88       }
89   }
90 };
91
92
93 Kumu::XMLElement::XMLElement(const char* name) : m_Namespace(0), m_NamespaceOwner(0)
94 {
95   m_Name = name;
96 }
97
98 Kumu::XMLElement::~XMLElement()
99 {
100   for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
101     delete *i;
102
103   delete (ns_map*)m_NamespaceOwner;
104 }
105
106 //
107 void
108 Kumu::XMLElement::SetAttr(const char* name, const char* value)
109 {
110   NVPair TmpVal;
111   TmpVal.name = name;
112   TmpVal.value = value;
113
114   m_AttrList.push_back(TmpVal);
115 }
116
117 //
118 Kumu::XMLElement*
119 Kumu::XMLElement::AddChild(Kumu::XMLElement* element)
120 {
121   m_ChildList.push_back(element); // takes posession!
122   return element;
123 }
124
125 //
126 Kumu::XMLElement*
127 Kumu::XMLElement::AddChild(const char* name)
128 {
129   XMLElement* tmpE = new XMLElement(name);
130   m_ChildList.push_back(tmpE);
131   return tmpE;
132 }
133
134 //
135 Kumu::XMLElement*
136 Kumu::XMLElement::AddChildWithContent(const char* name, const std::string& value)
137 {
138   return AddChildWithContent(name, value.c_str());
139 }
140
141 //
142 void
143 Kumu::XMLElement::AppendBody(const std::string& value)
144 {
145   m_Body += value;
146 }
147
148 //
149 void
150 Kumu::XMLElement::SetBody(const std::string& value)
151 {
152   m_Body = value;
153 }
154
155 //
156 Kumu::XMLElement*
157 Kumu::XMLElement::AddChildWithContent(const char* name, const char* value)
158 {
159   assert(name);
160   assert(value);
161   XMLElement* tmpE = new XMLElement(name);
162   tmpE->m_Body = value;
163   m_ChildList.push_back(tmpE);
164   return tmpE;
165 }
166
167 //
168 Kumu::XMLElement*
169 Kumu::XMLElement::AddChildWithPrefixedContent(const char* name, const char* prefix, const char* value)
170 {
171   XMLElement* tmpE = new XMLElement(name);
172   tmpE->m_Body = prefix;
173   tmpE->m_Body += value;
174   m_ChildList.push_back(tmpE);
175   return tmpE;
176 }
177
178 //
179 void
180 Kumu::XMLElement::AddComment(const char* value)
181 {
182   m_Body += "  <!-- ";
183   m_Body += value;
184   m_Body += " -->\n";
185 }
186
187 //
188 void
189 Kumu::XMLElement::Render(std::string& outbuf, const bool& pretty) const
190 {
191   outbuf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
192   RenderElement(outbuf, 0, pretty);
193 }
194
195 //
196 inline void
197 add_spacer(std::string& outbuf, i32_t depth)
198 {
199   while ( depth-- )
200     outbuf+= "  ";
201 }
202
203 //
204 void
205 Kumu::XMLElement::RenderElement(std::string& outbuf, const ui32_t& depth, const bool& pretty) const
206 {
207   if ( pretty )
208     {
209       add_spacer(outbuf, depth);
210     }
211
212   outbuf += "<";
213   outbuf += m_Name;
214
215   // render attributes
216   for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); ++i )
217     {
218       outbuf += " ";
219       outbuf += (*i).name;
220       outbuf += "=\"";
221       outbuf += (*i).value;
222       outbuf += "\"";
223     }
224
225   outbuf += ">";
226
227   // body contents and children
228   if ( ! m_ChildList.empty() )
229     {
230       outbuf += "\n";
231
232       // render body
233       if ( m_Body.length() > 0 )
234         {
235           outbuf += m_Body;
236         }
237
238       for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); ++i )
239         {
240           (*i)->RenderElement(outbuf, depth + 1, pretty);
241         }
242
243       if ( pretty )
244         {
245           add_spacer(outbuf, depth);
246         }
247     }
248   else if ( m_Body.length() > 0 )
249     {
250       outbuf += m_Body;
251     }
252
253   outbuf += "</";
254   outbuf += m_Name;
255   outbuf += ">\n";
256 }
257
258 //
259 bool
260 Kumu::XMLElement::HasName(const char* name) const
261 {
262   if ( name == 0 || *name == 0 )
263     return false;
264
265   return (m_Name == name);
266 }
267
268
269 void
270 Kumu::XMLElement::SetName(const char* name)
271 {
272   if ( name != 0)
273     m_Name = name;
274 }
275
276 //
277 const char*
278 Kumu::XMLElement::GetAttrWithName(const char* name) const
279 {
280   for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
281     {
282       if ( (*i).name == name )
283         return (*i).value.c_str();
284     }
285
286   return 0;
287 }
288
289 //
290 Kumu::XMLElement*
291 Kumu::XMLElement::GetChildWithName(const char* name) const
292 {
293   for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
294     {
295       if ( (*i)->HasName(name) )
296         return *i;
297     }
298
299   return 0;
300 }
301
302 //
303 const Kumu::ElementList&
304 Kumu::XMLElement::GetChildrenWithName(const char* name, ElementList& outList) const
305 {
306   assert(name);
307   for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
308     {
309       if ( (*i)->HasName(name) )
310         outList.push_back(*i);
311
312       if ( ! (*i)->m_ChildList.empty() )
313         (*i)->GetChildrenWithName(name, outList);
314     }
315
316   return outList;
317 }
318
319 //
320 void
321 Kumu::XMLElement::DeleteAttributes()
322 {
323   m_AttrList.clear();
324 }
325
326 //
327 void
328 Kumu::XMLElement::DeleteAttrWithName(const char* name)
329 {
330   assert(name);
331   AttributeList::iterator i = m_AttrList.begin();
332
333   while ( i != m_AttrList.end() )
334     {
335       if ( i->name == std::string(name) )
336         m_AttrList.erase(i++);
337       else
338         ++i;
339     }
340 }
341
342 //
343 void
344 Kumu::XMLElement::DeleteChildren()
345 {
346   while ( ! m_ChildList.empty() )
347     {
348       delete m_ChildList.back();
349       m_ChildList.pop_back();
350     }
351 }
352
353 //
354 void
355 Kumu::XMLElement::DeleteChild(const XMLElement* element)
356 {
357   if ( element != 0 )
358     {
359       for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
360         {
361           if ( *i == element )
362             {
363               delete *i;
364               m_ChildList.erase(i);
365               return;
366             }
367         }
368     }
369 }
370
371 //
372 void
373 Kumu::XMLElement::ForgetChild(const XMLElement* element)
374 {
375   if ( element != 0 )
376     {
377       for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
378         {
379           if ( *i == element )
380             {
381               m_ChildList.erase(i);
382               return;
383             }
384         }
385     }
386 }
387
388 //
389 bool
390 Kumu::XMLElement::ParseString(const ByteString& document)
391 {
392   return ParseString((const char*)document.RoData(), document.Length());
393 }
394
395 //
396 bool
397 Kumu::XMLElement::ParseString(const std::string& document)
398 {
399   return ParseString(document.c_str(), document.size());
400 }
401
402 //
403 bool
404 Kumu::XMLElement::ParseFirstFromString(const ByteString& document)
405 {
406   return ParseFirstFromString((const char*)document.RoData(), document.Length());
407 }
408
409 //
410 bool
411 Kumu::XMLElement::ParseFirstFromString(const std::string& document)
412 {
413   return ParseFirstFromString(document.c_str(), document.size());
414 }
415
416
417 //----------------------------------------------------------------------------------------------------
418
419 #ifdef HAVE_EXPAT
420
421
422 class ExpatParseContext
423 {
424   KM_NO_COPY_CONSTRUCT(ExpatParseContext);
425   ExpatParseContext();
426 public:
427   ns_map*                  Namespaces;
428   std::stack<XMLElement*>  Scope;
429   XMLElement*              Root;
430
431   ExpatParseContext(XMLElement* root) : Root(root) {
432     Namespaces = new ns_map;
433     assert(Root);
434   }
435
436   ~ExpatParseContext() {}
437 };
438
439 // expat wrapper functions
440 // 
441 static void
442 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
443 {
444   assert(p);  assert(name);  assert(attrs);
445   ExpatParseContext* Ctx = (ExpatParseContext*)p;
446   XMLElement* Element;
447
448   const char* ns_root = name;
449   const char* local_name = strchr(name, '|');
450   if ( local_name != 0 )
451     name = local_name + 1;
452
453   if ( Ctx->Scope.empty() )
454     {
455       Ctx->Scope.push(Ctx->Root);
456     }
457   else
458     {
459       Element = Ctx->Scope.top();
460       Ctx->Scope.push(Element->AddChild(name));
461     }
462
463   Element = Ctx->Scope.top();
464   Element->SetName(name);
465
466   // map the namespace
467   std::string key;
468   if ( ns_root != name )
469     key.assign(ns_root, name - ns_root - 1);
470   
471   ns_map::iterator ni = Ctx->Namespaces->find(key);
472   if ( ni != Ctx->Namespaces->end() )
473     Element->SetNamespace(ni->second);
474
475   // set attributes
476   for ( int i = 0; attrs[i] != 0; i += 2 )
477     {
478       if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
479         local_name = attrs[i];
480       else
481         local_name++;
482
483       Element->SetAttr(local_name, attrs[i+1]);
484     }
485 }
486
487 //
488 static void
489 xph_end(void* p, const XML_Char* name)
490 {
491   assert(p);  assert(name);
492   ExpatParseContext* Ctx = (ExpatParseContext*)p;
493   Ctx->Scope.pop();
494 }
495
496 //
497 static void
498 xph_char(void* p, const XML_Char* data, int len)
499 {
500   assert(p);  assert(data);
501   ExpatParseContext* Ctx = (ExpatParseContext*)p;
502
503   if ( len > 0 )
504     {
505       std::string tmp_str;
506       tmp_str.assign(data, len);
507       Ctx->Scope.top()->AppendBody(tmp_str);
508     }
509 }
510
511 //
512 void
513 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
514 {
515   assert(p);  assert(ns_name);
516   ExpatParseContext* Ctx = (ExpatParseContext*)p;
517   
518   if ( ns_prefix == 0 )
519     ns_prefix = "";
520
521   ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
522
523   if  ( ni != Ctx->Namespaces->end() )
524     {
525       if ( ni->second->Name() != std::string(ns_name) )
526         {
527           DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
528           return;
529         }
530     }
531   else
532     {
533       XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
534       Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
535     }
536 }
537
538 //
539 bool
540 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
541 {
542   if ( doc_len == 0 )
543     {
544       return false;
545     }
546
547   XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
548
549   if ( Parser == 0 )
550     {
551       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
552       return false;
553     }
554
555   ExpatParseContext Ctx(this);
556   XML_SetUserData(Parser, (void*)&Ctx);
557   XML_SetElementHandler(Parser, xph_start, xph_end);
558   XML_SetCharacterDataHandler(Parser, xph_char);
559   XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
560
561   if ( ! XML_Parse(Parser, document, doc_len, 1) )
562     {
563       DefaultLogSink().Error("XML Parse error on line %d: %s\n",
564                              XML_GetCurrentLineNumber(Parser),
565                              XML_ErrorString(XML_GetErrorCode(Parser)));
566       XML_ParserFree(Parser);
567       return false;
568     }
569
570   XML_ParserFree(Parser);
571
572   if ( ! Ctx.Namespaces->empty() )
573     {
574       m_NamespaceOwner = (void*)Ctx.Namespaces;
575     }
576
577   return true;
578 }
579
580 // expat wrapper functions
581 // 
582 static void
583 xph_start_one_shot(void* p, const XML_Char* name, const XML_Char** attrs)
584 {
585   xph_start(p, name, attrs);
586   XML_Parser parser = (XML_Parser)p;
587   XML_StopParser(parser, false);
588 }
589
590 //
591 bool
592 Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
593 {
594   if ( doc_len == 0 )
595     {
596       return false;
597     }
598
599   XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
600
601   if ( Parser == 0 )
602     {
603       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
604       return false;
605     }
606
607   ExpatParseContext Ctx(this);
608   XML_SetUserData(Parser, (void*)&Ctx);
609   XML_SetElementHandler(Parser, xph_start_one_shot, xph_end);
610   XML_SetCharacterDataHandler(Parser, xph_char);
611   XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
612
613   if ( ! XML_Parse(Parser, document, doc_len, 1) )
614     {
615       XML_ParserFree(Parser);
616       return false;
617     }
618
619   XML_ParserFree(Parser);
620
621   if ( ! Ctx.Namespaces->empty() )
622     {
623       m_NamespaceOwner = (void*)Ctx.Namespaces;
624     }
625
626   return true;
627 }
628
629
630
631 #endif
632
633 //----------------------------------------------------------------------------------------------------
634
635 #ifdef HAVE_XERCES_C
636
637 static Mutex sg_xerces_init_lock; // protect the xerces initialized
638 static bool  sg_xml_init = false; // signal initialization
639 static Mutex sg_coder_lock;       // protect the transcoder context 
640 static XMLTranscoder*   sg_coder = 0;
641 static const int sg_coder_buf_len = 128 * 1024;
642 static char sg_coder_buf[sg_coder_buf_len + 8];
643 static unsigned char sg_coder_counts[sg_coder_buf_len / sizeof(XMLCh)]; // see XMLTranscoder::transcodeFrom
644
645 static const XMLCh sg_LS[] = { chLatin_L, chLatin_S, chNull };
646 static const XMLCh sg_label_UTF_8[] = { chLatin_U, chLatin_T, chLatin_F,
647                                         chDash, chDigit_8, chNull}; 
648
649 //
650 void
651 kumu_init_xml_dom()
652 {
653   if ( ! sg_xml_init )
654     {
655       AutoMutex AL(sg_xerces_init_lock);
656
657       if ( ! sg_xml_init )
658         {
659           try
660             {
661               XMLPlatformUtils::Initialize();
662               sg_xml_init = true;
663
664               XMLTransService::Codes ret;
665               sg_coder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(sg_label_UTF_8, ret, sg_coder_buf_len);
666
667               if ( ret != XMLTransService::Ok )
668                 {
669                   const char* message = "Undefined Error";
670
671                   switch ( ret )
672                     {
673                     case XMLTransService::UnsupportedEncoding:  message = "Unsupported encoding";  break;
674                     case XMLTransService::InternalFailure:      message = "Internal failure";  break;
675                     case XMLTransService::SupportFilesNotFound: message = "Support files not found";  break;
676                     }
677
678                   DefaultLogSink().Error("Xerces transform initialization error: %s\n", message);
679                 }
680             }
681           catch (const XMLException &e)
682             {
683               DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
684             }
685         }
686     }
687 }
688
689 //
690 bool
691 kumu_XercesString_to_UTF_8(const std::basic_string<XMLCh>& in_str, std::string& out_str) {
692   return kumu_XercesString_to_UTF_8_p(in_str.c_str(), out_str);
693 }
694
695 //
696 bool
697 kumu_XercesString_to_UTF_8_p(const XMLCh* in_str, std::string& out_str)
698 {
699   assert(in_str);
700   assert(sg_xml_init);
701   AutoMutex AL(sg_coder_lock);
702   ui32_t str_len = XMLString::stringLen(in_str);
703   ui32_t read_total = 0;
704
705   try
706     {
707       while ( str_len > 0 )
708         {
709 #if XERCES_VERSION_MAJOR < 3
710           ui32_t read_count = 0;
711 #else
712           XMLSize_t read_count = 0;
713 #endif
714           ui32_t write_count = sg_coder->transcodeTo(in_str + read_total, str_len,
715                                                      (XMLByte*)sg_coder_buf, sg_coder_buf_len,
716                                                      read_count, XMLTranscoder::UnRep_Throw);
717
718           out_str.append(sg_coder_buf, write_count);
719           str_len -= read_count;
720           read_total += read_count;
721           assert(str_len >= 0);
722         }
723     }
724   catch (...)
725     {
726       return false;
727     }
728
729   return true;
730 }
731
732 //
733 bool
734 kumu_UTF_8_to_XercesString(const std::string& in_str, std::basic_string<XMLCh>& out_str) {
735   return kumu_UTF_8_to_XercesString_p(in_str.c_str(), out_str);
736 }
737
738 //
739 bool
740 kumu_UTF_8_to_XercesString_p(const char* in_str, std::basic_string<XMLCh>& out_str)
741 {
742   assert(in_str);
743   assert(sg_xml_init);
744   AutoMutex AL(sg_coder_lock);
745   ui32_t str_len = strlen(in_str);
746   ui32_t read_total = 0;
747
748   try
749     {
750       while ( str_len > 0 )
751         {
752 #if XERCES_VERSION_MAJOR < 3
753           ui32_t read_count = 0;
754 #else
755           XMLSize_t read_count = 0;
756 #endif
757           ui32_t write_count = sg_coder->transcodeFrom((const XMLByte*)(in_str + read_total), str_len,
758                                                        (XMLCh*)sg_coder_buf, sg_coder_buf_len / sizeof(XMLCh),
759                                                        read_count, sg_coder_counts);
760
761           out_str.append((XMLCh*)sg_coder_buf, write_count * sizeof(XMLCh));
762           str_len -= read_count;
763           read_total += read_count;
764           assert(str_len >= 0);
765         }
766     }
767   catch (...)
768     {
769       return false;
770     }
771
772   return true;
773 }
774
775 //
776 class MyTreeHandler : public HandlerBase
777 {
778   ns_map*                  m_Namespaces;
779   std::stack<XMLElement*>  m_Scope;
780   XMLElement*              m_Root;
781   bool                     m_HasEncodeErrors;
782
783 public:
784   MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root), m_HasEncodeErrors(false)
785   {
786     assert(m_Root);
787     m_Namespaces = new ns_map;
788   }
789
790   ~MyTreeHandler() {
791     delete m_Namespaces;
792   }
793
794   bool HasEncodeErrors() const { return m_HasEncodeErrors; }
795
796   ns_map* TakeNamespaceMap()
797   {
798     if ( m_Namespaces == 0 || m_Namespaces->empty() )
799       return 0;
800
801     ns_map* ret = m_Namespaces;
802     m_Namespaces = 0;
803     return ret;
804   }
805
806   //
807   void AddNamespace(const char* ns_prefix, const char* ns_name)
808   {
809     assert(ns_prefix);
810     assert(ns_name);
811
812     if ( ns_prefix[0] == ':' )
813       {
814         ns_prefix++;
815       }
816     else
817       {
818         assert(ns_prefix[0] == 0);
819         ns_prefix = "";
820       }
821
822     ns_map::iterator ni = m_Namespaces->find(ns_prefix);
823
824     if  ( ni != m_Namespaces->end() )
825       {
826         if ( ni->second->Name() != std::string(ns_name) )
827           {
828             DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
829             return;
830           }
831       }
832     else
833       {
834         XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
835         m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
836       }
837
838     assert(!m_Namespaces->empty());
839   }
840
841   //
842   void startElement(const XMLCh* const x_name,
843                     XERCES_CPP_NAMESPACE::AttributeList& attributes)
844   {
845     assert(x_name);
846     std::string tx_name;
847
848     if ( ! kumu_XercesString_to_UTF_8(x_name, tx_name) )
849       m_HasEncodeErrors = true;
850
851     const char* name = tx_name.c_str();
852     XMLElement* Element;
853     const char* ns_root = name;
854     const char* local_name = strchr(name, ':');
855
856     if ( local_name != 0 )
857       name = local_name + 1;
858
859     if ( m_Scope.empty() )
860       {
861         m_Scope.push(m_Root);
862       }
863     else
864       {
865         Element = m_Scope.top();
866         m_Scope.push(Element->AddChild(name));
867       }
868
869     Element = m_Scope.top();
870     Element->SetName(name);
871
872     // set attributes
873     ui32_t a_len = attributes.getLength();
874
875     for ( ui32_t i = 0; i < a_len; i++)
876       {
877         std::string aname, value;
878         if ( ! kumu_XercesString_to_UTF_8(attributes.getName(i), aname) )
879           m_HasEncodeErrors = true;
880
881         if ( ! kumu_XercesString_to_UTF_8(attributes.getValue(i), value) )
882           m_HasEncodeErrors = true;
883
884         const char* x_aname = aname.c_str();
885         const char* x_value = value.c_str();
886
887         if ( strncmp(x_aname, "xmlns", 5) == 0 )
888           AddNamespace(x_aname+5, x_value);
889
890         if ( ( local_name = strchr(x_aname, ':') ) == 0 )
891           local_name = x_aname;
892         else
893           local_name++;
894
895         Element->SetAttr(local_name, x_value);
896       }
897
898     // map the namespace
899     std::string key;
900     if ( ns_root != name )
901       key.assign(ns_root, name - ns_root - 1);
902   
903     ns_map::iterator ni = m_Namespaces->find(key);
904     if ( ni != m_Namespaces->end() )
905       Element->SetNamespace(ni->second);
906   }
907
908   void endElement(const XMLCh *const name) {
909     m_Scope.pop();
910   }
911
912 #if XERCES_VERSION_MAJOR < 3
913   void characters(const XMLCh *const chars, const unsigned int length)
914 #else
915   void characters(const XMLCh* const chars, const XMLSize_t length)
916 #endif
917   {
918     if ( length > 0 )
919       {
920         std::string tmp;
921         if ( ! kumu_XercesString_to_UTF_8(chars, tmp) )
922           m_HasEncodeErrors = true;
923
924         m_Scope.top()->AppendBody(tmp);
925       }
926   }
927 };
928
929 //
930 bool
931 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
932 {
933   if ( doc_len == 0 )
934     {
935       return false;
936     }
937
938   kumu_init_xml_dom();
939
940   int errorCount = 0;
941   SAXParser* parser = new SAXParser();
942
943   parser->setValidationScheme(SAXParser::Val_Always);
944   parser->setDoNamespaces(true);    // optional
945
946   MyTreeHandler* docHandler = new MyTreeHandler(this);
947   parser->setDocumentHandler(docHandler);
948   parser->setErrorHandler(docHandler);
949
950   try
951     {
952       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
953                                   static_cast<const unsigned int>(doc_len),
954                                   "pidc_rules_file");
955
956       parser->parse(xmlSource);
957     }
958   catch (const XMLException& e)
959     {
960       char* message = XMLString::transcode(e.getMessage());
961       DefaultLogSink().Error("Parser error: %s\n", message);
962       XMLString::release(&message);
963       errorCount++;
964     }
965   catch (const SAXParseException& e)
966     {
967       char* message = XMLString::transcode(e.getMessage());
968       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
969       XMLString::release(&message);
970       errorCount++;
971     }
972   catch (...)
973     {
974       DefaultLogSink().Error("Unexpected XML parser error\n");
975       errorCount++;
976     }
977   
978   if ( errorCount == 0 )
979     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
980
981   delete parser;
982   delete docHandler;
983
984   return errorCount > 0 ? false : true;
985 }
986
987 //
988 bool
989 Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
990 {
991   if ( doc_len == 0 )
992     {
993       return false;
994     }
995
996   kumu_init_xml_dom();
997   
998   int errorCount = 0;
999   SAXParser* parser = new SAXParser();
1000
1001   parser->setValidationScheme(SAXParser::Val_Always);
1002   parser->setDoNamespaces(true);    // optional
1003
1004   MyTreeHandler* docHandler = new MyTreeHandler(this);
1005   parser->setDocumentHandler(docHandler);
1006   parser->setErrorHandler(docHandler);
1007   XMLPScanToken token;
1008
1009   try
1010     {
1011       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
1012                                   static_cast<const unsigned int>(doc_len),
1013                                   "pidc_rules_file");
1014
1015       if ( ! parser->parseFirst(xmlSource, token) )
1016         {
1017           ++errorCount;
1018         }
1019
1020       if ( ! parser->parseNext(token) )
1021         {
1022           ++errorCount;
1023         }
1024     }
1025   catch (...)
1026     {
1027       errorCount++;
1028     }
1029   
1030   if ( errorCount == 0 )
1031     {
1032       m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
1033     }
1034
1035   delete parser;
1036   delete docHandler;
1037
1038   return errorCount > 0 ? false : true;
1039 }
1040
1041
1042 #endif
1043
1044 //----------------------------------------------------------------------------------------------------
1045
1046 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
1047
1048 //
1049 bool
1050 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
1051 {
1052   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
1053   return false;
1054 }
1055
1056 bool
1057 Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
1058 {
1059   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
1060   return false;
1061 }
1062
1063 #endif
1064
1065
1066 //----------------------------------------------------------------------------------------------------
1067
1068 //
1069 bool
1070 Kumu::GetXMLDocType(const ByteString& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1071                     AttributeList& doc_attr_list)
1072 {
1073   return GetXMLDocType(buf.RoData(), buf.Length(), ns_prefix, type_name, namespace_name, doc_attr_list);
1074 }
1075
1076 //
1077 bool
1078 Kumu::GetXMLDocType(const std::string& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1079                     AttributeList& doc_attr_list)
1080 {
1081   return GetXMLDocType((const byte_t*)buf.c_str(), buf.size(), ns_prefix, type_name, namespace_name, doc_attr_list);
1082 }
1083
1084 //
1085 bool
1086 Kumu::GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1087                     AttributeList& doc_attr_list)
1088 {
1089   XMLElement tmp_element("tmp");
1090
1091   if ( ! tmp_element.ParseFirstFromString((const char*)buf, buf_len) )
1092     {
1093       return false;
1094     }
1095
1096   const XMLNamespace* ns = tmp_element.Namespace();
1097
1098   if ( ns != 0 )
1099     {
1100       ns_prefix = ns->Prefix();
1101       namespace_name = ns->Name();
1102     }
1103
1104   type_name = tmp_element.GetName();
1105   doc_attr_list = tmp_element.GetAttributes();
1106   return true;
1107 }
1108
1109
1110 //
1111 // end KM_xml.cpp
1112 //