2 Copyright (c) 2005-2009, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
39 //#define HAVE_XERCES_C
43 # error "Both HAVE_EXPAT and HAVE_XERCES_C defined"
50 # error "Both HAVE_EXPAT and HAVE_XERCES_C defined"
53 #include <xercesc/util/PlatformUtils.hpp>
54 #include <xercesc/util/XMLString.hpp>
55 #include <xercesc/sax/AttributeList.hpp>
56 #include <xercesc/sax/HandlerBase.hpp>
57 #include <xercesc/sax/ErrorHandler.hpp>
58 #include <xercesc/sax/SAXParseException.hpp>
59 #include <xercesc/parsers/SAXParser.hpp>
60 #include <xercesc/framework/MemBufInputSource.hpp>
61 #include <xercesc/framework/XMLPScanToken.hpp>
64 XERCES_CPP_NAMESPACE_USE
70 class ns_map : public std::map<std::string, XMLNamespace*>
77 ns_map::iterator ni = begin();
85 Kumu::XMLElement::XMLElement(const char* name) : m_Namespace(0), m_NamespaceOwner(0)
90 Kumu::XMLElement::~XMLElement()
92 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
95 delete (ns_map*)m_NamespaceOwner;
100 Kumu::XMLElement::SetAttr(const char* name, const char* value)
104 TmpVal.value = value;
106 m_AttrList.push_back(TmpVal);
111 Kumu::XMLElement::AddChild(Kumu::XMLElement* element)
113 m_ChildList.push_back(element); // takes posession!
119 Kumu::XMLElement::AddChild(const char* name)
121 XMLElement* tmpE = new XMLElement(name);
122 m_ChildList.push_back(tmpE);
128 Kumu::XMLElement::AddChildWithContent(const char* name, const std::string& value)
130 return AddChildWithContent(name, value.c_str());
135 Kumu::XMLElement::AppendBody(const std::string& value)
142 Kumu::XMLElement::SetBody(const std::string& value)
149 Kumu::XMLElement::AddChildWithContent(const char* name, const char* value)
153 XMLElement* tmpE = new XMLElement(name);
154 tmpE->m_Body = value;
155 m_ChildList.push_back(tmpE);
161 Kumu::XMLElement::AddChildWithPrefixedContent(const char* name, const char* prefix, const char* value)
163 XMLElement* tmpE = new XMLElement(name);
164 tmpE->m_Body = prefix;
165 tmpE->m_Body += value;
166 m_ChildList.push_back(tmpE);
172 Kumu::XMLElement::AddComment(const char* value)
181 Kumu::XMLElement::Render(std::string& outbuf) const
183 outbuf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
184 RenderElement(outbuf, 0);
189 add_spacer(std::string& outbuf, i32_t depth)
197 Kumu::XMLElement::RenderElement(std::string& outbuf, ui32_t depth) const
199 add_spacer(outbuf, depth);
205 for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
210 outbuf += (*i).value;
216 // body contents and children
217 if ( ! m_ChildList.empty() )
222 if ( m_Body.length() > 0 )
225 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
226 (*i)->RenderElement(outbuf, depth + 1);
228 add_spacer(outbuf, depth);
230 else if ( m_Body.length() > 0 )
242 Kumu::XMLElement::HasName(const char* name) const
244 if ( name == 0 || *name == 0 )
247 return (m_Name == name);
252 Kumu::XMLElement::SetName(const char* name)
260 Kumu::XMLElement::GetAttrWithName(const char* name) const
262 for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
264 if ( (*i).name == name )
265 return (*i).value.c_str();
273 Kumu::XMLElement::GetChildWithName(const char* name) const
275 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
277 if ( (*i)->HasName(name) )
285 const Kumu::ElementList&
286 Kumu::XMLElement::GetChildrenWithName(const char* name, ElementList& outList) const
289 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
291 if ( (*i)->HasName(name) )
292 outList.push_back(*i);
294 if ( ! (*i)->m_ChildList.empty() )
295 (*i)->GetChildrenWithName(name, outList);
303 Kumu::XMLElement::DeleteAttributes()
310 Kumu::XMLElement::DeleteAttrWithName(const char* name)
313 AttributeList::iterator i = m_AttrList.begin();
315 while ( i != m_AttrList.end() )
317 if ( i->name == std::string(name) )
318 m_AttrList.erase(i++);
326 Kumu::XMLElement::DeleteChildren()
328 while ( ! m_ChildList.empty() )
330 delete m_ChildList.back();
331 m_ChildList.pop_back();
337 Kumu::XMLElement::DeleteChild(const XMLElement* element)
341 for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
346 m_ChildList.erase(i);
355 Kumu::XMLElement::ForgetChild(const XMLElement* element)
359 for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
363 m_ChildList.erase(i);
371 //----------------------------------------------------------------------------------------------------
376 class ExpatParseContext
378 KM_NO_COPY_CONSTRUCT(ExpatParseContext);
382 std::stack<XMLElement*> Scope;
385 ExpatParseContext(XMLElement* root) : Root(root) {
386 Namespaces = new ns_map;
390 ~ExpatParseContext() {}
393 // expat wrapper functions
396 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
398 assert(p); assert(name); assert(attrs);
399 ExpatParseContext* Ctx = (ExpatParseContext*)p;
402 const char* ns_root = name;
403 const char* local_name = strchr(name, '|');
404 if ( local_name != 0 )
405 name = local_name + 1;
407 if ( Ctx->Scope.empty() )
409 Ctx->Scope.push(Ctx->Root);
413 Element = Ctx->Scope.top();
414 Ctx->Scope.push(Element->AddChild(name));
417 Element = Ctx->Scope.top();
418 Element->SetName(name);
422 if ( ns_root != name )
423 key.assign(ns_root, name - ns_root - 1);
425 ns_map::iterator ni = Ctx->Namespaces->find(key);
426 if ( ni != Ctx->Namespaces->end() )
427 Element->SetNamespace(ni->second);
430 for ( int i = 0; attrs[i] != 0; i += 2 )
432 if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
433 local_name = attrs[i];
437 Element->SetAttr(local_name, attrs[i+1]);
443 xph_end(void* p, const XML_Char* name)
445 assert(p); assert(name);
446 ExpatParseContext* Ctx = (ExpatParseContext*)p;
452 xph_char(void* p, const XML_Char* data, int len)
454 assert(p); assert(data);
455 ExpatParseContext* Ctx = (ExpatParseContext*)p;
460 tmp_str.assign(data, len);
461 Ctx->Scope.top()->AppendBody(tmp_str);
467 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
469 assert(p); assert(ns_name);
470 ExpatParseContext* Ctx = (ExpatParseContext*)p;
472 if ( ns_prefix == 0 )
475 ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
477 if ( ni != Ctx->Namespaces->end() )
479 if ( ni->second->Name() != std::string(ns_name) )
481 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
487 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
488 Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
494 Kumu::XMLElement::ParseString(const std::string& document)
496 XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
500 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
504 ExpatParseContext Ctx(this);
505 XML_SetUserData(Parser, (void*)&Ctx);
506 XML_SetElementHandler(Parser, xph_start, xph_end);
507 XML_SetCharacterDataHandler(Parser, xph_char);
508 XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
510 if ( ! XML_Parse(Parser, document.c_str(), document.size(), 1) )
512 XML_ParserFree(Parser);
513 DefaultLogSink().Error("XML Parse error on line %d: %s\n",
514 XML_GetCurrentLineNumber(Parser),
515 XML_ErrorString(XML_GetErrorCode(Parser)));
519 XML_ParserFree(Parser);
521 if ( ! Ctx.Namespaces->empty() )
522 m_NamespaceOwner = (void*)Ctx.Namespaces;
527 //------------------------------------------------------------------------------------------
529 struct xph_test_wrapper
534 xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
537 // expat wrapper functions, map callbacks to IASAXHandler
540 xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
543 xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
545 Wrapper->Status = true;
546 XML_StopParser(Wrapper->Parser, false);
552 Kumu::StringIsXML(const char* document, ui32_t len)
558 len = strlen(document);
560 XML_Parser Parser = XML_ParserCreate("UTF-8");
564 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
568 xph_test_wrapper Wrapper(Parser);
569 XML_SetUserData(Parser, (void*)&Wrapper);
570 XML_SetStartElementHandler(Parser, xph_test_start);
572 XML_Parse(Parser, document, len, 1);
573 XML_ParserFree(Parser);
574 return Wrapper.Status;
579 //----------------------------------------------------------------------------------------------------
583 static Mutex sg_Lock;
584 static bool sg_xml_init = false;
593 AutoMutex AL(sg_Lock);
599 XMLPlatformUtils::Initialize();
602 catch (const XMLException &e)
604 DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
612 class MyTreeHandler : public HandlerBase
614 ns_map* m_Namespaces;
615 std::stack<XMLElement*> m_Scope;
619 MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root) {
621 m_Namespaces = new ns_map;
628 ns_map* TakeNamespaceMap() {
629 if ( m_Namespaces == 0 || m_Namespaces->empty() )
632 ns_map* ret = m_Namespaces;
638 void AddNamespace(const char* ns_prefix, const char* ns_name)
643 if ( ns_prefix[0] == ':' )
649 assert(ns_prefix[0] == 0);
653 ns_map::iterator ni = m_Namespaces->find(ns_name);
655 if ( ni != m_Namespaces->end() )
657 if ( ni->second->Name() != std::string(ns_name) )
659 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
665 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
666 m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
669 assert(!m_Namespaces->empty());
673 void startElement(const XMLCh* const x_name,
674 XERCES_CPP_NAMESPACE::AttributeList& attributes)
678 const char* tx_name = XMLString::transcode(x_name);
679 const char* name = tx_name;
681 const char* ns_root = name;
682 const char* local_name = strchr(name, ':');
684 if ( local_name != 0 )
685 name = local_name + 1;
687 if ( m_Scope.empty() )
689 m_Scope.push(m_Root);
693 Element = m_Scope.top();
694 m_Scope.push(Element->AddChild(name));
697 Element = m_Scope.top();
698 Element->SetName(name);
701 ui32_t a_len = attributes.getLength();
703 for ( ui32_t i = 0; i < a_len; i++)
705 const XMLCh* aname = attributes.getName(i);
706 const XMLCh* value = attributes.getValue(i);
710 char* x_aname = XMLString::transcode(aname);
711 char* x_value = XMLString::transcode(value);
713 if ( strncmp(x_aname, "xmlns", 5) == 0 )
714 AddNamespace(x_aname+5, x_value);
716 if ( ( local_name = strchr(x_aname, ':') ) == 0 )
717 local_name = x_aname;
721 Element->SetAttr(local_name, x_value);
723 XMLString::release(&x_aname);
724 XMLString::release(&x_value);
729 if ( ns_root != name )
730 key.assign(ns_root, name - ns_root - 1);
732 ns_map::iterator ni = m_Namespaces->find(key);
733 if ( ni != m_Namespaces->end() )
734 Element->SetNamespace(ni->second);
736 XMLString::release((char**)&tx_name);
739 void endElement(const XMLCh *const name) {
743 void characters(const XMLCh *const chars, const unsigned int length)
747 char* text = XMLString::transcode(chars);
748 m_Scope.top()->AppendBody(text);
749 XMLString::release(&text);
756 Kumu::XMLElement::ParseString(const std::string& document)
758 if ( document.empty() )
761 asdcp_init_xml_dom();
764 SAXParser* parser = new SAXParser();
766 // #if XERCES_VERSION_MAJOR < 3
767 // parser->setDoValidation(true);
769 parser->setValidationScheme(SAXParser::Val_Always);
772 parser->setDoNamespaces(true); // optional
774 MyTreeHandler* docHandler = new MyTreeHandler(this);
775 parser->setDocumentHandler(docHandler);
776 parser->setErrorHandler(docHandler);
780 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document.c_str()),
781 static_cast<const unsigned int>(document.size()),
784 parser->parse(xmlSource);
786 catch (const XMLException& e)
788 char* message = XMLString::transcode(e.getMessage());
789 DefaultLogSink().Error("Parser error: %s\n", message);
790 XMLString::release(&message);
793 catch (const SAXParseException& e)
795 char* message = XMLString::transcode(e.getMessage());
796 DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
797 XMLString::release(&message);
802 DefaultLogSink().Error("Unexpected XML parser error\n");
806 if ( errorCount == 0 )
807 m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
812 return errorCount > 0 ? false : true;
817 Kumu::StringIsXML(const char* document, ui32_t len)
819 if ( document == 0 || *document == 0 )
822 asdcp_init_xml_dom();
825 len = strlen(document);
833 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
834 static_cast<const unsigned int>(len),
837 if ( parser.parseFirst(xmlSource, token) )
839 if ( parser.parseNext(token) )
853 //----------------------------------------------------------------------------------------------------
855 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
859 Kumu::XMLElement::ParseString(const std::string& document)
861 DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
867 Kumu::StringIsXML(const char* document, ui32_t len)
869 DefaultLogSink().Error("Kumu compiled without XML parser support.\n");