a33d45ce6db15f52a4f089328d320e2aa1244ced
[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 std::string& document)
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.c_str(), document.size(), 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           ui32_t read_count = 0;
664           ui32_t write_count = sg_coder->transcodeTo(in_str + read_total, str_len,
665                                                      (XMLByte*)sg_coder_buf, sg_coder_buf_len,
666                                                      read_count, XMLTranscoder::UnRep_Throw);
667
668           out_str.append(sg_coder_buf, write_count);
669           str_len -= read_count;
670           read_total += read_count;
671           assert(str_len >= 0);
672         }
673     }
674   catch (...)
675     {
676       return false;
677     }
678
679   return true;
680 }
681
682 //
683 bool
684 Kumu::UTF_8_to_XercesString(const std::string& in_str, Kumu::XercesString& out_str) {
685   return UTF_8_to_XercesString(in_str.c_str(), out_str);
686 }
687
688 //
689 bool
690 Kumu::UTF_8_to_XercesString(const char* in_str, Kumu::XercesString& out_str)
691 {
692   assert(in_str);
693   assert(sg_xml_init);
694   AutoMutex AL(sg_coder_lock);
695   ui32_t str_len = strlen(in_str);
696   ui32_t read_total = 0;
697
698   try
699     {
700       while ( str_len > 0 )
701         {
702           ui32_t read_count = 0;
703           ui32_t write_count = sg_coder->transcodeFrom((const XMLByte*)(in_str + read_total), str_len,
704                                                        (XMLCh*)sg_coder_buf, sg_coder_buf_len / sizeof(XMLCh),
705                                                        read_count, sg_coder_counts);
706
707           out_str.append((XMLCh*)sg_coder_buf, write_count * sizeof(XMLCh));
708           str_len -= read_count;
709           read_total += read_count;
710           assert(str_len >= 0);
711         }
712     }
713   catch (...)
714     {
715       return false;
716     }
717
718   return true;
719 }
720
721 //
722 class MyTreeHandler : public HandlerBase
723 {
724   ns_map*                  m_Namespaces;
725   std::stack<XMLElement*>  m_Scope;
726   XMLElement*              m_Root;
727   bool                     m_HasEncodeErrors;
728
729 public:
730   MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root), m_HasEncodeErrors(false)
731   {
732     assert(m_Root);
733     m_Namespaces = new ns_map;
734   }
735
736   ~MyTreeHandler() {
737     delete m_Namespaces;
738   }
739
740   bool HasEncodeErrors() const { return m_HasEncodeErrors; }
741
742   ns_map* TakeNamespaceMap()
743   {
744     if ( m_Namespaces == 0 || m_Namespaces->empty() )
745       return 0;
746
747     ns_map* ret = m_Namespaces;
748     m_Namespaces = 0;
749     return ret;
750   }
751
752   //
753   void AddNamespace(const char* ns_prefix, const char* ns_name)
754   {
755     assert(ns_prefix);
756     assert(ns_name);
757
758     if ( ns_prefix[0] == ':' )
759       {
760         ns_prefix++;
761       }
762     else
763       {
764         assert(ns_prefix[0] == 0);
765         ns_prefix = "";
766       }
767
768     ns_map::iterator ni = m_Namespaces->find(ns_name);
769
770     if  ( ni != m_Namespaces->end() )
771       {
772         if ( ni->second->Name() != std::string(ns_name) )
773           {
774             DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
775             return;
776           }
777       }
778     else
779       {
780         XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
781         m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
782       }
783
784     assert(!m_Namespaces->empty());
785   }
786
787   //
788   void startElement(const XMLCh* const x_name,
789                     XERCES_CPP_NAMESPACE::AttributeList& attributes)
790   {
791     assert(x_name);
792     std::string tx_name;
793
794     if ( ! XercesString_to_UTF_8(x_name, tx_name) )
795       m_HasEncodeErrors = true;
796
797     const char* name = tx_name.c_str();
798     XMLElement* Element;
799     const char* ns_root = name;
800     const char* local_name = strchr(name, ':');
801
802     if ( local_name != 0 )
803       name = local_name + 1;
804
805     if ( m_Scope.empty() )
806       {
807         m_Scope.push(m_Root);
808       }
809     else
810       {
811         Element = m_Scope.top();
812         m_Scope.push(Element->AddChild(name));
813       }
814
815     Element = m_Scope.top();
816     Element->SetName(name);
817
818     // set attributes
819     ui32_t a_len = attributes.getLength();
820
821     for ( ui32_t i = 0; i < a_len; i++)
822       {
823         std::string aname, value;
824         if ( ! XercesString_to_UTF_8(attributes.getName(i), aname) )
825           m_HasEncodeErrors = true;
826
827         if ( ! XercesString_to_UTF_8(attributes.getValue(i), value) )
828           m_HasEncodeErrors = true;
829
830         const char* x_aname = aname.c_str();
831         const char* x_value = value.c_str();
832
833         if ( strncmp(x_aname, "xmlns", 5) == 0 )
834           AddNamespace(x_aname+5, x_value);
835
836         if ( ( local_name = strchr(x_aname, ':') ) == 0 )
837           local_name = x_aname;
838         else
839           local_name++;
840
841         Element->SetAttr(local_name, x_value);
842       }
843
844     // map the namespace
845     std::string key;
846     if ( ns_root != name )
847       key.assign(ns_root, name - ns_root - 1);
848   
849     ns_map::iterator ni = m_Namespaces->find(key);
850     if ( ni != m_Namespaces->end() )
851       Element->SetNamespace(ni->second);
852   }
853
854   void endElement(const XMLCh *const name) {
855     m_Scope.pop();
856   }
857
858   void characters(const XMLCh *const chars, const unsigned int length)
859   {
860     if ( length > 0 )
861       {
862         std::string tmp;
863         if ( ! XercesString_to_UTF_8(chars, tmp) )
864           m_HasEncodeErrors = true;
865
866         m_Scope.top()->AppendBody(tmp);
867       }
868   }
869 };
870
871 //
872 bool
873 Kumu::XMLElement::ParseString(const std::string& document)
874 {
875   if ( document.empty() )
876     return false;
877
878   init_xml_dom();
879
880   int errorCount = 0;
881   SAXParser* parser = new SAXParser();
882
883   parser->setValidationScheme(SAXParser::Val_Always);
884   parser->setDoNamespaces(true);    // optional
885
886   MyTreeHandler* docHandler = new MyTreeHandler(this);
887   parser->setDocumentHandler(docHandler);
888   parser->setErrorHandler(docHandler);
889
890   try
891     {
892       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document.c_str()),
893                                   static_cast<const unsigned int>(document.size()),
894                                   "pidc_rules_file");
895
896       parser->parse(xmlSource);
897     }
898   catch (const XMLException& e)
899     {
900       char* message = XMLString::transcode(e.getMessage());
901       DefaultLogSink().Error("Parser error: %s\n", message);
902       XMLString::release(&message);
903       errorCount++;
904     }
905   catch (const SAXParseException& e)
906     {
907       char* message = XMLString::transcode(e.getMessage());
908       DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
909       XMLString::release(&message);
910       errorCount++;
911     }
912   catch (...)
913     {
914       DefaultLogSink().Error("Unexpected XML parser error\n");
915       errorCount++;
916     }
917   
918   if ( errorCount == 0 )
919     m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
920
921   delete parser;
922   delete docHandler;
923
924   return errorCount > 0 ? false : true;
925 }
926
927 //
928 bool
929 Kumu::StringIsXML(const char* document, ui32_t len)
930 {
931   if ( document == 0 || *document == 0 )
932     return false;
933
934   init_xml_dom();
935
936   if ( len == 0 )
937     len = strlen(document);
938
939   SAXParser parser;
940   XMLPScanToken token;
941   bool status = false;
942
943   try
944     {
945       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
946                                   static_cast<const unsigned int>(len),
947                                   "pidc_rules_file");
948
949       if ( parser.parseFirst(xmlSource, token) )
950         {
951           if ( parser.parseNext(token) )
952             status = true;
953         }
954     }
955   catch (...)
956     {
957     }
958   
959   return status;
960 }
961
962
963 #endif
964
965 //----------------------------------------------------------------------------------------------------
966
967 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
968
969 //
970 bool
971 Kumu::XMLElement::ParseString(const std::string& document)
972 {
973   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
974   return false;
975 }
976
977 //
978 bool
979 Kumu::StringIsXML(const char* document, ui32_t len)
980 {
981   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
982   return false;
983 }
984
985 #endif
986
987
988 //
989 // end KM_xml.cpp
990 //