Reverting const accessor for class optional_property
[asdcplib.git] / src / KM_xml.cpp
index dcb35a3a4e60a427353839a4a280b3866b9d50f4..68839a178e33b9f98fdd2d07d5beb4a41c3f6358 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2010, John Hurst
+Copyright (c) 2005-2015, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -61,13 +61,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 XERCES_CPP_NAMESPACE_USE 
 
-namespace Kumu {
-  void init_xml_dom();
-  typedef std::basic_string<XMLCh> XercesString;
-  bool UTF_8_to_XercesString(const std::string& in_str, XercesString& out_str);
-  bool UTF_8_to_XercesString(const char* in_str, XercesString& out_str);
-  bool XercesString_to_UTF_8(const XercesString& in_str, std::string& out_str);
-  bool XercesString_to_UTF_8(const XMLCh* in_str, std::string& out_str);
+extern "C"
+{
+  void kumu_init_xml_dom();
+  bool kumu_UTF_8_to_XercesString(const std::string& in_str, std::basic_string<XMLCh>& out_str);
+  bool kumu_UTF_8_to_XercesString_p(const char* in_str, std::basic_string<XMLCh>& out_str);
+  bool kumu_XercesString_to_UTF_8(const std::basic_string<XMLCh>& in_str, std::string& out_str);
+  bool kumu_XercesString_to_UTF_8_p(const XMLCh* in_str, std::string& out_str);
 }
 
 #endif
@@ -399,6 +399,20 @@ Kumu::XMLElement::ParseString(const std::string& document)
   return ParseString(document.c_str(), document.size());
 }
 
+//
+bool
+Kumu::XMLElement::ParseFirstFromString(const ByteString& document)
+{
+  return ParseFirstFromString((const char*)document.RoData(), document.Length());
+}
+
+//
+bool
+Kumu::XMLElement::ParseFirstFromString(const std::string& document)
+{
+  return ParseFirstFromString(document.c_str(), document.size());
+}
+
 
 //----------------------------------------------------------------------------------------------------
 
@@ -525,6 +539,11 @@ xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
 bool
 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
 {
+  if ( doc_len == 0 )
+    {
+      return false;
+    }
+
   XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
 
   if ( Parser == 0 )
@@ -551,45 +570,33 @@ Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
   XML_ParserFree(Parser);
 
   if ( ! Ctx.Namespaces->empty() )
-    m_NamespaceOwner = (void*)Ctx.Namespaces;
+    {
+      m_NamespaceOwner = (void*)Ctx.Namespaces;
+    }
 
   return true;
 }
 
-//------------------------------------------------------------------------------------------
-
-struct xph_test_wrapper
-{
-  XML_Parser Parser;
-  bool  Status;
-
-  xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
-};
-
-// expat wrapper functions, map callbacks to IASAXHandler
+// expat wrapper functions
 // 
 static void
-xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
+xph_start_one_shot(void* p, const XML_Char* name, const XML_Char** attrs)
 {
-  assert(p);
-  xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
-
-  Wrapper->Status = true;
-  XML_StopParser(Wrapper->Parser, false);
+  xph_start(p, name, attrs);
+  XML_Parser parser = (XML_Parser)p;
+  XML_StopParser(parser, false);
 }
 
-
 //
 bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
 {
-  if ( document == 0 )
-    return false;
-
-  if ( len == 0 )
-    len = strlen(document);
+  if ( doc_len == 0 )
+    {
+      return false;
+    }
 
-  XML_Parser Parser = XML_ParserCreate("UTF-8");
+  XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
 
   if ( Parser == 0 )
     {
@@ -597,15 +604,30 @@ Kumu::StringIsXML(const char* document, ui32_t len)
       return false;
     }
 
-  xph_test_wrapper Wrapper(Parser);
-  XML_SetUserData(Parser, (void*)&Wrapper);
-  XML_SetStartElementHandler(Parser, xph_test_start);
+  ExpatParseContext Ctx(this);
+  XML_SetUserData(Parser, (void*)&Ctx);
+  XML_SetElementHandler(Parser, xph_start_one_shot, xph_end);
+  XML_SetCharacterDataHandler(Parser, xph_char);
+  XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
+
+  if ( ! XML_Parse(Parser, document, doc_len, 1) )
+    {
+      XML_ParserFree(Parser);
+      return false;
+    }
 
-  XML_Parse(Parser, document, len, 1);
   XML_ParserFree(Parser);
-  return Wrapper.Status;
+
+  if ( ! Ctx.Namespaces->empty() )
+    {
+      m_NamespaceOwner = (void*)Ctx.Namespaces;
+    }
+
+  return true;
 }
 
+
+
 #endif
 
 //----------------------------------------------------------------------------------------------------
