XML.cpp revision 341825
1285101Semaste//===-- XML.cpp -------------------------------------------------*- C++ -*-===// 2285101Semaste// 3285101Semaste// The LLVM Compiler Infrastructure 4285101Semaste// 5285101Semaste// This file is distributed under the University of Illinois Open Source 6285101Semaste// License. See LICENSE.TXT for details. 7285101Semaste// 8285101Semaste//===----------------------------------------------------------------------===// 9285101Semaste 10314564Sdim#include <stdlib.h> /* atof */ 11285101Semaste 12314564Sdim#include "lldb/Host/StringConvert.h" 13285101Semaste#include "lldb/Host/XML.h" 14285101Semaste 15285101Semasteusing namespace lldb; 16285101Semasteusing namespace lldb_private; 17285101Semaste 18314564Sdim#pragma mark-- XMLDocument 19285101Semaste 20314564SdimXMLDocument::XMLDocument() : m_document(nullptr) {} 21285101Semaste 22314564SdimXMLDocument::~XMLDocument() { Clear(); } 23285101Semaste 24314564Sdimvoid XMLDocument::Clear() { 25314564Sdim#if defined(LIBXML2_DEFINED) 26314564Sdim if (m_document) { 27314564Sdim xmlDocPtr doc = m_document; 28314564Sdim m_document = nullptr; 29314564Sdim xmlFreeDoc(doc); 30314564Sdim } 31285101Semaste#endif 32285101Semaste} 33285101Semaste 34314564Sdimbool XMLDocument::IsValid() const { return m_document != nullptr; } 35285101Semaste 36314564Sdimvoid XMLDocument::ErrorCallback(void *ctx, const char *format, ...) { 37314564Sdim XMLDocument *document = (XMLDocument *)ctx; 38314564Sdim va_list args; 39314564Sdim va_start(args, format); 40314564Sdim document->m_errors.PrintfVarArg(format, args); 41314564Sdim document->m_errors.EOL(); 42314564Sdim va_end(args); 43285101Semaste} 44285101Semaste 45314564Sdimbool XMLDocument::ParseFile(const char *path) { 46314564Sdim#if defined(LIBXML2_DEFINED) 47314564Sdim Clear(); 48314564Sdim xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 49314564Sdim m_document = xmlParseFile(path); 50314564Sdim xmlSetGenericErrorFunc(nullptr, nullptr); 51285101Semaste#endif 52314564Sdim return IsValid(); 53285101Semaste} 54285101Semaste 55314564Sdimbool XMLDocument::ParseMemory(const char *xml, size_t xml_length, 56314564Sdim const char *url) { 57314564Sdim#if defined(LIBXML2_DEFINED) 58314564Sdim Clear(); 59314564Sdim xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback); 60314564Sdim m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); 61314564Sdim xmlSetGenericErrorFunc(nullptr, nullptr); 62285101Semaste#endif 63314564Sdim return IsValid(); 64285101Semaste} 65285101Semaste 66314564SdimXMLNode XMLDocument::GetRootElement(const char *required_name) { 67314564Sdim#if defined(LIBXML2_DEFINED) 68314564Sdim if (IsValid()) { 69314564Sdim XMLNode root_node(xmlDocGetRootElement(m_document)); 70314564Sdim if (required_name) { 71314564Sdim llvm::StringRef actual_name = root_node.GetName(); 72314564Sdim if (actual_name == required_name) 73314564Sdim return root_node; 74314564Sdim } else { 75314564Sdim return root_node; 76285101Semaste } 77314564Sdim } 78285101Semaste#endif 79314564Sdim return XMLNode(); 80285101Semaste} 81285101Semaste 82314564Sdimllvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); } 83285101Semaste 84314564Sdimbool XMLDocument::XMLEnabled() { 85314564Sdim#if defined(LIBXML2_DEFINED) 86314564Sdim return true; 87285101Semaste#else 88314564Sdim return false; 89285101Semaste#endif 90285101Semaste} 91285101Semaste 92314564Sdim#pragma mark-- XMLNode 93285101Semaste 94314564SdimXMLNode::XMLNode() : m_node(nullptr) {} 95285101Semaste 96314564SdimXMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {} 97285101Semaste 98314564SdimXMLNode::~XMLNode() {} 99285101Semaste 100314564Sdimvoid XMLNode::Clear() { m_node = nullptr; } 101285101Semaste 102314564SdimXMLNode XMLNode::GetParent() const { 103314564Sdim#if defined(LIBXML2_DEFINED) 104314564Sdim if (IsValid()) 105314564Sdim return XMLNode(m_node->parent); 106314564Sdim else 107314564Sdim return XMLNode(); 108285101Semaste#else 109314564Sdim return XMLNode(); 110285101Semaste#endif 111285101Semaste} 112285101Semaste 113314564SdimXMLNode XMLNode::GetSibling() const { 114314564Sdim#if defined(LIBXML2_DEFINED) 115314564Sdim if (IsValid()) 116314564Sdim return XMLNode(m_node->next); 117314564Sdim else 118314564Sdim return XMLNode(); 119285101Semaste#else 120314564Sdim return XMLNode(); 121285101Semaste#endif 122285101Semaste} 123285101Semaste 124314564SdimXMLNode XMLNode::GetChild() const { 125314564Sdim#if defined(LIBXML2_DEFINED) 126285101Semaste 127314564Sdim if (IsValid()) 128314564Sdim return XMLNode(m_node->children); 129314564Sdim else 130314564Sdim return XMLNode(); 131285101Semaste#else 132314564Sdim return XMLNode(); 133285101Semaste#endif 134285101Semaste} 135285101Semaste 136314564Sdimllvm::StringRef XMLNode::GetAttributeValue(const char *name, 137314564Sdim const char *fail_value) const { 138314564Sdim const char *attr_value = NULL; 139314564Sdim#if defined(LIBXML2_DEFINED) 140285101Semaste 141314564Sdim if (IsValid()) 142314564Sdim attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); 143314564Sdim else 144314564Sdim attr_value = fail_value; 145285101Semaste#else 146314564Sdim attr_value = fail_value; 147285101Semaste#endif 148314564Sdim if (attr_value) 149314564Sdim return llvm::StringRef(attr_value); 150314564Sdim else 151314564Sdim return llvm::StringRef(); 152285101Semaste} 153285101Semaste 154341825Sdimbool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value, 155341825Sdim uint64_t fail_value, int base) const { 156341825Sdim#if defined(LIBXML2_DEFINED) 157341825Sdim llvm::StringRef str_value = GetAttributeValue(name, ""); 158341825Sdim#else 159341825Sdim llvm::StringRef str_value; 160341825Sdim#endif 161341825Sdim bool success = false; 162341825Sdim value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success); 163341825Sdim return success; 164341825Sdim} 165341825Sdim 166314564Sdimvoid XMLNode::ForEachChildNode(NodeCallback const &callback) const { 167314564Sdim#if defined(LIBXML2_DEFINED) 168314564Sdim if (IsValid()) 169314564Sdim GetChild().ForEachSiblingNode(callback); 170285101Semaste#endif 171285101Semaste} 172285101Semaste 173314564Sdimvoid XMLNode::ForEachChildElement(NodeCallback const &callback) const { 174314564Sdim#if defined(LIBXML2_DEFINED) 175314564Sdim XMLNode child = GetChild(); 176314564Sdim if (child) 177314564Sdim child.ForEachSiblingElement(callback); 178285101Semaste#endif 179285101Semaste} 180285101Semaste 181314564Sdimvoid XMLNode::ForEachChildElementWithName(const char *name, 182314564Sdim NodeCallback const &callback) const { 183314564Sdim#if defined(LIBXML2_DEFINED) 184314564Sdim XMLNode child = GetChild(); 185314564Sdim if (child) 186314564Sdim child.ForEachSiblingElementWithName(name, callback); 187285101Semaste#endif 188285101Semaste} 189285101Semaste 190314564Sdimvoid XMLNode::ForEachAttribute(AttributeCallback const &callback) const { 191314564Sdim#if defined(LIBXML2_DEFINED) 192285101Semaste 193314564Sdim if (IsValid()) { 194314564Sdim for (xmlAttrPtr attr = m_node->properties; attr != nullptr; 195314564Sdim attr = attr->next) { 196314564Sdim // check if name matches 197314564Sdim if (attr->name) { 198314564Sdim // check child is a text node 199314564Sdim xmlNodePtr child = attr->children; 200314564Sdim if (child->type == XML_TEXT_NODE) { 201314564Sdim llvm::StringRef attr_value; 202314564Sdim if (child->content) 203314564Sdim attr_value = llvm::StringRef((const char *)child->content); 204314564Sdim if (callback(llvm::StringRef((const char *)attr->name), attr_value) == 205314564Sdim false) 206314564Sdim return; 207285101Semaste } 208314564Sdim } 209285101Semaste } 210314564Sdim } 211285101Semaste#endif 212285101Semaste} 213285101Semaste 214314564Sdimvoid XMLNode::ForEachSiblingNode(NodeCallback const &callback) const { 215314564Sdim#if defined(LIBXML2_DEFINED) 216285101Semaste 217314564Sdim if (IsValid()) { 218314564Sdim // iterate through all siblings 219314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 220314564Sdim if (callback(XMLNode(node)) == false) 221314564Sdim return; 222285101Semaste } 223314564Sdim } 224285101Semaste#endif 225285101Semaste} 226285101Semaste 227314564Sdimvoid XMLNode::ForEachSiblingElement(NodeCallback const &callback) const { 228314564Sdim#if defined(LIBXML2_DEFINED) 229314564Sdim 230314564Sdim if (IsValid()) { 231314564Sdim // iterate through all siblings 232314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 233314564Sdim // we are looking for element nodes only 234314564Sdim if (node->type != XML_ELEMENT_NODE) 235314564Sdim continue; 236314564Sdim 237314564Sdim if (callback(XMLNode(node)) == false) 238314564Sdim return; 239285101Semaste } 240314564Sdim } 241285101Semaste#endif 242285101Semaste} 243285101Semaste 244314564Sdimvoid XMLNode::ForEachSiblingElementWithName( 245314564Sdim const char *name, NodeCallback const &callback) const { 246314564Sdim#if defined(LIBXML2_DEFINED) 247314564Sdim 248314564Sdim if (IsValid()) { 249314564Sdim // iterate through all siblings 250314564Sdim for (xmlNodePtr node = m_node; node; node = node->next) { 251314564Sdim // we are looking for element nodes only 252314564Sdim if (node->type != XML_ELEMENT_NODE) 253314564Sdim continue; 254314564Sdim 255341825Sdim // If name is nullptr, we take all nodes of type "t", else just the ones 256341825Sdim // whose name matches 257314564Sdim if (name) { 258314564Sdim if (strcmp((const char *)node->name, name) != 0) 259314564Sdim continue; // Name mismatch, ignore this one 260314564Sdim } else { 261314564Sdim if (node->name) 262314564Sdim continue; // nullptr name specified and this element has a name, 263314564Sdim // ignore this one 264314564Sdim } 265314564Sdim 266314564Sdim if (callback(XMLNode(node)) == false) 267314564Sdim return; 268285101Semaste } 269314564Sdim } 270285101Semaste#endif 271285101Semaste} 272285101Semaste 273314564Sdimllvm::StringRef XMLNode::GetName() const { 274314564Sdim#if defined(LIBXML2_DEFINED) 275314564Sdim if (IsValid()) { 276314564Sdim if (m_node->name) 277314564Sdim return llvm::StringRef((const char *)m_node->name); 278314564Sdim } 279285101Semaste#endif 280314564Sdim return llvm::StringRef(); 281285101Semaste} 282285101Semaste 283314564Sdimbool XMLNode::GetElementText(std::string &text) const { 284314564Sdim text.clear(); 285314564Sdim#if defined(LIBXML2_DEFINED) 286314564Sdim if (IsValid()) { 287314564Sdim bool success = false; 288314564Sdim if (m_node->type == XML_ELEMENT_NODE) { 289314564Sdim // check child is a text node 290314564Sdim for (xmlNodePtr node = m_node->children; node != nullptr; 291314564Sdim node = node->next) { 292314564Sdim if (node->type == XML_TEXT_NODE) { 293314564Sdim text.append((const char *)node->content); 294314564Sdim success = true; 295285101Semaste } 296314564Sdim } 297285101Semaste } 298314564Sdim return success; 299314564Sdim } 300285101Semaste#endif 301314564Sdim return false; 302285101Semaste} 303285101Semaste 304314564Sdimbool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value, 305314564Sdim int base) const { 306314564Sdim bool success = false; 307314564Sdim#if defined(LIBXML2_DEFINED) 308314564Sdim if (IsValid()) { 309314564Sdim std::string text; 310314564Sdim if (GetElementText(text)) 311314564Sdim value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); 312314564Sdim } 313285101Semaste#endif 314314564Sdim if (!success) 315314564Sdim value = fail_value; 316314564Sdim return success; 317285101Semaste} 318285101Semaste 319314564Sdimbool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const { 320314564Sdim bool success = false; 321314564Sdim#if defined(LIBXML2_DEFINED) 322314564Sdim if (IsValid()) { 323314564Sdim std::string text; 324314564Sdim if (GetElementText(text)) { 325314564Sdim value = atof(text.c_str()); 326314564Sdim success = true; 327285101Semaste } 328314564Sdim } 329285101Semaste#endif 330314564Sdim if (!success) 331314564Sdim value = fail_value; 332314564Sdim return success; 333285101Semaste} 334285101Semaste 335314564Sdimbool XMLNode::NameIs(const char *name) const { 336314564Sdim#if defined(LIBXML2_DEFINED) 337285101Semaste 338314564Sdim if (IsValid()) { 339314564Sdim // In case we are looking for a nullptr name or an exact pointer match 340314564Sdim if (m_node->name == (const xmlChar *)name) 341314564Sdim return true; 342314564Sdim if (m_node->name) 343314564Sdim return strcmp((const char *)m_node->name, name) == 0; 344314564Sdim } 345285101Semaste#endif 346314564Sdim return false; 347285101Semaste} 348285101Semaste 349314564SdimXMLNode XMLNode::FindFirstChildElementWithName(const char *name) const { 350314564Sdim XMLNode result_node; 351285101Semaste 352314564Sdim#if defined(LIBXML2_DEFINED) 353314564Sdim ForEachChildElementWithName( 354327952Sdim name, [&result_node](const XMLNode &node) -> bool { 355285101Semaste result_node = node; 356285101Semaste // Stop iterating, we found the node we wanted 357285101Semaste return false; 358314564Sdim }); 359285101Semaste#endif 360285101Semaste 361314564Sdim return result_node; 362285101Semaste} 363285101Semaste 364314564Sdimbool XMLNode::IsValid() const { return m_node != nullptr; } 365285101Semaste 366314564Sdimbool XMLNode::IsElement() const { 367314564Sdim#if defined(LIBXML2_DEFINED) 368314564Sdim if (IsValid()) 369314564Sdim return m_node->type == XML_ELEMENT_NODE; 370285101Semaste#endif 371314564Sdim return false; 372285101Semaste} 373285101Semaste 374314564SdimXMLNode XMLNode::GetElementForPath(const NamePath &path) { 375314564Sdim#if defined(LIBXML2_DEFINED) 376285101Semaste 377314564Sdim if (IsValid()) { 378314564Sdim if (path.empty()) 379314564Sdim return *this; 380314564Sdim else { 381314564Sdim XMLNode node = FindFirstChildElementWithName(path[0].c_str()); 382314564Sdim const size_t n = path.size(); 383314564Sdim for (size_t i = 1; node && i < n; ++i) 384314564Sdim node = node.FindFirstChildElementWithName(path[i].c_str()); 385314564Sdim return node; 386285101Semaste } 387314564Sdim } 388285101Semaste#endif 389285101Semaste 390314564Sdim return XMLNode(); 391285101Semaste} 392285101Semaste 393314564Sdim#pragma mark-- ApplePropertyList 394285101Semaste 395314564SdimApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {} 396285101Semaste 397314564SdimApplePropertyList::ApplePropertyList(const char *path) 398314564Sdim : m_xml_doc(), m_dict_node() { 399314564Sdim ParseFile(path); 400285101Semaste} 401285101Semaste 402314564SdimApplePropertyList::~ApplePropertyList() {} 403285101Semaste 404314564Sdimllvm::StringRef ApplePropertyList::GetErrors() const { 405314564Sdim return m_xml_doc.GetErrors(); 406285101Semaste} 407285101Semaste 408314564Sdimbool ApplePropertyList::ParseFile(const char *path) { 409314564Sdim if (m_xml_doc.ParseFile(path)) { 410314564Sdim XMLNode plist = m_xml_doc.GetRootElement("plist"); 411314564Sdim if (plist) { 412314564Sdim plist.ForEachChildElementWithName("dict", 413314564Sdim [this](const XMLNode &dict) -> bool { 414314564Sdim this->m_dict_node = dict; 415314564Sdim return false; // Stop iterating 416314564Sdim }); 417314564Sdim return (bool)m_dict_node; 418314564Sdim } 419314564Sdim } 420314564Sdim return false; 421285101Semaste} 422285101Semaste 423314564Sdimbool ApplePropertyList::IsValid() const { return (bool)m_dict_node; } 424285101Semaste 425314564Sdimbool ApplePropertyList::GetValueAsString(const char *key, 426314564Sdim std::string &value) const { 427314564Sdim XMLNode value_node = GetValueNode(key); 428314564Sdim if (value_node) 429314564Sdim return ApplePropertyList::ExtractStringFromValueNode(value_node, value); 430314564Sdim return false; 431285101Semaste} 432285101Semaste 433314564SdimXMLNode ApplePropertyList::GetValueNode(const char *key) const { 434314564Sdim XMLNode value_node; 435314564Sdim#if defined(LIBXML2_DEFINED) 436285101Semaste 437314564Sdim if (IsValid()) { 438314564Sdim m_dict_node.ForEachChildElementWithName( 439314564Sdim "key", [key, &value_node](const XMLNode &key_node) -> bool { 440314564Sdim std::string key_name; 441314564Sdim if (key_node.GetElementText(key_name)) { 442314564Sdim if (key_name.compare(key) == 0) { 443314564Sdim value_node = key_node.GetSibling(); 444314564Sdim while (value_node && !value_node.IsElement()) 445314564Sdim value_node = value_node.GetSibling(); 446314564Sdim return false; // Stop iterating 447285101Semaste } 448314564Sdim } 449314564Sdim return true; // Keep iterating 450285101Semaste }); 451314564Sdim } 452285101Semaste#endif 453314564Sdim return value_node; 454285101Semaste} 455285101Semaste 456314564Sdimbool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node, 457314564Sdim std::string &value) { 458314564Sdim value.clear(); 459314564Sdim#if defined(LIBXML2_DEFINED) 460314564Sdim if (node.IsValid()) { 461314564Sdim llvm::StringRef element_name = node.GetName(); 462314564Sdim if (element_name == "true" || element_name == "false") { 463314564Sdim // The text value _is_ the element name itself... 464314564Sdim value = element_name.str(); 465314564Sdim return true; 466314564Sdim } else if (element_name == "dict" || element_name == "array") 467314564Sdim return false; // dictionaries and arrays have no text value, so we fail 468314564Sdim else 469314564Sdim return node.GetElementText(value); 470314564Sdim } 471285101Semaste#endif 472314564Sdim return false; 473285101Semaste} 474285101Semaste 475314564Sdim#if defined(LIBXML2_DEFINED) 476285101Semaste 477285101Semastenamespace { 478285101Semaste 479314564SdimStructuredData::ObjectSP CreatePlistValue(XMLNode node) { 480314564Sdim llvm::StringRef element_name = node.GetName(); 481314564Sdim if (element_name == "array") { 482314564Sdim std::shared_ptr<StructuredData::Array> array_sp( 483314564Sdim new StructuredData::Array()); 484314564Sdim node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { 485314564Sdim array_sp->AddItem(CreatePlistValue(node)); 486314564Sdim return true; // Keep iterating through all child elements of the array 487314564Sdim }); 488314564Sdim return array_sp; 489314564Sdim } else if (element_name == "dict") { 490314564Sdim XMLNode key_node; 491314564Sdim std::shared_ptr<StructuredData::Dictionary> dict_sp( 492314564Sdim new StructuredData::Dictionary()); 493314564Sdim node.ForEachChildElement( 494314564Sdim [&key_node, &dict_sp](const XMLNode &node) -> bool { 495314564Sdim if (node.NameIs("key")) { 496314564Sdim // This is a "key" element node 497314564Sdim key_node = node; 498314564Sdim } else { 499314564Sdim // This is a value node 500314564Sdim if (key_node) { 501314564Sdim std::string key_name; 502314564Sdim key_node.GetElementText(key_name); 503314564Sdim dict_sp->AddItem(key_name, CreatePlistValue(node)); 504314564Sdim key_node.Clear(); 505314564Sdim } 506314564Sdim } 507314564Sdim return true; // Keep iterating through all child elements of the 508314564Sdim // dictionary 509314564Sdim }); 510314564Sdim return dict_sp; 511314564Sdim } else if (element_name == "real") { 512314564Sdim double value = 0.0; 513314564Sdim node.GetElementTextAsFloat(value); 514314564Sdim return StructuredData::ObjectSP(new StructuredData::Float(value)); 515314564Sdim } else if (element_name == "integer") { 516314564Sdim uint64_t value = 0; 517314564Sdim node.GetElementTextAsUnsigned(value, 0, 0); 518314564Sdim return StructuredData::ObjectSP(new StructuredData::Integer(value)); 519314564Sdim } else if ((element_name == "string") || (element_name == "data") || 520314564Sdim (element_name == "date")) { 521314564Sdim std::string text; 522314564Sdim node.GetElementText(text); 523314564Sdim return StructuredData::ObjectSP( 524314564Sdim new StructuredData::String(std::move(text))); 525314564Sdim } else if (element_name == "true") { 526314564Sdim return StructuredData::ObjectSP(new StructuredData::Boolean(true)); 527314564Sdim } else if (element_name == "false") { 528314564Sdim return StructuredData::ObjectSP(new StructuredData::Boolean(false)); 529314564Sdim } 530314564Sdim return StructuredData::ObjectSP(new StructuredData::Null()); 531285101Semaste} 532314564Sdim} 533285101Semaste#endif 534285101Semaste 535314564SdimStructuredData::ObjectSP ApplePropertyList::GetStructuredData() { 536314564Sdim StructuredData::ObjectSP root_sp; 537314564Sdim#if defined(LIBXML2_DEFINED) 538314564Sdim if (IsValid()) { 539314564Sdim return CreatePlistValue(m_dict_node); 540314564Sdim } 541285101Semaste#endif 542314564Sdim return root_sp; 543285101Semaste} 544