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