@@ -626,7 +648,7 @@ static const XMLCh sg_label_UTF_8[] = { chLatin_U, chLatin_T, chLatin_F,
 
 //
 void
-Kumu::init_xml_dom()
+kumu_init_xml_dom()
 {
   if ( ! sg_xml_init )
     {
@@ -666,13 +688,13 @@ Kumu::init_xml_dom()
 
 //
 bool
-Kumu::XercesString_to_UTF_8(const Kumu::XercesString& in_str, std::string& out_str) {
-  return XercesString_to_UTF_8(in_str.c_str(), out_str);
+kumu_XercesString_to_UTF_8(const std::basic_string<XMLCh>& in_str, std::string& out_str) {
+  return kumu_XercesString_to_UTF_8_p(in_str.c_str(), out_str);
 }
 
 //
 bool
-Kumu::XercesString_to_UTF_8(const XMLCh* in_str, std::string& out_str)
+kumu_XercesString_to_UTF_8_p(const XMLCh* in_str, std::string& out_str)
 {
   assert(in_str);
   assert(sg_xml_init);
@@ -709,13 +731,13 @@ Kumu::XercesString_to_UTF_8(const XMLCh* in_str, std::string& out_str)
 
 //
 bool
-Kumu::UTF_8_to_XercesString(const std::string& in_str, Kumu::XercesString& out_str) {
-  return UTF_8_to_XercesString(in_str.c_str(), out_str);
+kumu_UTF_8_to_XercesString(const std::string& in_str, std::basic_string<XMLCh>& out_str) {
+  return kumu_UTF_8_to_XercesString_p(in_str.c_str(), out_str);
 }
 
 //
 bool
-Kumu::UTF_8_to_XercesString(const char* in_str, Kumu::XercesString& out_str)
+kumu_UTF_8_to_XercesString_p(const char* in_str, std::basic_string<XMLCh>& out_str)
 {
   assert(in_str);
   assert(sg_xml_init);
@@ -797,7 +819,7 @@ public:
        ns_prefix = "";
       }
 
-    ns_map::iterator ni = m_Namespaces->find(ns_name);
+    ns_map::iterator ni = m_Namespaces->find(ns_prefix);
 
     if  ( ni != m_Namespaces->end() )
       {
@@ -823,7 +845,7 @@ public:
     assert(x_name);
     std::string tx_name;
 
-    if ( ! XercesString_to_UTF_8(x_name, tx_name) )
+    if ( ! kumu_XercesString_to_UTF_8(x_name, tx_name) )
       m_HasEncodeErrors = true;
 
     const char* name = tx_name.c_str();
@@ -853,10 +875,10 @@ public:
     for ( ui32_t i = 0; i < a_len; i++)
       {
        std::string aname, value;
-       if ( ! XercesString_to_UTF_8(attributes.getName(i), aname) )
+       if ( ! kumu_XercesString_to_UTF_8(attributes.getName(i), aname) )
          m_HasEncodeErrors = true;
 
-       if ( ! XercesString_to_UTF_8(attributes.getValue(i), value) )
+       if ( ! kumu_XercesString_to_UTF_8(attributes.getValue(i), value) )
          m_HasEncodeErrors = true;
 
        const char* x_aname = aname.c_str();
@@ -887,12 +909,16 @@ public:
     m_Scope.pop();
   }
 
+#if XERCES_VERSION_MAJOR < 3
   void characters(const XMLCh *const chars, const unsigned int length)
+#else
+  void characters(const XMLCh* const chars, const XMLSize_t length)
+#endif
   {
     if ( length > 0 )
       {
        std::string tmp;
-       if ( ! XercesString_to_UTF_8(chars, tmp) )
+       if ( ! kumu_XercesString_to_UTF_8(chars, tmp) )
          m_HasEncodeErrors = true;
 
        m_Scope.top()->AppendBody(tmp);
@@ -905,9 +931,11 @@ bool
 Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
 {
   if ( doc_len == 0 )
-    return false;
+    {
+      return false;
+    }
 
-  init_xml_dom();
+  kumu_init_xml_dom();
 
   int errorCount = 0;
   SAXParser* parser = new SAXParser();
@@ -958,37 +986,56 @@ Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
 
 //
 bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
 {
-  if ( document == 0 || *document == 0 )
-    return false;
+  if ( doc_len == 0 )
+    {
+      return false;
+    }
 
-  init_xml_dom();
+  kumu_init_xml_dom();
+  
+  int errorCount = 0;
+  SAXParser* parser = new SAXParser();
 
-  if ( len == 0 )
-    len = strlen(document);
+  parser->setValidationScheme(SAXParser::Val_Always);
+  parser->setDoNamespaces(true);    // optional
 
-  SAXParser parser;
+  MyTreeHandler* docHandler = new MyTreeHandler(this);
+  parser->setDocumentHandler(docHandler);
+  parser->setErrorHandler(docHandler);
   XMLPScanToken token;
-  bool status = false;
 
   try
     {
       MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
-                                 static_cast<const unsigned int>(len),
+                                 static_cast<const unsigned int>(doc_len),
                                  "pidc_rules_file");
 
-      if ( parser.parseFirst(xmlSource, token) )
+      if ( ! parser->parseFirst(xmlSource, token) )
+       {
+         ++errorCount;
+       }
+
+      if ( ! parser->parseNext(token) )
        {
-         if ( parser.parseNext(token) )
-           status = true;
+         ++errorCount;
        }
     }
   catch (...)
     {
+      errorCount++;
     }
   
-  return status;
+  if ( errorCount == 0 )
+    {
+      m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
+    }
+
+  delete parser;
+  delete docHandler;
+
+  return errorCount > 0 ? false : true;
 }
 
 
@@ -1006,9 +1053,8 @@ Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
   return false;
 }
 
-//
 bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
 {
   DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
   return false;
@@ -1040,124 +1086,27 @@ bool
 Kumu::GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
                    AttributeList& doc_attr_list)
 {
-  assert(buf);
-  const byte_t *p1 = buf, *p2;
-  const byte_t *end_p = buf + buf_len;
+  XMLElement tmp_element("tmp");
 
-  while ( p1 < end_p && *p1 )
+  if ( ! tmp_element.ParseFirstFromString((const char*)buf, buf_len) )
     {
-      if ( *p1 == '<' && isalpha(p1[1]) )
-        {
-          p2 = ++p1;
-
-          // collect element name
-          while ( p2 < end_p && *p2 && ! ( isspace(*p2) || *p2 == '>' ) )
-            ++p2;
-
-          if ( p2 < end_p )
-            {
-              const byte_t* separator = (byte_t*)strchr(reinterpret_cast<const char*>(p1), ':');
-              if ( separator != 0 && separator < p2 )
-                {
-                  ns_prefix.assign(reinterpret_cast<const char*>(p1), separator - p1);
-                  p1 = separator + 1;
-                }
-
-              type_name.assign(reinterpret_cast<const char*>(p1), p2 - p1);
-              break;
-            }
-        }
-      else if ( *p1 == '<' && ( ( p1 + 3 ) < end_p ) && p1[1] == '!' && p1[2] == '-' && p1[3] == '-' )
-       {
-          p1 += 4;
-
-         for (;;)
-           {
-             while ( *p1 != '>' && p1 < end_p )
-               {
-                 ++p1;
-               }
-
-             if ( *(p1-2) == '-' && *(p1-1) == '-' && *p1 == '>' )
-               {
-                 break;
-               }
-
-             ++p1;
-           }
-       }
-      else
-       {
-         ++p1;
-       }
+      return false;
     }
 
-  if ( isspace(*p2) )
-    {
-      const byte_t *p3 = p2+1;
-      while ( p3 < end_p && *p3 && *p3 != '>'  )
-       {       
-         ++p3;
-       }
+  const XMLNamespace* ns = tmp_element.Namespace();
 
-      if ( *p3 != '>' )
-       {
-         return false; // not well-formed XML
-       }
-
-      std::string attr_str;
-      attr_str.assign(reinterpret_cast<const char*>(p2+1), p3 - p2 - 1);
-      
-      // normalize whitespace so the subesquent split works properly
-      for ( int j = 0; j < attr_str.length(); ++j )
-       {
-         if ( attr_str[j] != ' ' && isspace(attr_str[j]) )
-           {
-             attr_str[j] = ' ';
-           }
-       }
-
-      std::list<std::string> doc_attr_nvpairs = km_token_split(attr_str, " ");
-      
-      std::list<std::string>::iterator i;
-      std::map<std::string, std::string> ns_map;
-
-      for ( i = doc_attr_nvpairs.begin(); i != doc_attr_nvpairs.end(); ++i )
-        {
-         // trim leading and trailing whitespace an right-most character, i.e., \"
-         std::string trimmed = i->substr(i->find_first_not_of(" "), i->find_last_not_of(" \r\n\t"));
-          std::list<std::string> nv_tokens = km_token_split(trimmed, "=\"");
-
-          if ( nv_tokens.size() != 2 )
-           {
-             continue;
-           }
-
-         NVPair nv_pair;
-         nv_pair.name = nv_tokens.front();
-         nv_pair.value = nv_tokens.back();
-         doc_attr_list.push_back(nv_pair);
-          ns_map.insert(std::map<std::string,std::string>::value_type(nv_pair.name, nv_pair.value));
-        }
-
-      std::string doc_ns_name_selector = ns_prefix.empty() ? "xmlns" : "xmlns:"+ns_prefix;
-      std::map<std::string,std::string>::iterator j = ns_map.find(doc_ns_name_selector);
-
-      if ( j != ns_map.end() )
-        {
-          namespace_name = j->second;
-        }
+  if ( ns != 0 )
+    {
+      ns_prefix = ns->Prefix();
+      namespace_name = ns->Name();
     }
- else if ( *p2 != '>' )
-   {
-     return false; // not well-formed XML
-   }
 
-  return ! type_name.empty();
+  type_name = tmp_element.GetName();
+  doc_attr_list = tmp_element.GetAttributes();
+  return true;
 }
 
 
-
 //
 // end KM_xml.cpp
 //