98dccc4bdd9e0c7c290659cb61f0fb262ea15c0f
[libdcp.git] / asdcplib / 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: KM_xml.cpp,v 1.19 2011/12/01 18:42:39 jhurst Exp $
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 bool
380 Kumu::XMLElement::ParseString(const ByteString& document)
381 {
382   return ParseString((const char*)document.RoData(), document.Length());
383 }
384
385 //
386 bool
387 Kumu::XMLElement::ParseString(const std::string& document)
388 {
389   return ParseString(document.c_str(), document.size());
390 }
391
392
393 //----------------------------------------------------------------------------------------------------
394
395 #ifdef HAVE_EXPAT
396
397
398 class ExpatParseContext
399 {
400   KM_NO_COPY_CONSTRUCT(ExpatParseContext);
401   ExpatParseContext();
402 public:
403   ns_map*                  Namespaces;
404   std::stack<XMLElement*>  Scope;
405   XMLElement*              Root;
406
407   ExpatParseContext(XMLElement* root) : Root(root) {
408     Namespaces = new ns_map;
409     assert(Root);
410   }
411
412   ~ExpatParseContext() {}
413 };
414
415 // expat wrapper functions
416 // 
417 static void
418 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
419 {
420   assert(p);  assert(name);  assert(attrs);
421   ExpatParseContext* Ctx = (ExpatParseContext*)p;
422   XMLElement* Element;
423
424   const char* ns_root = name;
425   const char* local_name = strchr(name, '|');
426   if ( local_name != 0 )
427     name = local_name + 1;
428
429   if ( Ctx->Scope.empty() )
430     {
431       Ctx->Scope.push(Ctx->Root);
432     }
433   else
434     {
435       Element = Ctx->Scope.top();
436       Ctx->Scope.push(Element->AddChild(name));
437     }
438
439   Element = Ctx->Scope.top();
440   Element->SetName(name);
441
442   // map the namespace
443   std::string key;
444   if ( ns_root != name )
445     key.assign(ns_root, name - ns_root - 1);
446   
447   ns_map::iterator ni = Ctx->Namespaces->find(key);
448   if ( ni != Ctx->Namespaces->end() )
449     Element->SetNamespace(ni->second);
450
451   // set attributes
452   for ( int i = 0; attrs[i] != 0; i += 2 )
453     {
454       if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
455         local_name = attrs[i];
456       else
457         local_name++;
458
459       Element->SetAttr(local_name, attrs[i+1]);
460     }
461 }
462
463 //
464 static void
465 xph_end(void* p, const XML_Char* name)
466 {
467   assert(p);  assert(name);
468   ExpatParseContext* Ctx = (ExpatParseContext*)p;
469   Ctx->Scope.pop();
470 }
471
472 //
473 static void
474 xph_char(void* p, const XML_Char* data, int len)
475 {
476   assert(p);  assert(data);
477   ExpatParseContext* Ctx = (ExpatParseContext*)p;
478
479   if ( len > 0 )
480     {
481       std::string tmp_str;
482       tmp_str.assign(data, len);
483       Ctx->Scope.top()->AppendBody(tmp_str);
484     }
485 }
486
487 //
488 void
489 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
490 {
491   assert(p);  assert(ns_name);
492   ExpatParseContext* Ctx = (ExpatParseContext*)p;
493   
494   if ( ns_prefix == 0 )
495     ns_prefix = "";
496
497   ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
498
499   if  ( ni != Ctx->Namespaces->end() )
500     {
501       if ( ni->second->Name() != std::string(ns_name) )
502         {
503           DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
504           return;
505         }
506     }
507   else
508     {
509       XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
510       Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
511     }
512 }
513
514 //
515 bool
516 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
517 {
518   XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
519
520   if ( Parser == 0 )
521     {
522       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
523       return false;
524     }
525
526   ExpatParseContext Ctx(this);
527   XML_SetUserData(Parser, (void*)&Ctx);
528   XML_SetElementHandler(Parser, xph_start, xph_end);
529   XML_SetCharacterDataHandler(Parser, xph_char);
530   XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
531
532   if ( ! XML_Parse(Parser, document, doc_len, 1) )
533     {
534       XML_ParserFree(Parser);
535       DefaultLogSink().Error("XML Parse error on line %d: %s\n",
536                              XML_GetCurrentLineNumber(Parser),
537                              XML_ErrorString(XML_GetErrorCode(Parser)));
538       return false;
539     }
540
541   XML_ParserFree(Parser);
542
543   if ( ! Ctx.Namespaces->empty() )
544     m_NamespaceOwner = (void*)Ctx.Namespaces;
545
546   return true;
547 }
548
549 //------------------------------------------------------------------------------------------
550
551 struct xph_test_wrapper
552 {
553   XML_Parser Parser;
554   bool  Status;
555
556   xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
557 };
558
559 // expat wrapper functions, map callbacks to IASAXHandler
560 // 
561 static void
562 xph_test_start(void* p, const XML_Char*, const XML_Char**)
563 {
564   assert(p);
565   xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
566
567   Wrapper->Status = true;
568   XML_StopParser(Wrapper->Parser, false);
569 }
570
571
572 //
573 bool
574 Kumu::StringIsXML(const char* document, ui32_t len)
575 {
576   if ( document == 0 )
577     return false;
578
579   if ( len == 0 )
580     len = strlen(document);
581
582   XML_Parser Parser = XML_ParserCreate("UTF-8");
583
584   if ( Parser == 0 )
585     {
586       DefaultLogSink().Error("Error allocating memory for XML parser.\n");
587       return false;
588     }
589
590   xph_test_wrapper Wrapper(Parser);
591   XML_SetUserData(Parser, (void*)&Wrapper);
592   XML_SetStartElementHandler(Parser, xph_test_start);
593
594   XML_Parse(Parser, document, len, 1);
595   XML_ParserFree(Parser);
596   return Wrapper.Status;
597 }
598
599 #endif
600
601 //----------------------------------------------------------------------------------------------------
602
603 #ifdef HAVE_XERCES_C
604
605 static Mutex sg_xerces_init_lock; // protect the xerces initialized
606 static bool  sg_xml_init = false; // signal initialization
607 static Mutex sg_coder_lock;       // protect the transcoder context 
608 static XMLTranscoder*   sg_coder = 0;
609 static const int sg_coder_buf_len = 128 * 1024;
610 static char sg_coder_buf[sg_coder_buf_len + 8];
611 static unsigned char sg_coder_counts[sg_coder_buf_len / sizeof(XMLCh)]; // see XMLTranscoder::transcodeFrom
612
613 static const XMLCh sg_LS[] = { chLatin_L, chLatin_S, chNull };
614 static const XMLCh sg_label_UTF_8[] = { chLatin_U, chLatin_T, chLatin_F,
615                                         chDash, chDigit_8, chNull}; 
616
617 //
618 void
619 Kumu::init_xml_dom()
620 {
621   if ( ! sg_xml_init )
622     {
623       AutoMutex AL(sg_xerces_init_lock);
624
625       if ( ! sg_xml_init )
626         {
627           try
628             {
629               XMLPlatformUtils::Initialize();
630               sg_xml_init = true;
631
632               XMLTransService::Codes ret;
633               sg_coder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(sg_label_UTF_8, ret, sg_coder_buf_len);
634
635               if ( ret != XMLTransService::Ok )
636                 {
637                   const char* message = "Undefined Error";
638
639                   switch ( ret )
640                     {
641                     case XMLTransService::UnsupportedEncoding:  message = "Unsupported encoding";  break;
642                     case XMLTransService::InternalFailure:      message = "Internal failure";  break;
643                     case XMLTransService::SupportFilesNotFound: message = "Support files not found";  break;
644                     }
645
646                   DefaultLogSink().Error("Xerces transform initialization error: %s\n", message);
647                 }
648             }
649           catch (const XMLException &e)
650             {
651               DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
652             }
653         }
654     }
655 }
656
657 //
658 bool
659 Kumu::XercesString_to_UTF_8(const Kumu::XercesString& in_str, std::string& out_str) {
660   return XercesString_to_UTF_8(in_str.c_str(), out_str);
661 }
662
663 //
664 bool
665 Kumu::XercesString_to_UTF_8(const XMLCh* in_str, std::string& out_str)
666 {
667   assert(in_str);
668   assert(sg_xml_init);
669   AutoMutex AL(sg_coder_lock);
670   ui32_t str_len = XMLString::stringLen(in_str);
671   ui32_t read_total = 0;
672
673   try
674     {
675       while ( str_len > 0 )
676         {
677 #if XERCES_VERSION_MAJOR < 3
678           ui32_t read_count = 0;
679 #else
680           XMLSize_t read_count = 0;
681 #endif
682           ui32_t write_count = sg_coder->transcodeTo(in_str + read_total, str_len,
683                                                      (XMLByte*)sg_coder_buf, sg_coder_buf_len,
684                                                      read_count, XMLTranscoder::UnRep_Throw);
685
686           out_str.append(sg_coder_buf, write_count);
687           str_len -= read_count;
688           read_total += read_count;
689           assert(str_len >= 0);
690         }
691     }
692   catch (...)
693     {
694       return false;
695     }
696
697   return true;
698 }
699
700 //
701 bool
702 Kumu::UTF_8_to_XercesString(const std::string& in_str, Kumu::XercesString& out_str) {
703   return UTF_8_to_XercesString(in_str.c_str(), out_str);
704 }
705
706 //
707 bool
708 Kumu::UTF_8_to_XercesString(const char* in_str, Kumu::XercesString& out_str)
709 {
710   assert(in_str);
711   assert(sg_xml_init);
712   AutoMutex AL(sg_coder_lock);
713   ui32_t str_len = strlen(in_str);
714   ui32_t read_total = 0;
715
716   try
717     {
718       while ( str_len > 0 )
719         {
720 #if XERCES_VERSION_MAJOR < 3
721           ui32_t read_count = 0;
722 #else
723           XMLSize_t read_count = 0;
724 #endif
725           ui32_t write_count = sg_coder->transcodeFrom((const XMLByte*)(in_str + read_total), str_len,
726                                                        (XMLCh*)sg_coder_buf, sg_coder_buf_len / sizeof(XMLCh),
727                                                        read_count, sg_coder_counts);
728
729           out_str.append((XMLCh*)sg_coder_buf, write_count * sizeof(XMLCh));
730           str_len -= read_count;
731           read_total += read_count;
732           assert(str_len >= 0);
733         }
734     }
735   catch (...)
736     {
737       return false;
738     }
739
740   return true;
741 }
742
743 //
744 class MyTreeHandler : public HandlerBase
745 {
746   ns_map*                  m_Namespaces;
747   std::stack<XMLElement*>  m_Scope;
748   XMLElement*              m_Root;
749   bool                     m_HasEncodeErrors;
750
751 public:
752   MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root), m_HasEncodeErrors(false)
753   {
754     assert(m_Root);
755     m_Namespaces = new ns_map;
756   }
757
758   ~MyTreeHandler() {
759     delete m_Namespaces;
760   }
761
762   bool HasEncodeErrors() const { return m_HasEncodeErrors; }
763
764   ns_map* TakeNamespaceMap()
765   {
766     if ( m_Namespaces == 0 || m_Namespaces->empty() )
767       return 0;
768
769     ns_map* ret = m_Namespaces;
770     m_Namespaces = 0;
771     return ret;
772   }
773
774   //
775   void AddNamespace(const char* ns_prefix, const char* ns_name)
776   {
777     assert(ns_prefix);
778     assert(ns_name);
779
780     if ( ns_prefix[0] == ':' )
781       {
782         ns_prefix++;
783       }
784     else
785       {
786         assert(ns_prefix[0] == 0);
787         ns_prefix = "";
788       }
789
790     ns_map::iterator ni = m_Namespaces->find(ns_name);
791
792     if  ( ni != m_Namespaces->end() )
793       {
794         if ( ni->second->Name() != std::string(ns_name) )
795           {
796             DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
797             return;
798           }
799       }
800     else
801       {
802         XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
803         m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
804       }
805
806     assert(!m_Namespaces->empty());
807   }
808
809   //
810   void startElement(const XMLCh* const x_name,
811                     XERCES_CPP_NAMESPACE::AttributeList& attributes)
812   {
813     assert(x_name);
814     std::string tx_name;
815
816     if ( ! XercesString_to_UTF_8(x_name, tx_name) )
817       m_HasEncodeErrors = true;
818
819     const char* name = tx_name.c_str();
820     XMLElement* Element;
821     const char* ns_root = name;
822     const char* local_name = strchr(name, ':');
823
824     if ( local_name != 0 )
825       name = local_name + 1;
826
827     if ( m_Scope.empty() )
828       {
829         m_Scope.push(m_Root);
830       }
831     else
832       {
833         Element = m_Scope.top();
834         m_Scope.push(Element->AddChild(name));
835       }
836
837     Element = m_Scope.top();
838     Element->SetName(name);
839
840     // set attributes
841     ui32_t a_len = attributes.getLength();
842
843     for ( ui32_t i = 0; i < a_len; i++)
844       {
845         std::string aname, value;
846         if ( ! XercesString_to_UTF_8(attributes.getName(i), aname) )
847           m_HasEncodeErrors = true;
848
849         if ( ! XercesString_to_UTF_8(attributes.getValue(i), value) )
850           m_HasEncodeErrors = true;
851
852         const char* x_aname = aname.c_str();
853         const char* x_value = value.c_str();
854
855         if ( strncmp(x_aname, "xmlns", 5) == 0 )
856           AddNamespace(x_aname+5, x_value);
857
858         if ( ( local_name = strchr(x_aname, ':') ) == 0 )
859           local_name = x_aname;
860         else
861           local_name++;
862
863         Element->SetAttr(local_name, x_value);
864       }
865
866     // map the namespace
867     std::string key;
868     if ( ns_root != name )
869       key.assign(ns_root, name - ns_root - 1);
870   
871     ns_map::iterator ni = m_Namespaces->find(key);
872     if ( ni != m_Namespaces->end() )
873       Element->SetNamespace(ni->second);
874   }
875
876   void endElement(const XMLCh *const name) {
877     m_Scope.pop();
878   }
879
880   void characters(const XMLCh *const chars, const unsigned int length)
881   {
882     if ( length > 0 )
883       {
884         std::string tmp;
885         if ( ! XercesString_to_UTF_8(chars, tmp) )
886           m_HasEncodeErrors = true;
887
888         m_Scope.top()->AppendBody(tmp);
889       }
890   }
891 };
892
893 //
894 bool
895 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
896 {
897   if ( doc_len == 0 )
898     return false;
899
900   init_xml_dom();
901
902   int errorCount = 0;
903   SAXParser* parser = new SAXParser();
904
905   parser->setValidationScheme(SAXParser::Val_Always);
906   parser->setDoNamespaces(true);    // optional
907
908   MyTreeHandler* docHandler = new MyTreeHandler(this);
909   parser->setDocumentHandler(docHandler);
910   parser->setErrorHandler(docHandler);
911
912   try
913     {
914       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
915                                   static_cast<const unsigned int>(doc_len),
916                                   "pidc_rules_file");
917
918       parser->parse(xmlSource);
919     }
920   catch (const XMLException& e)
921     {
922       char* message = XMLString::transcode(e.getMessage());
923       DefaultLogSink().Error("Parser error: %s\n", message);
924       XMLString::release(&message);
925       errorCount++;
926     }
927   catch (const SAXParseException& e)
928     {
929       char* message = XMLString::transcode(e.getMessage());
930       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
931       XMLString::release(&message);
932       errorCount++;
933     }
934   catch (...)
935     {
936       DefaultLogSink().Error("Unexpected XML parser error\n");
937       errorCount++;
938     }
939   
940   if ( errorCount == 0 )
941     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
942
943   delete parser;
944   delete docHandler;
945
946   return errorCount > 0 ? false : true;
947 }
948
949 //
950 bool
951 Kumu::StringIsXML(const char* document, ui32_t len)
952 {
953   if ( document == 0 || *document == 0 )
954     return false;
955
956   init_xml_dom();
957
958   if ( len == 0 )
959     len = strlen(document);
960
961   SAXParser parser;
962   XMLPScanToken token;
963   bool status = false;
964
965   try
966     {
967       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
968                                   static_cast<const unsigned int>(len),
969                                   "pidc_rules_file");
970
971       if ( parser.parseFirst(xmlSource, token) )
972         {
973           if ( parser.parseNext(token) )
974             status = true;
975         }
976     }
977   catch (...)
978     {
979     }
980   
981   return status;
982 }
983
984
985 #endif
986
987 //----------------------------------------------------------------------------------------------------
988
989 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
990
991 //
992 bool
993 Kumu::XMLElement::ParseString(const char*, ui32_t)
994 {
995   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
996   return false;
997 }
998
999 //
1000 bool
1001 Kumu::StringIsXML(const char*, ui32_t)
1002 {
1003   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
1004   return false;
1005 }
1006
1007 #endif
1008
1009
1010 //
1011 // end KM_xml.cpp
1012 //