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;
314 for ( i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
316 if ( i->name == std::string(name) )
323 Kumu::XMLElement::DeleteChildren()
325 for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
328 m_ChildList.erase(i);
334 Kumu::XMLElement::DeleteChild(const XMLElement* element)
338 for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
343 m_ChildList.erase(i);
352 Kumu::XMLElement::ForgetChild(const XMLElement* element)
356 for ( ElementList::iterator i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
360 m_ChildList.erase(i);
368 //----------------------------------------------------------------------------------------------------
373 class ExpatParseContext
375 KM_NO_COPY_CONSTRUCT(ExpatParseContext);
379 std::stack<XMLElement*> Scope;
382 ExpatParseContext(XMLElement* root) : Root(root) {
383 Namespaces = new ns_map;
387 ~ExpatParseContext() {}
390 // expat wrapper functions
393 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
395 assert(p); assert(name); assert(attrs);
396 ExpatParseContext* Ctx = (ExpatParseContext*)p;
399 const char* ns_root = name;
400 const char* local_name = strchr(name, '|');
401 if ( local_name != 0 )
402 name = local_name + 1;
404 if ( Ctx->Scope.empty() )
406 Ctx->Scope.push(Ctx->Root);
410 Element = Ctx->Scope.top();
411 Ctx->Scope.push(Element->AddChild(name));
414 Element = Ctx->Scope.top();
415 Element->SetName(name);
419 if ( ns_root != name )
420 key.assign(ns_root, name - ns_root - 1);
422 ns_map::iterator ni = Ctx->Namespaces->find(key);
423 if ( ni != Ctx->Namespaces->end() )
424 Element->SetNamespace(ni->second);
427 for ( int i = 0; attrs[i] != 0; i += 2 )
429 if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
430 local_name = attrs[i];
434 Element->SetAttr(local_name, attrs[i+1]);
440 xph_end(void* p, const XML_Char* name)
442 assert(p); assert(name);
443 ExpatParseContext* Ctx = (ExpatParseContext*)p;
449 xph_char(void* p, const XML_Char* data, int len)
451 assert(p); assert(data);
452 ExpatParseContext* Ctx = (ExpatParseContext*)p;
457 tmp_str.assign(data, len);
458 Ctx->Scope.top()->AppendBody(tmp_str);
464 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
466 assert(p); assert(ns_name);
467 ExpatParseContext* Ctx = (ExpatParseContext*)p;
469 if ( ns_prefix == 0 )
472 ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
474 if ( ni != Ctx->Namespaces->end() )
476 if ( ni->second->Name() != std::string(ns_name) )
478 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
484 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
485 Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
491 Kumu::XMLElement::ParseString(const std::string& document)
493 XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
497 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
501 ExpatParseContext Ctx(this);
502 XML_SetUserData(Parser, (void*)&Ctx);
503 XML_SetElementHandler(Parser, xph_start, xph_end);
504 XML_SetCharacterDataHandler(Parser, xph_char);
505 XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
507 if ( ! XML_Parse(Parser, document.c_str(), document.size(), 1) )
509 XML_ParserFree(Parser);
510 DefaultLogSink().Error("XML Parse error on line %d: %s\n",
511 XML_GetCurrentLineNumber(Parser),
512 XML_ErrorString(XML_GetErrorCode(Parser)));
516 XML_ParserFree(Parser);
518 if ( ! Ctx.Namespaces->empty() )
519 m_NamespaceOwner = (void*)Ctx.Namespaces;
524 //------------------------------------------------------------------------------------------
526 struct xph_test_wrapper
531 xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
534 // expat wrapper functions, map callbacks to IASAXHandler
537 xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
540 xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
542 Wrapper->Status = true;
543 XML_StopParser(Wrapper->Parser, false);
549 Kumu::StringIsXML(const char* document, ui32_t len)
555 len = strlen(document);
557 XML_Parser Parser = XML_ParserCreate("UTF-8");
561 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
565 xph_test_wrapper Wrapper(Parser);
566 XML_SetUserData(Parser, (void*)&Wrapper);
567 XML_SetStartElementHandler(Parser, xph_test_start);
569 XML_Parse(Parser, document, len, 1);
570 XML_ParserFree(Parser);
571 return Wrapper.Status;
576 //----------------------------------------------------------------------------------------------------
580 static Mutex sg_Lock;
581 static bool sg_xml_init = false;
590 AutoMutex AL(sg_Lock);
596 XMLPlatformUtils::Initialize();
599 catch (const XMLException &e)
601 DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
609 class MyTreeHandler : public HandlerBase
611 ns_map* m_Namespaces;
612 std::stack<XMLElement*> m_Scope;
616 MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root) {
618 m_Namespaces = new ns_map;
625 ns_map* TakeNamespaceMap() {
626 if ( m_Namespaces == 0 || m_Namespaces->empty() )
629 ns_map* ret = m_Namespaces;
635 void AddNamespace(const char* ns_prefix, const char* ns_name)
640 if ( ns_prefix[0] == ':' )
646 assert(ns_prefix[0] == 0);
650 ns_map::iterator ni = m_Namespaces->find(ns_name);
652 if ( ni != m_Namespaces->end() )
654 if ( ni->second->Name() != std::string(ns_name) )
656 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
662 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
663 m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
666 assert(!m_Namespaces->empty());
670 void startElement(const XMLCh* const x_name,
671 XERCES_CPP_NAMESPACE::AttributeList& attributes)
675 const char* tx_name = XMLString::transcode(x_name);
676 const char* name = tx_name;
678 const char* ns_root = name;
679 const char* local_name = strchr(name, ':');
681 if ( local_name != 0 )
682 name = local_name + 1;
684 if ( m_Scope.empty() )
686 m_Scope.push(m_Root);
690 Element = m_Scope.top();
691 m_Scope.push(Element->AddChild(name));
694 Element = m_Scope.top();
695 Element->SetName(name);
698 ui32_t a_len = attributes.getLength();
700 for ( ui32_t i = 0; i < a_len; i++)
702 const XMLCh* aname = attributes.getName(i);
703 const XMLCh* value = attributes.getValue(i);
707 char* x_aname = XMLString::transcode(aname);
708 char* x_value = XMLString::transcode(value);
710 if ( strncmp(x_aname, "xmlns", 5) == 0 )
711 AddNamespace(x_aname+5, x_value);
713 if ( ( local_name = strchr(x_aname, ':') ) == 0 )
714 local_name = x_aname;
718 Element->SetAttr(local_name, x_value);
720 XMLString::release(&x_aname);
721 XMLString::release(&x_value);
726 if ( ns_root != name )
727 key.assign(ns_root, name - ns_root - 1);
729 ns_map::iterator ni = m_Namespaces->find(key);
730 if ( ni != m_Namespaces->end() )
731 Element->SetNamespace(ni->second);
733 XMLString::release((char**)&tx_name);
736 void endElement(const XMLCh *const name) {
740 void characters(const XMLCh *const chars, const unsigned int length)
744 char* text = XMLString::transcode(chars);
745 m_Scope.top()->AppendBody(text);
746 XMLString::release(&text);
753 Kumu::XMLElement::ParseString(const std::string& document)
755 if ( document.empty() )
758 asdcp_init_xml_dom();
761 SAXParser* parser = new SAXParser();
762 parser->setDoValidation(true);
763 parser->setDoNamespaces(true); // optional
765 MyTreeHandler* docHandler = new MyTreeHandler(this);
766 ErrorHandler* errHandler = (ErrorHandler*)docHandler;
767 parser->setDocumentHandler(docHandler);
771 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document.c_str()),
772 static_cast<const unsigned int>(document.size()),
775 parser->parse(xmlSource);
777 catch (const XMLException& e)
779 char* message = XMLString::transcode(e.getMessage());
780 DefaultLogSink().Error("Parser error: %s\n", message);
781 XMLString::release(&message);
784 catch (const SAXParseException& e)
786 char* message = XMLString::transcode(e.getMessage());
787 DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
788 XMLString::release(&message);
793 DefaultLogSink().Error("Unexpected XML parser error\n");
797 if ( errorCount == 0 )
798 m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
803 return errorCount > 0 ? false : true;
808 Kumu::StringIsXML(const char* document, ui32_t len)
810 if ( document == 0 || *document == 0 )
813 asdcp_init_xml_dom();
816 len = strlen(document);
824 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
825 static_cast<const unsigned int>(len),
828 if ( parser.parseFirst(xmlSource, token) )
830 if ( parser.parseNext(token) )
844 //----------------------------------------------------------------------------------------------------
846 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
850 Kumu::XMLElement::ParseString(const std::string& document)
852 DefaultLogSink().Error("asdcplib compiled without XML parser support.\n");
858 Kumu::StringIsXML(const char* document, ui32_t len)
860 DefaultLogSink().Error("Kumu compiled without XML parser support.\n");