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();
524 catch (const XMLException &e)
526 DefaultLogSink().Error("Xerces initialization error: %s\n", e.getMessage());
537 class MyTreeHandler : public HandlerBase
539 ns_map* m_Namespaces;
540 std::stack<XMLElement*> m_Scope;
544 MyTreeHandler(XMLElement* root) : m_Namespaces(0), m_Root(root) {
546 m_Namespaces = new ns_map;
553 ns_map* TakeNamespaceMap() {
554 if ( m_Namespaces == 0 || m_Namespaces->empty() )
557 ns_map* ret = m_Namespaces;
563 void AddNamespace(const char* ns_prefix, const char* ns_name)
568 if ( ns_prefix[0] == ':' )
574 assert(ns_prefix[0] == 0);
578 ns_map::iterator ni = m_Namespaces->find(ns_name);
580 if ( ni != m_Namespaces->end() )
582 if ( ni->second->Name() != std::string(ns_name) )
584 DefaultLogSink().Error("Duplicate prefix: %s\n", ns_prefix);
590 XMLNamespace* Namespace = new XMLNamespace(ns_prefix, ns_name);
591 m_Namespaces->insert(ns_map::value_type(ns_prefix, Namespace));
594 assert(!m_Namespaces->empty());
598 void startElement(const XMLCh* const x_name,
599 XERCES_CPP_NAMESPACE::AttributeList& attributes)
603 const char* tx_name = XMLString::transcode(x_name);
604 const char* name = tx_name;
606 const char* ns_root = name;
607 const char* local_name = strchr(name, ':');
609 if ( local_name != 0 )
610 name = local_name + 1;
612 if ( m_Scope.empty() )
614 m_Scope.push(m_Root);
618 Element = m_Scope.top();
619 m_Scope.push(Element->AddChild(name));
622 Element = m_Scope.top();
623 Element->SetName(name);
626 ui32_t a_len = attributes.getLength();
628 for ( ui32_t i = 0; i < a_len; i++)
630 const XMLCh* aname = attributes.getName(i);
631 const XMLCh* value = attributes.getValue(i);
635 char* x_aname = XMLString::transcode(aname);
636 char* x_value = XMLString::transcode(value);
638 if ( strncmp(x_aname, "xmlns", 5) == 0 )
639 AddNamespace(x_aname+5, x_value);
641 if ( ( local_name = strchr(x_aname, ':') ) == 0 )
642 local_name = x_aname;
646 Element->SetAttr(local_name, x_value);
648 XMLString::release(&x_aname);
649 XMLString::release(&x_value);
654 if ( ns_root != name )
655 key.assign(ns_root, name - ns_root - 1);
657 ns_map::iterator ni = m_Namespaces->find(key);
658 if ( ni != m_Namespaces->end() )
659 Element->SetNamespace(ni->second);
661 XMLString::release((char**)&tx_name);
664 void endElement(const XMLCh *const name) {
668 void characters(const XMLCh *const chars, const unsigned int length)
672 char* text = XMLString::transcode(chars);
673 m_Scope.top()->AppendBody(text);
674 XMLString::release(&text);
681 Kumu::XMLElement::ParseString(const std::string& document)
683 if ( document.empty() )
689 SAXParser* parser = new SAXParser();
690 parser->setDoValidation(true);
691 parser->setDoNamespaces(true); // optional
693 MyTreeHandler* docHandler = new MyTreeHandler(this);
694 ErrorHandler* errHandler = (ErrorHandler*)docHandler;
695 parser->setDocumentHandler(docHandler);
699 MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document.c_str()),
700 static_cast<const unsigned int>(document.size()),
703 parser->parse(xmlSource);
705 catch (const XMLException& e)
707 char* message = XMLString::transcode(e.getMessage());
708 DefaultLogSink().Error("Parser error: %s\n", message);
709 XMLString::release(&message);
712 catch (const SAXParseException& e)
714 char* message = XMLString::transcode(e.getMessage());
715 DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
716 XMLString::release(&message);
721 DefaultLogSink().Error("Unexpected XML parser error\n");
725 m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
733 Kumu::StringIsXML(const char* document, ui32_t len)
735 if ( document == 0 || *document == 0 )
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");