XML.cpp revision 285101
1//===-- XML.cpp -------------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <stdlib.h>     /* atof */
11
12#include "lldb/Host/XML.h"
13#include "lldb/Host/StringConvert.h"
14
15using namespace lldb;
16using namespace lldb_private;
17
18
19#pragma mark -- XMLDocument
20
21XMLDocument::XMLDocument () :
22    m_document (nullptr)
23{
24}
25
26XMLDocument::~XMLDocument ()
27{
28    Clear();
29}
30
31void
32XMLDocument::Clear()
33{
34#if defined( LIBXML2_DEFINED )
35    if (m_document)
36    {
37        xmlDocPtr doc = m_document;
38        m_document = nullptr;
39        xmlFreeDoc(doc);
40    }
41#endif
42}
43
44bool
45XMLDocument::IsValid() const
46{
47    return m_document != nullptr;
48}
49
50void
51XMLDocument::ErrorCallback (void *ctx, const char *format, ...)
52{
53    XMLDocument *document = (XMLDocument *)ctx;
54    va_list args;
55    va_start (args, format);
56    document->m_errors.PrintfVarArg(format, args);
57    document->m_errors.EOL();
58    va_end (args);
59}
60
61bool
62XMLDocument::ParseFile (const char *path)
63{
64#if defined( LIBXML2_DEFINED )
65    Clear();
66    xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback );
67    m_document = xmlParseFile(path);
68    xmlSetGenericErrorFunc(nullptr, nullptr);
69#endif
70    return IsValid();
71}
72
73bool
74XMLDocument::ParseMemory (const char *xml, size_t xml_length, const char *url)
75{
76#if defined( LIBXML2_DEFINED )
77    Clear();
78    xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback );
79    m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
80    xmlSetGenericErrorFunc(nullptr, nullptr);
81#endif
82    return IsValid();
83
84}
85
86XMLNode
87XMLDocument::GetRootElement(const char *required_name)
88{
89#if defined( LIBXML2_DEFINED )
90    if (IsValid())
91    {
92        XMLNode root_node(xmlDocGetRootElement(m_document));
93        if (required_name)
94        {
95            llvm::StringRef actual_name = root_node.GetName();
96            if (actual_name == required_name)
97                return root_node;
98        }
99        else
100        {
101            return root_node;
102        }
103    }
104#endif
105    return XMLNode();
106}
107
108const std::string &
109XMLDocument::GetErrors() const
110{
111    return m_errors.GetString();
112}
113
114bool
115XMLDocument::XMLEnabled ()
116{
117#if defined( LIBXML2_DEFINED )
118    return true;
119#else
120    return false;
121#endif
122}
123
124#pragma mark -- XMLNode
125
126XMLNode::XMLNode() :
127    m_node(nullptr)
128{
129}
130
131XMLNode::XMLNode(XMLNodeImpl node) :
132    m_node(node)
133{
134}
135
136XMLNode::~XMLNode()
137{
138
139}
140
141void
142XMLNode::Clear()
143{
144    m_node = nullptr;
145}
146
147XMLNode
148XMLNode::GetParent() const
149{
150#if defined( LIBXML2_DEFINED )
151    if (IsValid())
152        return XMLNode(m_node->parent);
153    else
154        return XMLNode();
155#else
156    return XMLNode();
157#endif
158
159}
160
161XMLNode
162XMLNode::GetSibling() const
163{
164#if defined( LIBXML2_DEFINED )
165    if (IsValid())
166        return XMLNode(m_node->next);
167    else
168        return XMLNode();
169#else
170    return XMLNode();
171#endif
172
173}
174
175XMLNode
176XMLNode::GetChild () const
177{
178#if defined( LIBXML2_DEFINED )
179
180    if (IsValid())
181        return XMLNode(m_node->children);
182    else
183        return XMLNode();
184#else
185    return XMLNode();
186#endif
187
188}
189
190llvm::StringRef
191XMLNode::GetAttributeValue(const char *name, const char *fail_value) const
192{
193    const char *attr_value = NULL;
194#if defined( LIBXML2_DEFINED )
195
196    if (IsValid())
197        attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name);
198    else
199        attr_value = fail_value;
200#else
201    attr_value = fail_value;
202#endif
203    if (attr_value)
204        return llvm::StringRef(attr_value);
205    else
206        return llvm::StringRef();
207}
208
209
210
211
212void
213XMLNode::ForEachChildNode (NodeCallback const &callback) const
214{
215#if defined( LIBXML2_DEFINED )
216    if (IsValid())
217        GetChild().ForEachSiblingNode(callback);
218#endif
219}
220
221void
222XMLNode::ForEachChildElement (NodeCallback const &callback) const
223{
224#if defined( LIBXML2_DEFINED )
225    XMLNode child = GetChild();
226    if (child)
227        child.ForEachSiblingElement(callback);
228#endif
229}
230
231void
232XMLNode::ForEachChildElementWithName (const char *name, NodeCallback const &callback) const
233{
234#if defined( LIBXML2_DEFINED )
235    XMLNode child = GetChild();
236    if (child)
237        child.ForEachSiblingElementWithName(name, callback);
238#endif
239}
240
241void
242XMLNode::ForEachAttribute (AttributeCallback const &callback) const
243{
244#if defined( LIBXML2_DEFINED )
245
246    if (IsValid())
247    {
248        for (xmlAttrPtr attr = m_node->properties; attr != nullptr; attr=attr->next)
249        {
250            // check if name matches
251            if (attr->name)
252            {
253                // check child is a text node
254                xmlNodePtr child = attr->children;
255                if (child->type == XML_TEXT_NODE)
256                {
257                    llvm::StringRef attr_value;
258                    if (child->content)
259                        attr_value = llvm::StringRef((const char *)child->content);
260                    if (callback(llvm::StringRef((const char *)attr->name), attr_value) == false)
261                        return;
262                }
263            }
264        }
265    }
266#endif
267}
268
269
270void
271XMLNode::ForEachSiblingNode (NodeCallback const &callback) const
272{
273#if defined( LIBXML2_DEFINED )
274
275    if (IsValid())
276    {
277        // iterate through all siblings
278        for (xmlNodePtr node = m_node; node; node=node->next)
279        {
280            if (callback(XMLNode(node)) == false)
281                return;
282        }
283    }
284#endif
285}
286
287void
288XMLNode::ForEachSiblingElement (NodeCallback const &callback) const
289{
290#if defined( LIBXML2_DEFINED )
291
292    if (IsValid())
293    {
294        // iterate through all siblings
295        for (xmlNodePtr node = m_node; node; node=node->next)
296        {
297            // we are looking for element nodes only
298            if (node->type != XML_ELEMENT_NODE)
299                continue;
300
301            if (callback(XMLNode(node)) == false)
302                return;
303        }
304    }
305#endif
306}
307
308void
309XMLNode::ForEachSiblingElementWithName (const char *name, NodeCallback const &callback) const
310{
311#if defined( LIBXML2_DEFINED )
312
313    if (IsValid())
314    {
315        // iterate through all siblings
316        for (xmlNodePtr node = m_node; node; node=node->next)
317        {
318            // we are looking for element nodes only
319            if (node->type != XML_ELEMENT_NODE)
320                continue;
321
322            // If name is nullptr, we take all nodes of type "t", else
323            // just the ones whose name matches
324            if (name)
325            {
326                if (strcmp((const char *)node->name, name) != 0)
327                    continue; // Name mismatch, ignore this one
328            }
329            else
330            {
331                if (node->name)
332                    continue; // nullptr name specified and this elemnt has a name, ignore this one
333            }
334
335            if (callback(XMLNode(node)) == false)
336                return;
337        }
338    }
339#endif
340}
341
342llvm::StringRef
343XMLNode::GetName() const
344{
345#if defined( LIBXML2_DEFINED )
346    if (IsValid())
347    {
348        if (m_node->name)
349            return llvm::StringRef((const char *)m_node->name);
350    }
351#endif
352    return llvm::StringRef();
353}
354
355bool
356XMLNode::GetElementText (std::string &text) const
357{
358    text.clear();
359#if defined( LIBXML2_DEFINED )
360    if (IsValid())
361    {
362        bool success = false;
363        if (m_node->type == XML_ELEMENT_NODE)
364        {
365            // check child is a text node
366            for (xmlNodePtr node = m_node->children;
367                 node != nullptr;
368                 node = node->next)
369            {
370                if (node->type == XML_TEXT_NODE)
371                {
372                    text.append((const char *)node->content);
373                    success = true;
374                }
375            }
376        }
377        return success;
378    }
379#endif
380    return false;
381}
382
383
384bool
385XMLNode::GetElementTextAsUnsigned (uint64_t &value, uint64_t fail_value, int base) const
386{
387    bool success = false;
388#if defined( LIBXML2_DEFINED )
389    if (IsValid())
390    {
391        std::string text;
392        if (GetElementText(text))
393            value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success);
394    }
395#endif
396    if (!success)
397        value = fail_value;
398    return success;
399}
400
401bool
402XMLNode::GetElementTextAsFloat (double &value, double fail_value) const
403{
404    bool success = false;
405#if defined( LIBXML2_DEFINED )
406    if (IsValid())
407    {
408        std::string text;
409        if (GetElementText(text))
410        {
411            value = atof(text.c_str());
412            success = true;
413        }
414    }
415#endif
416    if (!success)
417        value = fail_value;
418    return success;
419}
420
421
422
423bool
424XMLNode::NameIs (const char *name) const
425{
426#if defined( LIBXML2_DEFINED )
427
428    if (IsValid())
429    {
430        // In case we are looking for a nullptr name or an exact pointer match
431        if (m_node->name == (const xmlChar *)name)
432            return true;
433        if (m_node->name)
434            return strcmp((const char *)m_node->name, name) == 0;
435    }
436#endif
437    return false;
438}
439
440XMLNode
441XMLNode::FindFirstChildElementWithName (const char *name) const
442{
443    XMLNode result_node;
444
445#if defined( LIBXML2_DEFINED )
446    ForEachChildElementWithName(name, [&result_node, name](const XMLNode& node) -> bool {
447        result_node = node;
448        // Stop iterating, we found the node we wanted
449        return false;
450    });
451#endif
452
453    return result_node;
454}
455
456bool
457XMLNode::IsValid() const
458{
459    return m_node != nullptr;
460}
461
462bool
463XMLNode::IsElement () const
464{
465#if defined( LIBXML2_DEFINED )
466    if (IsValid())
467        return m_node->type == XML_ELEMENT_NODE;
468#endif
469    return false;
470}
471
472
473XMLNode
474XMLNode::GetElementForPath (const NamePath &path)
475{
476#if defined( LIBXML2_DEFINED )
477
478    if (IsValid())
479    {
480        if (path.empty())
481            return *this;
482        else
483        {
484            XMLNode node = FindFirstChildElementWithName(path[0].c_str());
485            const size_t n = path.size();
486            for (size_t i=1; node && i<n; ++i)
487                node = node.FindFirstChildElementWithName(path[i].c_str());
488            return node;
489        }
490    }
491#endif
492
493    return XMLNode();
494}
495
496
497#pragma mark -- ApplePropertyList
498
499ApplePropertyList::ApplePropertyList() :
500    m_xml_doc(),
501    m_dict_node()
502{
503
504}
505
506ApplePropertyList::ApplePropertyList (const char *path) :
507    m_xml_doc(),
508    m_dict_node()
509{
510    ParseFile(path);
511}
512
513ApplePropertyList::~ApplePropertyList()
514{
515}
516
517const std::string &
518ApplePropertyList::GetErrors() const
519{
520    return m_xml_doc.GetErrors();
521}
522
523
524bool
525ApplePropertyList::ParseFile (const char *path)
526{
527    if (m_xml_doc.ParseFile(path))
528    {
529        XMLNode plist = m_xml_doc.GetRootElement("plist");
530        if (plist)
531        {
532            plist.ForEachChildElementWithName("dict", [this](const XMLNode &dict) -> bool {
533                this->m_dict_node = dict;
534                return false; // Stop iterating
535            });
536            return (bool)m_dict_node;
537        }
538    }
539    return false;
540}
541
542bool
543ApplePropertyList::IsValid() const
544{
545    return (bool)m_dict_node;
546}
547
548bool
549ApplePropertyList::GetValueAsString (const char *key, std::string &value) const
550{
551    XMLNode value_node = GetValueNode (key);
552    if (value_node)
553        return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
554    return false;
555}
556
557XMLNode
558ApplePropertyList::GetValueNode (const char *key) const
559{
560    XMLNode value_node;
561#if defined( LIBXML2_DEFINED )
562
563    if (IsValid())
564    {
565        m_dict_node.ForEachChildElementWithName("key", [key, &value_node](const XMLNode &key_node) -> bool {
566            std::string key_name;
567            if (key_node.GetElementText(key_name))
568            {
569                if (key_name.compare(key) == 0)
570                {
571                    value_node = key_node.GetSibling();
572                    while (value_node && !value_node.IsElement())
573                        value_node = value_node.GetSibling();
574                    return false; // Stop iterating
575                }
576            }
577            return true; // Keep iterating
578        });
579    }
580#endif
581    return value_node;
582}
583
584bool
585ApplePropertyList::ExtractStringFromValueNode (const XMLNode &node, std::string &value)
586{
587    value.clear();
588#if defined( LIBXML2_DEFINED )
589    if (node.IsValid())
590    {
591        llvm::StringRef element_name = node.GetName();
592        if (element_name == "true" || element_name == "false")
593        {
594            // The text value _is_ the element name itself...
595            value = std::move(element_name.str());
596            return true;
597        }
598        else if (element_name == "dict" || element_name == "array")
599            return false; // dictionaries and arrays have no text value, so we fail
600        else
601            return node.GetElementText(value);
602    }
603#endif
604    return false;
605}
606
607#if defined( LIBXML2_DEFINED )
608
609namespace {
610
611    StructuredData::ObjectSP
612    CreatePlistValue (XMLNode node)
613    {
614        llvm::StringRef element_name = node.GetName();
615        if (element_name == "array")
616        {
617            std::shared_ptr<StructuredData::Array> array_sp(new StructuredData::Array());
618            node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
619                array_sp->AddItem(CreatePlistValue(node));
620                return true; // Keep iterating through all child elements of the array
621            });
622            return array_sp;
623        }
624        else if (element_name == "dict")
625        {
626            XMLNode key_node;
627            std::shared_ptr<StructuredData::Dictionary> dict_sp(new StructuredData::Dictionary());
628            node.ForEachChildElement([&key_node, &dict_sp](const XMLNode &node) -> bool {
629                if (node.NameIs("key"))
630                {
631                    // This is a "key" element node
632                    key_node = node;
633                }
634                else
635                {
636                    // This is a value node
637                    if (key_node)
638                    {
639                        std::string key_name;
640                        key_node.GetElementText(key_name);
641                        dict_sp->AddItem(key_name, CreatePlistValue(node));
642                        key_node.Clear();
643                    }
644                }
645                return true; // Keep iterating through all child elements of the dictionary
646            });
647            return dict_sp;
648        }
649        else if (element_name == "real")
650        {
651            double value = 0.0;
652            node.GetElementTextAsFloat(value);
653            return StructuredData::ObjectSP(new StructuredData::Float(value));
654        }
655        else if (element_name == "integer")
656        {
657            uint64_t value = 0;
658            node.GetElementTextAsUnsigned(value, 0, 0);
659            return StructuredData::ObjectSP(new StructuredData::Integer(value));
660        }
661        else if ((element_name == "string") || (element_name == "data") || (element_name == "date"))
662        {
663            std::string text;
664            node.GetElementText(text);
665            return StructuredData::ObjectSP(new StructuredData::String(std::move(text)));
666        }
667        else if (element_name == "true")
668        {
669            return StructuredData::ObjectSP(new StructuredData::Boolean(true));
670        }
671        else if (element_name == "false")
672        {
673            return StructuredData::ObjectSP(new StructuredData::Boolean(false));
674        }
675        return StructuredData::ObjectSP(new StructuredData::Null());
676    }
677}
678#endif
679
680StructuredData::ObjectSP
681ApplePropertyList::GetStructuredData()
682{
683    StructuredData::ObjectSP root_sp;
684#if defined( LIBXML2_DEFINED )
685    if (IsValid())
686    {
687        return CreatePlistValue(m_dict_node);
688    }
689#endif
690    return root_sp;
691}
692
693
694