o removed waywars #endif
[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       DefaultLogSink().Error("XML Parse error on line %d: %s\n",
616                              XML_GetCurrentLineNumber(Parser),
617                              XML_ErrorString(XML_GetErrorCode(Parser)));
618       XML_ParserFree(Parser);
619       return false;
620     }
621
622   XML_ParserFree(Parser);
623
624   if ( ! Ctx.Namespaces->empty() )
625     {
626       m_NamespaceOwner = (void*)Ctx.Namespaces;
627     }
628
629   return true;
630 }
631
632
633
634 #endif
635
636 //----------------------------------------------------------------------------------------------------
637
638 #ifdef HAVE_XERCES_C
639
640 static Mutex sg_xerces_init_lock; // protect the xerces initialized
641 static bool  sg_xml_init = false; // signal initialization
642 static Mutex sg_coder_lock;       // protect the transcoder context 
643 static XMLTranscoder*   sg_coder = 0;
644 static const int sg_coder_buf_len = 128 * 1024;
645 static char sg_coder_buf[sg_coder_buf_len + 8];
646 static unsigned char sg_coder_counts[sg_coder_buf_len / sizeof(XMLCh)]; // see XMLTranscoder::transcodeFrom
647
648 static const XMLCh sg_LS[] = { chLatin_L, chLatin_S, chNull };
649 static const XMLCh sg_label_UTF_8[] = { chLatin_U, chLatin_T, chLatin_F,
650                                         chDash, chDigit_8, chNull}; 
651
652 //
653 void
654 kumu_init_xml_dom()
655 {
656   if ( ! sg_xml_init )
657     {
658       AutoMutex AL(sg_xerces_init_lock);
659
660       if ( ! sg_xml_init )
661         {
662           try
663             {
664               XMLPlatformUtils::Initialize();
665               sg_xml_init = true;
666
667               XMLTransService::Codes ret;
668               sg_coder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(sg_label_UTF_8, ret, sg_coder_buf_len);
669
670               if ( ret != XMLTransService::Ok )
671                 {
672                   const char* message = "Undefined Error";
673
674                   switch ( ret )
675                     {
676                     case XMLTransService::UnsupportedEncoding:  message = "Unsupported encoding";  break;
677                     case XMLTransService::InternalFailure:      message = "Internal failure";  break;
678                     case XMLTransService::SupportFilesNotFound: message = "Support files not found";  break;
679                     }
680
681                   DefaultLogSink().Error("Xerces transform initialization error: %s\n", message);
682                 }
683             }
684           catch (const XMLException &e)
685             {
686               DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
687             }
688         }
689     }
690 }
691
692 //
693 bool
694 kumu_XercesString_to_UTF_8(const std::basic_string<XMLCh>& in_str, std::string& out_str) {
695   return kumu_XercesString_to_UTF_8_p(in_str.c_str(), out_str);
696 }
697
698 //
699 bool
700 kumu_XercesString_to_UTF_8_p(const XMLCh* in_str, std::string& out_str)
701 {
702   assert(in_str);
703   assert(sg_xml_init);
704   AutoMutex AL(sg_coder_lock);
705   ui32_t str_len = XMLString::stringLen(in_str);
706   ui32_t read_total = 0;
707
708   try
709     {
710       while ( str_len > 0 )
711         {
712 #if XERCES_VERSION_MAJOR < 3
713           ui32_t read_count = 0;
714 #else
715           XMLSize_t read_count = 0;
716 #endif
717           ui32_t write_count = sg_coder->transcodeTo(in_str + read_total, str_len,
718                                                      (XMLByte*)sg_coder_buf, sg_coder_buf_len,
719                                                      read_count, XMLTranscoder::UnRep_Throw);
720
721           out_str.append(sg_coder_buf, write_count);
722           str_len -= read_count;
723           read_total += read_count;
724           assert(str_len >= 0);
725         }
726     }
727   catch (...)
728     {
729       return false;
730     }
731
732   return true;
733 }
734
735 //
736 bool
737 kumu_UTF_8_to_XercesString(const std::string& in_str, std::basic_string<XMLCh>& out_str) {
738   return kumu_UTF_8_to_XercesString_p(in_str.c_str(), out_str);
739 }
740
741 //
742 bool
743 kumu_UTF_8_to_XercesString_p(const char* in_str, std::basic_string<XMLCh>& out_str)
744 {
745   assert(in_str);
746   assert(sg_xml_init);
747   AutoMutex AL(sg_coder_lock);
748   ui32_t str_len = strlen(in_str);
749   ui32_t read_total = 0;
750
751   try
752     {
753       while ( str_len > 0 )
754         {
755 #if XERCES_VERSION_MAJOR < 3
756           ui32_t read_count = 0;
757 #else
758           XMLSize_t read_count = 0;
759 #endif
760           ui32_t write_count = sg_coder->transcodeFrom((const XMLByte*)(in_str + read_total), str_len,
761                                                        (XMLCh*)sg_coder_buf, sg_coder_buf_len / sizeof(XMLCh),
762                                                        read_count, sg_coder_counts);
763
764           out_str.append((XMLCh*)sg_coder_buf, write_count * sizeof(XMLCh));
765           str_len -= read_count;
766           read_total += read_count;
767           assert(str_len >= 0);
768         }
769     }
770   catch (...)
771     {
772       return false;
773     }
774
775   return true;
776 }
777
778 //
779 class MyTreeHandler : public HandlerBase
780 {
781   ns_map*                  m_Namespaces;
782   std::stack<XMLElement*>  m_Scope;
783   XMLElement*              m_Root;
784   bool                     m_HasEncodeErrors;
785
786 public:
787   MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root), m_HasEncodeErrors(false)
788   {
789     assert(m_Root);
790     m_Namespaces = new ns_map;
791   }
792
793   ~MyTreeHandler() {
794     delete m_Namespaces;
795   }
796
797   bool HasEncodeErrors() const { return m_HasEncodeErrors; }
798
799   ns_map* TakeNamespaceMap()
800   {
801     if ( m_Namespaces == 0 || m_Namespaces->empty() )
802       return 0;
803
804     ns_map* ret = m_Namespaces;
805     m_Namespaces = 0;
806     return ret;
807   }
808
809   //
810   void AddNamespace(const char* ns_prefix, const char* ns_name)
811   {
812     assert(ns_prefix);
813     assert(ns_name);
814
815     if ( ns_prefix[0] == ':' )
816       {
817         ns_prefix++;
818       }
819     else
820       {
821         assert(ns_prefix[0] == 0);
822         ns_prefix = "";
823       }
824
825     ns_map::iterator ni = m_Namespaces->find(ns_prefix);
826
827     if  ( ni != m_Namespaces->end() )
828       {
829         if ( ni->second->Name() != std::string(ns_name) )
830           {
831             DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
832             return;
833           }
834       }
835     else
836       {
837         XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
838         m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
839       }
840
841     assert(!m_Namespaces->empty());
842   }
843
844   //
845   void startElement(const XMLCh* const x_name,
846                     XERCES_CPP_NAMESPACE::AttributeList& attributes)
847   {
848     assert(x_name);
849     std::string tx_name;
850
851     if ( ! kumu_XercesString_to_UTF_8(x_name, tx_name) )
852       m_HasEncodeErrors = true;
853
854     const char* name = tx_name.c_str();
855     XMLElement* Element;
856     const char* ns_root = name;
857     const char* local_name = strchr(name, ':');
858
859     if ( local_name != 0 )
860       name = local_name + 1;
861
862     if ( m_Scope.empty() )
863       {
864         m_Scope.push(m_Root);
865       }
866     else
867       {
868         Element = m_Scope.top();
869         m_Scope.push(Element->AddChild(name));
870       }
871
872     Element = m_Scope.top();
873     Element->SetName(name);
874
875     // set attributes
876     ui32_t a_len = attributes.getLength();
877
878     for ( ui32_t i = 0; i < a_len; i++)
879       {
880         std::string aname, value;
881         if ( ! kumu_XercesString_to_UTF_8(attributes.getName(i), aname) )
882           m_HasEncodeErrors = true;
883
884         if ( ! kumu_XercesString_to_UTF_8(attributes.getValue(i), value) )
885           m_HasEncodeErrors = true;
886
887         const char* x_aname = aname.c_str();
888         const char* x_value = value.c_str();
889
890         if ( strncmp(x_aname, "xmlns", 5) == 0 )
891           AddNamespace(x_aname+5, x_value);
892
893         if ( ( local_name = strchr(x_aname, ':') ) == 0 )
894           local_name = x_aname;
895         else
896           local_name++;
897
898         Element->SetAttr(local_name, x_value);
899       }
900
901     // map the namespace
902     std::string key;
903     if ( ns_root != name )
904       key.assign(ns_root, name - ns_root - 1);
905   
906     ns_map::iterator ni = m_Namespaces->find(key);
907     if ( ni != m_Namespaces->end() )
908       Element->SetNamespace(ni->second);
909   }
910
911   void endElement(const XMLCh *const name) {
912     m_Scope.pop();
913   }
914
915 #if XERCES_VERSION_MAJOR < 3
916   void characters(const XMLCh *const chars, const unsigned int length)
917 #else
918   void characters(const XMLCh* const chars, const XMLSize_t length)
919 #endif
920   {
921     if ( length > 0 )
922       {
923         std::string tmp;
924         if ( ! kumu_XercesString_to_UTF_8(chars, tmp) )
925           m_HasEncodeErrors = true;
926
927         m_Scope.top()->AppendBody(tmp);
928       }
929   }
930 };
931
932 //
933 bool
934 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
935 {
936   if ( doc_len == 0 )
937     {
938       return false;
939     }
940
941   kumu_init_xml_dom();
942
943   int errorCount = 0;
944   SAXParser* parser = new SAXParser();
945
946   parser->setValidationScheme(SAXParser::Val_Always);
947   parser->setDoNamespaces(true);    // optional
948
949   MyTreeHandler* docHandler = new MyTreeHandler(this);
950   parser->setDocumentHandler(docHandler);
951   parser->setErrorHandler(docHandler);
952
953   try
954     {
955       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
956                                   static_cast<const unsigned int>(doc_len),
957                                   "pidc_rules_file");
958
959       parser->parse(xmlSource);
960     }
961   catch (const XMLException& e)
962     {
963       char* message = XMLString::transcode(e.getMessage());
964       DefaultLogSink().Error("Parser error: %s\n", message);
965       XMLString::release(&message);
966       errorCount++;
967     }
968   catch (const SAXParseException& e)
969     {
970       char* message = XMLString::transcode(e.getMessage());
971       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
972       XMLString::release(&message);
973       errorCount++;
974     }
975   catch (...)
976     {
977       DefaultLogSink().Error("Unexpected XML parser error\n");
978       errorCount++;
979     }
980   
981   if ( errorCount == 0 )
982     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
983
984   delete parser;
985   delete docHandler;
986
987   return errorCount > 0 ? false : true;
988 }
989
990 //
991 bool
992 Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
993 {
994   if ( doc_len == 0 )
995     {
996       return false;
997     }
998
999   kumu_init_xml_dom();
1000   
1001   int errorCount = 0;
1002   SAXParser* parser = new SAXParser();
1003
1004   parser->setValidationScheme(SAXParser::Val_Always);
1005   parser->setDoNamespaces(true);    // optional
1006
1007   MyTreeHandler* docHandler = new MyTreeHandler(this);
1008   parser->setDocumentHandler(docHandler);
1009   parser->setErrorHandler(docHandler);
1010   XMLPScanToken token;
1011
1012   try
1013     {
1014       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
1015                                   static_cast<const unsigned int>(doc_len),
1016                                   "pidc_rules_file");
1017
1018       if ( ! parser->parseFirst(xmlSource, token) )
1019         {
1020           ++errorCount;
1021         }
1022
1023       if ( ! parser->parseNext(token) )
1024         {
1025           ++errorCount;
1026         }
1027     }
1028   catch (const XMLException& e)
1029     {
1030       char* message = XMLString::transcode(e.getMessage());
1031       DefaultLogSink().Error("Parser error: %s\n", message);
1032       XMLString::release(&message);
1033       errorCount++;
1034     }
1035   catch (const SAXParseException& e)
1036     {
1037       char* message = XMLString::transcode(e.getMessage());
1038       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
1039       XMLString::release(&message);
1040       errorCount++;
1041     }
1042   catch (...)
1043     {
1044       DefaultLogSink().Error("Unexpected XML parser error\n");
1045       errorCount++;
1046     }
1047   
1048   if ( errorCount == 0 )
1049     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
1050
1051   delete parser;
1052   delete docHandler;
1053
1054   return errorCount > 0 ? false : true;
1055 }
1056
1057
1058 #endif
1059
1060 //----------------------------------------------------------------------------------------------------
1061
1062 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
1063
1064 //
1065 bool
1066 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
1067 {
1068   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
1069   return false;
1070 }
1071
1072 bool
1073 Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
1074 {
1075   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
1076   return false;
1077 }
1078
1079 #endif
1080
1081
1082 //----------------------------------------------------------------------------------------------------
1083
1084 //
1085 bool
1086 Kumu::GetXMLDocType(const ByteString& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1087                     AttributeList& doc_attr_list)
1088 {
1089   return GetXMLDocType(buf.RoData(), buf.Length(), ns_prefix, type_name, namespace_name, doc_attr_list);
1090 }
1091
1092 //
1093 bool
1094 Kumu::GetXMLDocType(const std::string& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1095                     AttributeList& doc_attr_list)
1096 {
1097   return GetXMLDocType((const byte_t*)buf.c_str(), buf.size(), ns_prefix, type_name, namespace_name, doc_attr_list);
1098 }
1099
1100 //
1101 bool
1102 Kumu::GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
1103                     AttributeList& doc_attr_list)
1104 {
1105   XMLElement tmp_element("tmp");
1106
1107   if ( ! tmp_element.ParseFirstFromString((const char*)buf, buf_len) )
1108     {
1109       return false;
1110     }
1111
1112   const XMLNamespace* ns = tmp_element.Namespace();
1113
1114   if ( ns != 0 )
1115     {
1116       ns_prefix = ns->Prefix();
1117       namespace_name = ns->Name();
1118     }
1119
1120   type_name = tmp_element.GetName();
1121   doc_attr_list = tmp_element.GetAttributes();
1122   return true;
1123 }
1124
1125
1126 //
1127 // end KM_xml.cpp
1128 //