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