high frame rate stereo
[asdcplib.git] / src / KM_xml.cpp
1 /*
2 Copyright (c) 2005-2010, 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 namespace Kumu {
65   void init_xml_dom();
66   typedef std::basic_string<XMLCh> XercesString;
67   bool UTF_8_to_XercesString(const std::string& in_str, XercesString& out_str);
68   bool UTF_8_to_XercesString(const char* in_str, XercesString& out_str);
69   bool XercesString_to_UTF_8(const XercesString& in_str, std::string& out_str);
70   bool XercesString_to_UTF_8(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
190 {
191   outbuf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
192   RenderElement(outbuf, 0);
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, ui32_t depth) const
206 {
207   add_spacer(outbuf, depth);
208
209   outbuf += "<";
210   outbuf += m_Name;
211
212   // render attributes
213   for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
214     {
215       outbuf += " ";
216       outbuf += (*i).name;
217       outbuf += "=\"";
218       outbuf += (*i).value;
219       outbuf += "\"";
220     }
221
222   outbuf += ">";
223
224   // body contents and children
225   if ( ! m_ChildList.empty() )
226     {
227       outbuf += "\n";
228
229       // render body
230       if ( m_Body.length() > 0 )
231         outbuf += m_Body;
232
233       for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
234         (*i)->RenderElement(outbuf, depth + 1);
235
236       add_spacer(outbuf, depth);
237     }
238   else if ( m_Body.length() > 0 )
239     {
240       outbuf += m_Body;
241     }
242
243   outbuf += "</";
244   outbuf += m_Name;
245   outbuf += ">\n";
246 }
247
248 //
249 bool
250 Kumu::XMLElement::HasName(const char* name) const
251 {
252   if ( name == 0 || *name == 0 )
253     return false;
254
255   return (m_Name == name);
256 }
257
258
259 void
260 Kumu::XMLElement::SetName(const char* name)
261 {
262   if ( name != 0)
263     m_Name = name;
264 }
265
266 //
267 const char*
268 Kumu::XMLElement::GetAttrWithName(const char* name) const
269 {
270   for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
271     {
272       if ( (*i).name == name )
273         return (*i).value.c_str();
274     }
275
276   return 0;
277 }
278
279 //
280 Kumu::XMLElement*
281 Kumu::XMLElement::GetChildWithName(const char* name) const
282 {
283   for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
284     {
285       if ( (*i)->HasName(name) )
286         return *i;
287     }
288
289   return 0;
290 }
291
292 //
293 const Kumu::ElementList&
294 Kumu::XMLElement::GetChildrenWithName(const char* name, ElementList& outList) const
295 {
296   assert(name);
297   for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
298     {
299       if ( (*i)->HasName(name) )
300         outList.push_back(*i);
301
302       if ( ! (*i)->m_ChildList.empty() )
303         (*i)->GetChildrenWithName(name, outList);
304     }
305
306   return outList;
307 }
308
309 //
310 void
311 Kumu::XMLElement::DeleteAttributes()
312 {
313   m_AttrList.clear();
314 }
315
316 //
317 void
318 Kumu::XMLElement::DeleteAttrWithName(const char* name)
319 {
320   assert(name);
321   AttributeList::iterator i = m_AttrList.begin();
322
323   while ( i != m_AttrList.end() )
324     {
325       if ( i->name == std::string(name) )
326         m_AttrList.erase(i++);
327       else
328         ++i;
329     }
330 }
331
332 //
333 void
334 Kumu::XMLElement::DeleteChildren()
335 {
336   while ( ! m_ChildList.empty() )
337     {
338       delete m_ChildList.back();
339       m_ChildList.pop_back();
340     }
341 }
342
343 //
344 void
345 Kumu::XMLElement::DeleteChild(const XMLElement* element)
346 {
347   if ( element != 0 )
348     {
349       for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
350         {
351           if ( *i == element )
352             {
353               delete *i;
354               m_ChildList.erase(i);
355               return;
356             }
357         }
358     }
359 }
360
361 //
362 void
363 Kumu::XMLElement::ForgetChild(const XMLElement* element)
364 {
365   if ( element != 0 )
366     {
367       for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
368         {
369           if ( *i == element )
370             {
371               m_ChildList.erase(i);
372               return;
373             }
374         }
375     }
376 }
377
378
379 //----------------------------------------------------------------------------------------------------
380
381 #ifdef HAVE_EXPAT
382
383
384 class ExpatParseContext
385 {
386   KM_NO_COPY_CONSTRUCT(ExpatParseContext);
387   ExpatParseContext();
388 public:
389   ns_map*                  Namespaces;
390   std::stack<XMLElement*>  Scope;
391   XMLElement*              Root;
392
393   ExpatParseContext(XMLElement* root) : Root(root) {
394     Namespaces = new ns_map;
395     assert(Root);
396   }
397
398   ~ExpatParseContext() {}
399 };
400
401 // expat wrapper functions
402 // 
403 static void
404 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
405 {
406   assert(p);  assert(name);  assert(attrs);
407   ExpatParseContext* Ctx = (ExpatParseContext*)p;
408   XMLElement* Element;
409
410   const char* ns_root = name;
411   const char* local_name = strchr(name, '|');
412   if ( local_name != 0 )
413     name = local_name + 1;
414
415   if ( Ctx->Scope.empty() )
416     {
417       Ctx->Scope.push(Ctx->Root);
418     }
419   else
420     {
421       Element = Ctx->Scope.top();
422       Ctx->Scope.push(Element->AddChild(name));
423     }
424
425   Element = Ctx->Scope.top();
426   Element->SetName(name);
427
428   // map the namespace
429   std::string key;
430   if ( ns_root != name )
431     key.assign(ns_root, name - ns_root - 1);
432   
433   ns_map::iterator ni = Ctx->Namespaces->find(key);
434   if ( ni != Ctx->Namespaces->end() )
435     Element->SetNamespace(ni->second);
436
437   // set attributes
438   for ( int i = 0; attrs[i] != 0; i += 2 )
439     {
440       if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
441         local_name = attrs[i];
442       else
443         local_name++;
444
445       Element->SetAttr(local_name, attrs[i+1]);
446     }
447 }
448
449 //
450 static void
451 xph_end(void* p, const XML_Char* name)
452 {
453   assert(p);  assert(name);
454   ExpatParseContext* Ctx = (ExpatParseContext*)p;
455   Ctx->Scope.pop();
456 }
457
458 //
459 static void
460 xph_char(void* p, const XML_Char* data, int len)
461 {
462   assert(p);  assert(data);
463   ExpatParseContext* Ctx = (ExpatParseContext*)p;
464
465   if ( len > 0 )
466     {
467       std::string tmp_str;
468       tmp_str.assign(data, len);
469       Ctx->Scope.top()->AppendBody(tmp_str);
470     }
471 }
472
473 //
474 void
475 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
476 {
477   assert(p);  assert(ns_name);
478   ExpatParseContext* Ctx = (ExpatParseContext*)p;
479   
480   if ( ns_prefix == 0 )
481     ns_prefix = "";
482
483   ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
484
485   if  ( ni != Ctx->Namespaces->end() )
486     {
487       if ( ni->second->Name() != std::string(ns_name) )
488         {
489           DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
490           return;
491         }
492     }
493   else
494     {
495       XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
496       Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
497     }
498 }
499
500 //
501 bool
502 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
503 {
504   XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
505
506   if ( Parser == 0 )
507     {
508       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
509       return false;
510     }
511
512   ExpatParseContext Ctx(this);
513   XML_SetUserData(Parser, (void*)&Ctx);
514   XML_SetElementHandler(Parser, xph_start, xph_end);
515   XML_SetCharacterDataHandler(Parser, xph_char);
516   XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
517
518   if ( ! XML_Parse(Parser, document, doc_len, 1) )
519     {
520       XML_ParserFree(Parser);
521       DefaultLogSink().Error("XML Parse error on line %d: %s\n",
522                              XML_GetCurrentLineNumber(Parser),
523                              XML_ErrorString(XML_GetErrorCode(Parser)));
524       return false;
525     }
526
527   XML_ParserFree(Parser);
528
529   if ( ! Ctx.Namespaces->empty() )
530     m_NamespaceOwner = (void*)Ctx.Namespaces;
531
532   return true;
533 }
534
535 //------------------------------------------------------------------------------------------
536
537 struct xph_test_wrapper
538 {
539   XML_Parser Parser;
540   bool  Status;
541
542   xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
543 };
544
545 // expat wrapper functions, map callbacks to IASAXHandler
546 // 
547 static void
548 xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
549 {
550   assert(p);
551   xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
552
553   Wrapper->Status = true;
554   XML_StopParser(Wrapper->Parser, false);
555 }
556
557
558 //
559 bool
560 Kumu::StringIsXML(const char* document, ui32_t len)
561 {
562   if ( document == 0 )
563     return false;
564
565   if ( len == 0 )
566     len = strlen(document);
567
568   XML_Parser Parser = XML_ParserCreate("UTF-8");
569
570   if ( Parser == 0 )
571     {
572       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
573       return false;
574     }
575
576   xph_test_wrapper Wrapper(Parser);
577   XML_SetUserData(Parser, (void*)&Wrapper);
578   XML_SetStartElementHandler(Parser, xph_test_start);
579
580   XML_Parse(Parser, document, len, 1);
581   XML_ParserFree(Parser);
582   return Wrapper.Status;
583 }
584
585 #endif
586
587 //----------------------------------------------------------------------------------------------------
588
589 #ifdef HAVE_XERCES_C
590
591 static Mutex sg_xerces_init_lock; // protect the xerces initialized
592 static bool  sg_xml_init = false; // signal initialization
593 static Mutex sg_coder_lock;       // protect the transcoder context 
594 static XMLTranscoder*   sg_coder = 0;
595 static const int sg_coder_buf_len = 128 * 1024;
596 static char sg_coder_buf[sg_coder_buf_len + 8];
597 static unsigned char sg_coder_counts[sg_coder_buf_len / sizeof(XMLCh)]; // see XMLTranscoder::transcodeFrom
598
599 static const XMLCh sg_LS[] = { chLatin_L, chLatin_S, chNull };
600 static const XMLCh sg_label_UTF_8[] = { chLatin_U, chLatin_T, chLatin_F,
601                                         chDash, chDigit_8, chNull}; 
602
603 //
604 void
605 Kumu::init_xml_dom()
606 {
607   if ( ! sg_xml_init )
608     {
609       AutoMutex AL(sg_xerces_init_lock);
610
611       if ( ! sg_xml_init )
612         {
613           try
614             {
615               XMLPlatformUtils::Initialize();
616               sg_xml_init = true;
617
618               XMLTransService::Codes ret;
619               sg_coder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(sg_label_UTF_8, ret, sg_coder_buf_len);
620
621               if ( ret != XMLTransService::Ok )
622                 {
623                   const char* message = "Undefined Error";
624
625                   switch ( ret )
626                     {
627                     case XMLTransService::UnsupportedEncoding:  message = "Unsupported encoding";  break;
628                     case XMLTransService::InternalFailure:      message = "Internal failure";  break;
629                     case XMLTransService::SupportFilesNotFound: message = "Support files not found";  break;
630                     }
631
632                   DefaultLogSink().Error("Xerces transform initialization error: %s\n", message);
633                 }
634             }
635           catch (const XMLException &e)
636             {
637               DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
638             }
639         }
640     }
641 }
642
643 //
644 bool
645 Kumu::XercesString_to_UTF_8(const Kumu::XercesString& in_str, std::string& out_str) {
646   return XercesString_to_UTF_8(in_str.c_str(), out_str);
647 }
648
649 //
650 bool
651 Kumu::XercesString_to_UTF_8(const XMLCh* in_str, std::string& out_str)
652 {
653   assert(in_str);
654   assert(sg_xml_init);
655   AutoMutex AL(sg_coder_lock);
656   ui32_t str_len = XMLString::stringLen(in_str);
657   ui32_t read_total = 0;
658
659   try
660     {
661       while ( str_len > 0 )
662         {
663 #if XERCES_VERSION_MAJOR < 3
664           ui32_t read_count = 0;
665 #else
666           XMLSize_t read_count = 0;
667 #endif
668           ui32_t write_count = sg_coder->transcodeTo(in_str + read_total, str_len,
669                                                      (XMLByte*)sg_coder_buf, sg_coder_buf_len,
670                                                      read_count, XMLTranscoder::UnRep_Throw);
671
672           out_str.append(sg_coder_buf, write_count);
673           str_len -= read_count;
674           read_total += read_count;
675           assert(str_len >= 0);
676         }
677     }
678   catch (...)
679     {
680       return false;
681     }
682
683   return true;
684 }
685
686 //
687 bool
688 Kumu::UTF_8_to_XercesString(const std::string& in_str, Kumu::XercesString& out_str) {
689   return UTF_8_to_XercesString(in_str.c_str(), out_str);
690 }
691
692 //
693 bool
694 Kumu::UTF_8_to_XercesString(const char* in_str, Kumu::XercesString& out_str)
695 {
696   assert(in_str);
697   assert(sg_xml_init);
698   AutoMutex AL(sg_coder_lock);
699   ui32_t str_len = strlen(in_str);
700   ui32_t read_total = 0;
701
702   try
703     {
704       while ( str_len > 0 )
705         {
706 #if XERCES_VERSION_MAJOR < 3
707           ui32_t read_count = 0;
708 #else
709           XMLSize_t read_count = 0;
710 #endif
711           ui32_t write_count = sg_coder->transcodeFrom((const XMLByte*)(in_str + read_total), str_len,
712                                                        (XMLCh*)sg_coder_buf, sg_coder_buf_len / sizeof(XMLCh),
713                                                        read_count, sg_coder_counts);
714
715           out_str.append((XMLCh*)sg_coder_buf, write_count * sizeof(XMLCh));
716           str_len -= read_count;
717           read_total += read_count;
718           assert(str_len >= 0);
719         }
720     }
721   catch (...)
722     {
723       return false;
724     }
725
726   return true;
727 }
728
729 //
730 class MyTreeHandler : public HandlerBase
731 {
732   ns_map*                  m_Namespaces;
733   std::stack<XMLElement*>  m_Scope;
734   XMLElement*              m_Root;
735   bool                     m_HasEncodeErrors;
736
737 public:
738   MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root), m_HasEncodeErrors(false)
739   {
740     assert(m_Root);
741     m_Namespaces = new ns_map;
742   }
743
744   ~MyTreeHandler() {
745     delete m_Namespaces;
746   }
747
748   bool HasEncodeErrors() const { return m_HasEncodeErrors; }
749
750   ns_map* TakeNamespaceMap()
751   {
752     if ( m_Namespaces == 0 || m_Namespaces->empty() )
753       return 0;
754
755     ns_map* ret = m_Namespaces;
756     m_Namespaces = 0;
757     return ret;
758   }
759
760   //
761   void AddNamespace(const char* ns_prefix, const char* ns_name)
762   {
763     assert(ns_prefix);
764     assert(ns_name);
765
766     if ( ns_prefix[0] == ':' )
767       {
768         ns_prefix++;
769       }
770     else
771       {
772         assert(ns_prefix[0] == 0);
773         ns_prefix = "";
774       }
775
776     ns_map::iterator ni = m_Namespaces->find(ns_name);
777
778     if  ( ni != m_Namespaces->end() )
779       {
780         if ( ni->second->Name() != std::string(ns_name) )
781           {
782             DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
783             return;
784           }
785       }
786     else
787       {
788         XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
789         m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
790       }
791
792     assert(!m_Namespaces->empty());
793   }
794
795   //
796   void startElement(const XMLCh* const x_name,
797                     XERCES_CPP_NAMESPACE::AttributeList& attributes)
798   {
799     assert(x_name);
800     std::string tx_name;
801
802     if ( ! XercesString_to_UTF_8(x_name, tx_name) )
803       m_HasEncodeErrors = true;
804
805     const char* name = tx_name.c_str();
806     XMLElement* Element;
807     const char* ns_root = name;
808     const char* local_name = strchr(name, ':');
809
810     if ( local_name != 0 )
811       name = local_name + 1;
812
813     if ( m_Scope.empty() )
814       {
815         m_Scope.push(m_Root);
816       }
817     else
818       {
819         Element = m_Scope.top();
820         m_Scope.push(Element->AddChild(name));
821       }
822
823     Element = m_Scope.top();
824     Element->SetName(name);
825
826     // set attributes
827     ui32_t a_len = attributes.getLength();
828
829     for ( ui32_t i = 0; i < a_len; i++)
830       {
831         std::string aname, value;
832         if ( ! XercesString_to_UTF_8(attributes.getName(i), aname) )
833           m_HasEncodeErrors = true;
834
835         if ( ! XercesString_to_UTF_8(attributes.getValue(i), value) )
836           m_HasEncodeErrors = true;
837
838         const char* x_aname = aname.c_str();
839         const char* x_value = value.c_str();
840
841         if ( strncmp(x_aname, "xmlns", 5) == 0 )
842           AddNamespace(x_aname+5, x_value);
843
844         if ( ( local_name = strchr(x_aname, ':') ) == 0 )
845           local_name = x_aname;
846         else
847           local_name++;
848
849         Element->SetAttr(local_name, x_value);
850       }
851
852     // map the namespace
853     std::string key;
854     if ( ns_root != name )
855       key.assign(ns_root, name - ns_root - 1);
856   
857     ns_map::iterator ni = m_Namespaces->find(key);
858     if ( ni != m_Namespaces->end() )
859       Element->SetNamespace(ni->second);
860   }
861
862   void endElement(const XMLCh *const name) {
863     m_Scope.pop();
864   }
865
866   void characters(const XMLCh *const chars, const unsigned int length)
867   {
868     if ( length > 0 )
869       {
870         std::string tmp;
871         if ( ! XercesString_to_UTF_8(chars, tmp) )
872           m_HasEncodeErrors = true;
873
874         m_Scope.top()->AppendBody(tmp);
875       }
876   }
877 };
878
879 //
880 bool
881 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
882 {
883   if ( doc_len == 0 )
884     return false;
885
886   init_xml_dom();
887
888   int errorCount = 0;
889   SAXParser* parser = new SAXParser();
890
891   parser->setValidationScheme(SAXParser::Val_Always);
892   parser->setDoNamespaces(true);    // optional
893
894   MyTreeHandler* docHandler = new MyTreeHandler(this);
895   parser->setDocumentHandler(docHandler);
896   parser->setErrorHandler(docHandler);
897
898   try
899     {
900       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
901                                   static_cast<const unsigned int>(doc_len),
902                                   "pidc_rules_file");
903
904       parser->parse(xmlSource);
905     }
906   catch (const XMLException& e)
907     {
908       char* message = XMLString::transcode(e.getMessage());
909       DefaultLogSink().Error("Parser error: %s\n", message);
910       XMLString::release(&message);
911       errorCount++;
912     }
913   catch (const SAXParseException& e)
914     {
915       char* message = XMLString::transcode(e.getMessage());
916       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
917       XMLString::release(&message);
918       errorCount++;
919     }
920   catch (...)
921     {
922       DefaultLogSink().Error("Unexpected XML parser error\n");
923       errorCount++;
924     }
925   
926   if ( errorCount == 0 )
927     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
928
929   delete parser;
930   delete docHandler;
931
932   return errorCount > 0 ? false : true;
933 }
934
935 //
936 bool
937 Kumu::StringIsXML(const char* document, ui32_t len)
938 {
939   if ( document == 0 || *document == 0 )
940     return false;
941
942   init_xml_dom();
943
944   if ( len == 0 )
945     len = strlen(document);
946
947   SAXParser parser;
948   XMLPScanToken token;
949   bool status = false;
950
951   try
952     {
953       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
954                                   static_cast<const unsigned int>(len),
955                                   "pidc_rules_file");
956
957       if ( parser.parseFirst(xmlSource, token) )
958         {
959           if ( parser.parseNext(token) )
960             status = true;
961         }
962     }
963   catch (...)
964     {
965     }
966   
967   return status;
968 }
969
970
971 #endif
972
973 //----------------------------------------------------------------------------------------------------
974
975 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
976
977 //
978 bool
979 Kumu::XMLElement::ParseString(const std::string& document)
980 {
981   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
982   return false;
983 }
984
985 //
986 bool
987 Kumu::StringIsXML(const char* document, ui32_t len)
988 {
989   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
990   return false;
991 }
992
993 #endif
994
995
996 //
997 // end KM_xml.cpp
998 //