2 Copyright (c) 2005-2008, 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::AddChildWithContent(const char* name, const char* value)
146 XMLElement* tmpE = new XMLElement(name);
147 tmpE->m_Body = value;
148 m_ChildList.push_back(tmpE);
154 Kumu::XMLElement::AddChildWithPrefixedContent(const char* name, const char* prefix, const char* value)
156 XMLElement* tmpE = new XMLElement(name);
157 tmpE->m_Body = prefix;
158 tmpE->m_Body += value;
159 m_ChildList.push_back(tmpE);
165 Kumu::XMLElement::AddComment(const char* value)
174 Kumu::XMLElement::Render(std::string& outbuf) const
176 outbuf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
177 RenderElement(outbuf, 0);
182 add_spacer(std::string& outbuf, i32_t depth)
190 Kumu::XMLElement::RenderElement(std::string& outbuf, ui32_t depth) const
192 add_spacer(outbuf, depth);
198 for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
203 outbuf += (*i).value;
209 // body contents and children
210 if ( ! m_ChildList.empty() )
215 if ( m_Body.length() > 0 )
218 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
219 (*i)->RenderElement(outbuf, depth + 1);
221 add_spacer(outbuf, depth);
223 else if ( m_Body.length() > 0 )
235 Kumu::XMLElement::HasName(const char* name) const
237 if ( name == 0 || *name == 0 )
240 return (m_Name == name);
245 Kumu::XMLElement::SetName(const char* name)
253 Kumu::XMLElement::GetAttrWithName(const char* name) const
255 for ( Attr_i i = m_AttrList.begin(); i != m_AttrList.end(); i++ )
257 if ( (*i).name == name )
258 return (*i).value.c_str();
266 Kumu::XMLElement::GetChildWithName(const char* name) const
268 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
270 if ( (*i)->HasName(name) )
278 const Kumu::ElementList&
279 Kumu::XMLElement::GetChildrenWithName(const char* name, ElementList& outList) const
282 for ( Elem_i i = m_ChildList.begin(); i != m_ChildList.end(); i++ )
284 if ( (*i)->HasName(name) )
285 outList.push_back(*i);
287 if ( ! (*i)->m_ChildList.empty() )
288 (*i)->GetChildrenWithName(name, outList);
294 //----------------------------------------------------------------------------------------------------
299 class ExpatParseContext
301 KM_NO_COPY_CONSTRUCT(ExpatParseContext);
305 std::stack<XMLElement*> Scope;
308 ExpatParseContext(XMLElement* root) : Root(root) {
309 Namespaces = new ns_map;
313 ~ExpatParseContext() {}
316 // expat wrapper functions
319 xph_start(void* p, const XML_Char* name, const XML_Char** attrs)
321 assert(p); assert(name); assert(attrs);
322 ExpatParseContext* Ctx = (ExpatParseContext*)p;
325 const char* ns_root = name;
326 const char* local_name = strchr(name, '|');
327 if ( local_name != 0 )
328 name = local_name + 1;
330 if ( Ctx->Scope.empty() )
332 Ctx->Scope.push(Ctx->Root);
336 Element = Ctx->Scope.top();
337 Ctx->Scope.push(Element->AddChild(name));
340 Element = Ctx->Scope.top();
341 Element->SetName(name);
345 if ( ns_root != name )
346 key.assign(ns_root, name - ns_root - 1);
348 ns_map::iterator ni = Ctx->Namespaces->find(key);
349 if ( ni != Ctx->Namespaces->end() )
350 Element->SetNamespace(ni->second);
353 for ( int i = 0; attrs[i] != 0; i += 2 )
355 if ( ( local_name = strchr(attrs[i], '|') ) == 0 )
356 local_name = attrs[i];
360 Element->SetAttr(local_name, attrs[i+1]);
366 xph_end(void* p, const XML_Char* name)
368 assert(p); assert(name);
369 ExpatParseContext* Ctx = (ExpatParseContext*)p;
375 xph_char(void* p, const XML_Char* data, int len)
377 assert(p); assert(data);
378 ExpatParseContext* Ctx = (ExpatParseContext*)p;
383 tmp_str.assign(data, len);
384 Ctx->Scope.top()->AppendBody(tmp_str);
390 xph_namespace_start(void* p, const XML_Char* ns_prefix, const XML_Char* ns_name)
392 assert(p); assert(ns_name);
393 ExpatParseContext* Ctx = (ExpatParseContext*)p;
395 if ( ns_prefix == 0 )
398 ns_map::iterator ni = Ctx->Namespaces->find(ns_name);
400 if ( ni != Ctx->Namespaces->end() )
402 if ( ni->second->Name() != std::string(ns_name) )
404 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
410 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
411 Ctx->Namespaces->insert(ns_map::value_type(ns_name, Namespace));
417 Kumu::XMLElement::ParseString(const std::string& document)
419 XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
423 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
427 ExpatParseContext Ctx(this);
428 XML_SetUserData(Parser, (void*)&Ctx);
429 XML_SetElementHandler(Parser, xph_start, xph_end);
430 XML_SetCharacterDataHandler(Parser, xph_char);
431 XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
433 if ( ! XML_Parse(Parser, document.c_str(), document.size(), 1) )
435 XML_ParserFree(Parser);
436 DefaultLogSink().Error("XML Parse error on line %d: %s\n",
437 XML_GetCurrentLineNumber(Parser),
438 XML_ErrorString(XML_GetErrorCode(Parser)));
442 XML_ParserFree(Parser);
444 if ( ! Ctx.Namespaces->empty() )
445 m_NamespaceOwner = (void*)Ctx.Namespaces;
450 //------------------------------------------------------------------------------------------
452 struct xph_test_wrapper
457 xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
460 // expat wrapper functions, map callbacks to IASAXHandler
463 xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
466 xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
468 Wrapper->Status = true;
469 XML_StopParser(Wrapper->Parser, false);
475 Kumu::StringIsXML(const char* document, ui32_t len)
481 len = strlen(document);
483 XML_Parser Parser = XML_ParserCreate("UTF-8");
487 DefaultLogSink().Error("Error allocating memory for XML parser.\n");
491 xph_test_wrapper Wrapper(Parser);
492 XML_SetUserData(Parser, (void*)&Wrapper);
493 XML_SetStartElementHandler(Parser, xph_test_start);
495 XML_Parse(Parser, document, len, 1);
496 XML_ParserFree(Parser);
497 return Wrapper.Status;
502 //----------------------------------------------------------------------------------------------------
506 static Mutex sg_Lock;
507 static bool sg_xml_init = false;
516 AutoMutex AL(sg_Lock);
522 XMLPlatformUtils::Initialize();
525 catch (const XMLException &e)
527 DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
535 class MyTreeHandler : public HandlerBase
537 ns_map* m_Namespaces;
538 std::stack<XMLElement*> m_Scope;
542 MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root) {
544 m_Namespaces = new ns_map;
551 ns_map* TakeNamespaceMap() {
552 if ( m_Namespaces == 0 || m_Namespaces->empty() )
555 ns_map* ret = m_Namespaces;
561 void AddNamespace(const char* ns_prefix, const char* ns_name)
566 if ( ns_prefix[0] == ':' )
572 assert(ns_prefix[0] == 0);
576 ns_map::iterator ni = m_Namespaces->find(ns_name);
578 if ( ni != m_Namespaces->end() )
580 if ( ni->second->Name() != std::string(ns_name) )
582 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
588 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
589 m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
592 assert(!m_Namespaces->empty());
596 void startElement(const XMLCh* const x_name,
597 XERCES_CPP_NAMESPACE::AttributeList& attributes)
601 const char* tx_name = XMLString::transcode(x_name);
602 const char* name = tx_name;
604 const char* ns_root = name;
605 const char* local_name = strchr(name, ':');
607 if ( local_name != 0 )
608 name = local_name + 1;
610 if ( m_Scope.empty() )
612 m_Scope.push(m_Root);
616 Element = m_Scope.top();
617 m_Scope.push(Element->AddChild(name));
620 Element = m_Scope.top();
621 Element->SetName(name);
624 ui32_t a_len = attributes.getLength();
626 for ( ui32_t i = 0; i < a_len; i++)
628 const XMLCh* aname = attributes.getName(i);
629 const XMLCh* value = attributes.getValue(i);
633 char* x_aname = XMLString::transcode(aname);
634 char* x_value = XMLString::transcode(value);
636 if ( strncmp(x_aname, "xmlns", 5) == 0 )
637 AddNamespace(x_aname+5, x_value);
639 if ( ( local_name = strchr(x_aname, ':') ) == 0 )
640 local_name = x_aname;
644 Element->SetAttr(local_name, x_value);
646 XMLString::release(&x_aname);
647 XMLString::release(&x_value);
652 if ( ns_root != name )
653 key.assign(ns_root, name - ns_root - 1);
655 ns_map::iterator ni = m_Namespaces->find(key);
656 if ( ni != m_Namespaces->end() )
657 Element->SetNamespace(ni->second);
659 XMLString::release((char**)&tx_name);
662 void endElement(const XMLCh *const name) {
666 void characters(const XMLCh *const chars, const unsigned int length)
670 char* text = XMLString::transcode(chars);
671 m_Scope.top()->AppendBody(text);
672 XMLString::release(&text);
679 Kumu::XMLElement::ParseString(const std::string& document)
681 if ( document.empty() )
684 asdcp_init_xml_dom();
687 SAXParser* parser = new SAXParser();
688 parser->setDoValidation(true);
689 parser->setDoNamespaces(true); // optional
691 MyTreeHandler* docHandler = new MyTreeHandler(this);
692 ErrorHandler* errHandler = (ErrorHandler*)docHandler;
693 parser->setDocumentHandler(docHandler);
697 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document.c_str()),
698 static_cast<const unsigned int>(document.size()),
701 parser->parse(xmlSource);
703 catch (const XMLException& e)
705 char* message = XMLString::transcode(e.getMessage());
706 DefaultLogSink().Error("Parser error: %s\n", message);
707 XMLString::release(&message);
710 catch (const SAXParseException& e)
712 char* message = XMLString::transcode(e.getMessage());
713 DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
714 XMLString::release(&message);
719 DefaultLogSink().Error("Unexpected XML parser error\n");
723 if ( errorCount == 0 )
724 m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
729 return errorCount > 0 ? false : true;
734 Kumu::StringIsXML(const char* document, ui32_t len)
736 if ( document == 0 || *document == 0 )
739 asdcp_init_xml_dom();
742 len = strlen(document);
750 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
751 static_cast<const unsigned int>(len),
754 if ( parser.parseFirst(xmlSource, token) )
756 if ( parser.parseNext(token) )
770 //----------------------------------------------------------------------------------------------------
772 #if ! defined(HAVE_EXPAT) && ! defined(HAVE_XERCES_C)
776 Kumu::XMLElement::ParseString(const std::string& document)
778 DefaultLogSink().Error("asdcplib compiled without XML parser support.\n");
784 Kumu::StringIsXML(const char* document, ui32_t len)
786 DefaultLogSink().Error("Kumu compiled without XML parser support.\n");