VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/xml.cpp@ 23903

Last change on this file since 23903 was 23903, checked in by vboxsync, 16 years ago

Runtime: fix memory leak

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.5 KB
Line 
1/** @file
2 * VirtualBox XML Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.215389.xyz. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 *
25 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
26 * Clara, CA 95054 USA or visit http://www.sun.com if you need
27 * additional information or have any questions.
28 */
29
30#include <iprt/cdefs.h>
31#include <iprt/err.h>
32#include <iprt/file.h>
33#include <iprt/lock.h>
34#include <iprt/xml_cpp.h>
35
36#include <libxml/tree.h>
37#include <libxml/parser.h>
38#include <libxml/globals.h>
39#include <libxml/xmlIO.h>
40#include <libxml/xmlsave.h>
41#include <libxml/uri.h>
42
43#include <libxml/xmlschemas.h>
44
45#include <map>
46#include <boost/shared_ptr.hpp>
47
48////////////////////////////////////////////////////////////////////////////////
49//
50// globals
51//
52////////////////////////////////////////////////////////////////////////////////
53
54/**
55 * Global module initialization structure. This is to wrap non-reentrant bits
56 * of libxml, among other things.
57 *
58 * The constructor and destructor of this structure are used to perform global
59 * module initiaizaton and cleanup. Thee must be only one global variable of
60 * this structure.
61 */
62static
63class Global
64{
65public:
66
67 Global()
68 {
69 /* Check the parser version. The docs say it will kill the app if
70 * there is a serious version mismatch, but I couldn't find it in the
71 * source code (it only prints the error/warning message to the console) so
72 * let's leave it as is for informational purposes. */
73 LIBXML_TEST_VERSION
74
75 /* Init libxml */
76 xmlInitParser();
77
78 /* Save the default entity resolver before someone has replaced it */
79 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
80 }
81
82 ~Global()
83 {
84 /* Shutdown libxml */
85 xmlCleanupParser();
86 }
87
88 struct
89 {
90 xmlExternalEntityLoader defaultEntityLoader;
91
92 /** Used to provide some thread safety missing in libxml2 (see e.g.
93 * XmlTreeBackend::read()) */
94 RTLockMtx lock;
95 }
96 sxml; /* XXX naming this xml will break with gcc-3.3 */
97}
98gGlobal;
99
100
101
102namespace xml
103{
104
105////////////////////////////////////////////////////////////////////////////////
106//
107// Exceptions
108//
109////////////////////////////////////////////////////////////////////////////////
110
111LogicError::LogicError(RT_SRC_POS_DECL)
112 : Error(NULL)
113{
114 char *msg = NULL;
115 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
116 pszFunction, pszFile, iLine);
117 setWhat(msg);
118 RTStrFree(msg);
119}
120
121XmlError::XmlError(xmlErrorPtr aErr)
122{
123 if (!aErr)
124 throw EInvalidArg(RT_SRC_POS);
125
126 char *msg = Format(aErr);
127 setWhat(msg);
128 RTStrFree(msg);
129}
130
131/**
132 * Composes a single message for the given error. The caller must free the
133 * returned string using RTStrFree() when no more necessary.
134 */
135// static
136char *XmlError::Format(xmlErrorPtr aErr)
137{
138 const char *msg = aErr->message ? aErr->message : "<none>";
139 size_t msgLen = strlen(msg);
140 /* strip spaces, trailing EOLs and dot-like char */
141 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
142 --msgLen;
143
144 char *finalMsg = NULL;
145 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
146 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
147
148 return finalMsg;
149}
150
151EIPRTFailure::EIPRTFailure(int aRC)
152 : RuntimeError(NULL),
153 mRC(aRC)
154{
155 char *newMsg = NULL;
156 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
157 setWhat(newMsg);
158 RTStrFree(newMsg);
159}
160
161////////////////////////////////////////////////////////////////////////////////
162//
163// File Class
164//
165//////////////////////////////////////////////////////////////////////////////
166
167struct File::Data
168{
169 Data()
170 : handle(NIL_RTFILE), opened(false)
171 { }
172
173 iprt::MiniString strFileName;
174 RTFILE handle;
175 bool opened : 1;
176};
177
178File::File(Mode aMode, const char *aFileName)
179 : m(new Data())
180{
181 m->strFileName = aFileName;
182
183 unsigned flags = 0;
184 switch (aMode)
185 {
186 case Mode_Read:
187 flags = RTFILE_O_READ;
188 break;
189 case Mode_WriteCreate: // fail if file exists
190 flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
191 break;
192 case Mode_Overwrite: // overwrite if file exists
193 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE;
194 break;
195 case Mode_ReadWrite:
196 flags = RTFILE_O_READ | RTFILE_O_WRITE;
197 }
198
199 int vrc = RTFileOpen (&m->handle, aFileName, flags);
200 if (RT_FAILURE (vrc))
201 throw EIPRTFailure (vrc);
202
203 m->opened = true;
204}
205
206File::File(RTFILE aHandle, const char *aFileName /* = NULL */)
207 : m(new Data())
208{
209 if (aHandle == NIL_RTFILE)
210 throw EInvalidArg (RT_SRC_POS);
211
212 m->handle = aHandle;
213
214 if (aFileName)
215 m->strFileName = aFileName;
216
217 setPos (0);
218}
219
220File::~File()
221{
222 if (m->opened)
223 RTFileClose(m->handle);
224 delete m;
225}
226
227const char* File::uri() const
228{
229 return m->strFileName.c_str();
230}
231
232uint64_t File::pos() const
233{
234 uint64_t p = 0;
235 int vrc = RTFileSeek (m->handle, 0, RTFILE_SEEK_CURRENT, &p);
236 if (RT_SUCCESS (vrc))
237 return p;
238
239 throw EIPRTFailure (vrc);
240}
241
242void File::setPos (uint64_t aPos)
243{
244 uint64_t p = 0;
245 unsigned method = RTFILE_SEEK_BEGIN;
246 int vrc = VINF_SUCCESS;
247
248 /* check if we overflow int64_t and move to INT64_MAX first */
249 if (((int64_t) aPos) < 0)
250 {
251 vrc = RTFileSeek (m->handle, INT64_MAX, method, &p);
252 aPos -= (uint64_t) INT64_MAX;
253 method = RTFILE_SEEK_CURRENT;
254 }
255 /* seek the rest */
256 if (RT_SUCCESS (vrc))
257 vrc = RTFileSeek (m->handle, (int64_t) aPos, method, &p);
258 if (RT_SUCCESS (vrc))
259 return;
260
261 throw EIPRTFailure (vrc);
262}
263
264int File::read (char *aBuf, int aLen)
265{
266 size_t len = aLen;
267 int vrc = RTFileRead (m->handle, aBuf, len, &len);
268 if (RT_SUCCESS (vrc))
269 return (int)len;
270
271 throw EIPRTFailure (vrc);
272}
273
274int File::write (const char *aBuf, int aLen)
275{
276 size_t len = aLen;
277 int vrc = RTFileWrite (m->handle, aBuf, len, &len);
278 if (RT_SUCCESS (vrc))
279 return (int)len;
280
281 throw EIPRTFailure (vrc);
282
283 return -1 /* failure */;
284}
285
286void File::truncate()
287{
288 int vrc = RTFileSetSize (m->handle, pos());
289 if (RT_SUCCESS (vrc))
290 return;
291
292 throw EIPRTFailure (vrc);
293}
294
295////////////////////////////////////////////////////////////////////////////////
296//
297// MemoryBuf Class
298//
299//////////////////////////////////////////////////////////////////////////////
300
301struct MemoryBuf::Data
302{
303 Data()
304 : buf (NULL), len (0), uri (NULL), pos (0) {}
305
306 const char *buf;
307 size_t len;
308 char *uri;
309
310 size_t pos;
311};
312
313MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
314 : m (new Data())
315{
316 if (aBuf == NULL)
317 throw EInvalidArg (RT_SRC_POS);
318
319 m->buf = aBuf;
320 m->len = aLen;
321 m->uri = RTStrDup (aURI);
322}
323
324MemoryBuf::~MemoryBuf()
325{
326 RTStrFree (m->uri);
327}
328
329const char *MemoryBuf::uri() const
330{
331 return m->uri;
332}
333
334uint64_t MemoryBuf::pos() const
335{
336 return m->pos;
337}
338
339void MemoryBuf::setPos (uint64_t aPos)
340{
341 size_t pos = (size_t) aPos;
342 if ((uint64_t) pos != aPos)
343 throw EInvalidArg();
344
345 if (pos > m->len)
346 throw EInvalidArg();
347
348 m->pos = pos;
349}
350
351int MemoryBuf::read (char *aBuf, int aLen)
352{
353 if (m->pos >= m->len)
354 return 0 /* nothing to read */;
355
356 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
357 memcpy (aBuf, m->buf + m->pos, len);
358 m->pos += len;
359
360 return (int)len;
361}
362
363////////////////////////////////////////////////////////////////////////////////
364//
365// GlobalLock class
366//
367////////////////////////////////////////////////////////////////////////////////
368
369struct GlobalLock::Data
370{
371 PFNEXTERNALENTITYLOADER pOldLoader;
372 RTLock lock;
373
374 Data()
375 : pOldLoader(NULL),
376 lock(gGlobal.sxml.lock)
377 {
378 }
379};
380
381GlobalLock::GlobalLock()
382 : m(new Data())
383{
384}
385
386GlobalLock::~GlobalLock()
387{
388 if (m->pOldLoader)
389 xmlSetExternalEntityLoader(m->pOldLoader);
390 delete m;
391 m = NULL;
392}
393
394void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
395{
396 m->pOldLoader = xmlGetExternalEntityLoader();
397 xmlSetExternalEntityLoader(pLoader);
398}
399
400// static
401xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
402 const char *aID,
403 xmlParserCtxt *aCtxt)
404{
405 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
406}
407
408////////////////////////////////////////////////////////////////////////////////
409//
410// Node class
411//
412////////////////////////////////////////////////////////////////////////////////
413
414struct Node::Data
415{
416 xmlNode *plibNode; // != NULL if this is an element or content node
417 xmlAttr *plibAttr; // != NULL if this is an attribute node
418
419 Node *pParent; // NULL only for the root element
420 const char *pcszName; // element or attribute name, points either into plibNode or plibAttr;
421 // NULL if this is a content node
422
423 struct compare_const_char
424 {
425 bool operator()(const char* s1, const char* s2) const
426 {
427 return strcmp(s1, s2) < 0;
428 }
429 };
430
431 // attributes, if this is an element; can be empty
432 typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap;
433 AttributesMap attribs;
434
435 // child elements, if this is an element; can be empty
436 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
437 InternalNodesList children;
438};
439
440Node::Node(EnumType type)
441 : mType(type),
442 m(new Data)
443{
444 m->plibNode = NULL;
445 m->plibAttr = NULL;
446 m->pParent = NULL;
447}
448
449Node::~Node()
450{
451 delete m;
452}
453
454void Node::buildChildren() // private
455{
456 // go thru this element's attributes
457 xmlAttr *plibAttr = m->plibNode->properties;
458 while (plibAttr)
459 {
460 const char *pcszAttribName = (const char*)plibAttr->name;
461 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
462 pNew->m->plibAttr = plibAttr;
463 pNew->m->pcszName = (const char*)plibAttr->name;
464 pNew->m->pParent = this;
465 // store
466 m->attribs[pcszAttribName] = pNew;
467
468 plibAttr = plibAttr->next;
469 }
470
471 // go thru this element's child elements
472 xmlNodePtr plibNode = m->plibNode->children;
473 while (plibNode)
474 {
475 boost::shared_ptr<Node> pNew;
476
477 if (plibNode->type == XML_ELEMENT_NODE)
478 pNew = boost::shared_ptr<Node>(new ElementNode);
479 else if (plibNode->type == XML_TEXT_NODE)
480 pNew = boost::shared_ptr<Node>(new ContentNode);
481 if (pNew)
482 {
483 pNew->m->plibNode = plibNode;
484 pNew->m->pcszName = (const char*)plibNode->name;
485 pNew->m->pParent = this;
486 // store
487 m->children.push_back(pNew);
488
489 // recurse for this child element to get its own children
490 pNew->buildChildren();
491 }
492
493 plibNode = plibNode->next;
494 }
495}
496
497const char* Node::getName() const
498{
499 return m->pcszName;
500}
501
502bool Node::nameEquals(const char *pcsz) const
503{
504 if (m->pcszName == pcsz)
505 return true;
506 if (m->pcszName == NULL)
507 return false;
508 if (pcsz == NULL)
509 return false;
510 return !strcmp(m->pcszName, pcsz);
511}
512
513
514/**
515 * Returns the value of a node. If this node is an attribute, returns
516 * the attribute value; if this node is an element, then this returns
517 * the element text content.
518 * @return
519 */
520const char* Node::getValue() const
521{
522 if ( (m->plibAttr)
523 && (m->plibAttr->children)
524 )
525 // libxml hides attribute values in another node created as a
526 // single child of the attribute node, and it's in the content field
527 return (const char*)m->plibAttr->children->content;
528
529 if ( (m->plibNode)
530 && (m->plibNode->children)
531 )
532 return (const char*)m->plibNode->children->content;
533
534 return NULL;
535}
536
537/**
538 * Copies the value of a node into the given integer variable.
539 * Returns TRUE only if a value was found and was actually an
540 * integer of the given type.
541 * @return
542 */
543bool Node::copyValue(int32_t &i) const
544{
545 const char *pcsz;
546 if ( ((pcsz = getValue()))
547 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
548 )
549 return true;
550
551 return false;
552}
553
554/**
555 * Copies the value of a node into the given integer variable.
556 * Returns TRUE only if a value was found and was actually an
557 * integer of the given type.
558 * @return
559 */
560bool Node::copyValue(uint32_t &i) const
561{
562 const char *pcsz;
563 if ( ((pcsz = getValue()))
564 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
565 )
566 return true;
567
568 return false;
569}
570
571/**
572 * Copies the value of a node into the given integer variable.
573 * Returns TRUE only if a value was found and was actually an
574 * integer of the given type.
575 * @return
576 */
577bool Node::copyValue(int64_t &i) const
578{
579 const char *pcsz;
580 if ( ((pcsz = getValue()))
581 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
582 )
583 return true;
584
585 return false;
586}
587
588/**
589 * Copies the value of a node into the given integer variable.
590 * Returns TRUE only if a value was found and was actually an
591 * integer of the given type.
592 * @return
593 */
594bool Node::copyValue(uint64_t &i) const
595{
596 const char *pcsz;
597 if ( ((pcsz = getValue()))
598 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
599 )
600 return true;
601
602 return false;
603}
604
605/**
606 * Returns the line number of the current node in the source XML file.
607 * Useful for error messages.
608 * @return
609 */
610int Node::getLineNumber() const
611{
612 if (m->plibAttr)
613 return m->pParent->m->plibNode->line;
614
615 return m->plibNode->line;
616}
617
618ElementNode::ElementNode()
619 : Node(IsElement)
620{
621}
622
623/**
624 * Builds a list of direct child elements of the current element that
625 * match the given string; if pcszMatch is NULL, all direct child
626 * elements are returned.
627 * @param children out: list of nodes to which children will be appended.
628 * @param pcszMatch in: match string, or NULL to return all children.
629 * @return Number of items appended to the list (0 if none).
630 */
631int ElementNode::getChildElements(ElementNodesList &children,
632 const char *pcszMatch /*= NULL*/)
633 const
634{
635 int i = 0;
636 Data::InternalNodesList::const_iterator
637 it,
638 last = m->children.end();
639 for (it = m->children.begin();
640 it != last;
641 ++it)
642 {
643 // export this child node if ...
644 if ( (!pcszMatch) // the caller wants all nodes or
645 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
646 )
647 {
648 Node *pNode = (*it).get();
649 if (pNode->isElement())
650 children.push_back(static_cast<ElementNode*>(pNode));
651 ++i;
652 }
653 }
654 return i;
655}
656
657/**
658 * Returns the first child element whose name matches pcszMatch.
659 * @param pcszMatch
660 * @return
661 */
662const ElementNode* ElementNode::findChildElement(const char *pcszMatch)
663 const
664{
665 Data::InternalNodesList::const_iterator
666 it,
667 last = m->children.end();
668 for (it = m->children.begin();
669 it != last;
670 ++it)
671 {
672 if ((**it).isElement())
673 {
674 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
675 if (!strcmp(pcszMatch, pelm->getName())) // the element name matches
676 return pelm;
677 }
678 }
679
680 return NULL;
681}
682
683/**
684 * Returns the first child element whose "id" attribute matches pcszId.
685 * @param pcszId identifier to look for.
686 * @return child element or NULL if not found.
687 */
688const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
689{
690 Data::InternalNodesList::const_iterator
691 it,
692 last = m->children.end();
693 for (it = m->children.begin();
694 it != last;
695 ++it)
696 {
697 if ((**it).isElement())
698 {
699 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
700 const AttributeNode *pAttr;
701 if ( ((pAttr = pelm->findAttribute("id")))
702 && (!strcmp(pAttr->getValue(), pcszId))
703 )
704 return pelm;
705 }
706 }
707
708 return NULL;
709}
710
711/**
712 *
713 * @param pcszMatch
714 * @return
715 */
716const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
717{
718 Data::AttributesMap::const_iterator it;
719
720 it = m->attribs.find(pcszMatch);
721 if (it != m->attribs.end())
722 return it->second.get();
723
724 return NULL;
725}
726
727/**
728 * Convenience method which attempts to find the attribute with the given
729 * name and returns its value as a string.
730 *
731 * @param pcszMatch name of attribute to find.
732 * @param str out: attribute value
733 * @return TRUE if attribute was found and str was thus updated.
734 */
735bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
736{
737 const Node* pAttr;
738 if ((pAttr = findAttribute(pcszMatch)))
739 {
740 ppcsz = pAttr->getValue();
741 return true;
742 }
743
744 return false;
745}
746
747/**
748 * Convenience method which attempts to find the attribute with the given
749 * name and returns its value as a string.
750 *
751 * @param pcszMatch name of attribute to find.
752 * @param str out: attribute value; overwritten only if attribute was found
753 * @return TRUE if attribute was found and str was thus updated.
754 */
755bool ElementNode::getAttributeValue(const char *pcszMatch, iprt::MiniString &str) const
756{
757 const Node* pAttr;
758 if ((pAttr = findAttribute(pcszMatch)))
759 {
760 str = pAttr->getValue();
761 return true;
762 }
763
764 return false;
765}
766
767/**
768 * Convenience method which attempts to find the attribute with the given
769 * name and returns its value as a signed integer. This calls
770 * RTStrToInt32Ex internally and will only output the integer if that
771 * function returns no error.
772 *
773 * @param pcszMatch name of attribute to find.
774 * @param i out: attribute value; overwritten only if attribute was found
775 * @return TRUE if attribute was found and str was thus updated.
776 */
777bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
778{
779 const char *pcsz;
780 if ( (getAttributeValue(pcszMatch, pcsz))
781 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
782 )
783 return true;
784
785 return false;
786}
787
788/**
789 * Convenience method which attempts to find the attribute with the given
790 * name and returns its value as an unsigned integer.This calls
791 * RTStrToUInt32Ex internally and will only output the integer if that
792 * function returns no error.
793 *
794 * @param pcszMatch name of attribute to find.
795 * @param i out: attribute value; overwritten only if attribute was found
796 * @return TRUE if attribute was found and str was thus updated.
797 */
798bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
799{
800 const char *pcsz;
801 if ( (getAttributeValue(pcszMatch, pcsz))
802 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
803 )
804 return true;
805
806 return false;
807}
808
809/**
810 * Convenience method which attempts to find the attribute with the given
811 * name and returns its value as a signed long integer. This calls
812 * RTStrToInt64Ex internally and will only output the integer if that
813 * function returns no error.
814 *
815 * @param pcszMatch name of attribute to find.
816 * @param i out: attribute value
817 * @return TRUE if attribute was found and str was thus updated.
818 */
819bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
820{
821 const char *pcsz;
822 if ( (getAttributeValue(pcszMatch, pcsz))
823 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
824 )
825 return true;
826
827 return false;
828}
829
830/**
831 * Convenience method which attempts to find the attribute with the given
832 * name and returns its value as an unsigned long integer.This calls
833 * RTStrToUInt64Ex internally and will only output the integer if that
834 * function returns no error.
835 *
836 * @param pcszMatch name of attribute to find.
837 * @param i out: attribute value; overwritten only if attribute was found
838 * @return TRUE if attribute was found and str was thus updated.
839 */
840bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
841{
842 const char *pcsz;
843 if ( (getAttributeValue(pcszMatch, pcsz))
844 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
845 )
846 return true;
847
848 return false;
849}
850
851/**
852 * Convenience method which attempts to find the attribute with the given
853 * name and returns its value as a boolean. This accepts "true", "false",
854 * "yes", "no", "1" or "0" as valid values.
855 *
856 * @param pcszMatch name of attribute to find.
857 * @param i out: attribute value; overwritten only if attribute was found
858 * @return TRUE if attribute was found and str was thus updated.
859 */
860bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const
861{
862 const char *pcsz;
863 if (getAttributeValue(pcszMatch, pcsz))
864 {
865 if ( (!strcmp(pcsz, "true"))
866 || (!strcmp(pcsz, "yes"))
867 || (!strcmp(pcsz, "1"))
868 )
869 {
870 f = true;
871 return true;
872 }
873 if ( (!strcmp(pcsz, "false"))
874 || (!strcmp(pcsz, "no"))
875 || (!strcmp(pcsz, "0"))
876 )
877 {
878 f = false;
879 return true;
880 }
881 }
882
883 return false;
884}
885
886/**
887 * Creates a new child element node and appends it to the list
888 * of children in "this".
889 *
890 * @param pcszElementName
891 * @return
892 */
893ElementNode* ElementNode::createChild(const char *pcszElementName)
894{
895 // we must be an element, not an attribute
896 if (!m->plibNode)
897 throw ENodeIsNotElement(RT_SRC_POS);
898
899 // libxml side: create new node
900 xmlNode *plibNode;
901 if (!(plibNode = xmlNewNode(NULL, // namespace
902 (const xmlChar*)pcszElementName)))
903 throw std::bad_alloc();
904 xmlAddChild(m->plibNode, plibNode);
905
906 // now wrap this in C++
907 ElementNode *p = new ElementNode;
908 boost::shared_ptr<ElementNode> pNew(p);
909 pNew->m->plibNode = plibNode;
910 pNew->m->pcszName = (const char*)plibNode->name;
911
912 m->children.push_back(pNew);
913
914 return p;
915}
916
917
918/**
919 * Creates a content node and appends it to the list of children
920 * in "this".
921 *
922 * @param pcszElementName
923 * @return
924 */
925ContentNode* ElementNode::addContent(const char *pcszContent)
926{
927 // libxml side: create new node
928 xmlNode *plibNode;
929 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
930 throw std::bad_alloc();
931 xmlAddChild(m->plibNode, plibNode);
932
933 // now wrap this in C++
934 ContentNode *p = new ContentNode;
935 boost::shared_ptr<ContentNode> pNew(p);
936 pNew->m->plibNode = plibNode;
937 pNew->m->pcszName = NULL;
938
939 m->children.push_back(pNew);
940
941 return p;
942}
943
944/**
945 * Sets the given attribute.
946 *
947 * If an attribute with the given name exists, it is overwritten,
948 * otherwise a new attribute is created. Returns the attribute node
949 * that was either created or changed.
950 *
951 * @param pcszName
952 * @param pcszValue
953 * @return
954 */
955AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
956{
957 Data::AttributesMap::const_iterator it;
958
959 it = m->attribs.find(pcszName);
960 if (it == m->attribs.end())
961 {
962 // libxml side: xmlNewProp creates an attribute
963 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
964 const char *pcszAttribName = (const char*)plibAttr->name;
965
966 // C++ side: create an attribute node around it
967 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
968 pNew->m->plibAttr = plibAttr;
969 pNew->m->pcszName = (const char*)plibAttr->name;
970 pNew->m->pParent = this;
971 // store
972 m->attribs[pcszAttribName] = pNew;
973 }
974 else
975 {
976 // @todo
977 throw LogicError("Attribute exists");
978 }
979
980 return NULL;
981
982}
983
984AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
985{
986 char *psz = NULL;
987 RTStrAPrintf(&psz, "%RI32", i);
988 AttributeNode *p = setAttribute(pcszName, psz);
989 RTStrFree(psz);
990 return p;
991}
992
993AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t i)
994{
995 char *psz = NULL;
996 RTStrAPrintf(&psz, "%RU32", i);
997 AttributeNode *p = setAttribute(pcszName, psz);
998 RTStrFree(psz);
999 return p;
1000}
1001
1002AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1003{
1004 char *psz = NULL;
1005 RTStrAPrintf(&psz, "%RI64", i);
1006 AttributeNode *p = setAttribute(pcszName, psz);
1007 RTStrFree(psz);
1008 return p;
1009}
1010
1011AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t i)
1012{
1013 char *psz = NULL;
1014 RTStrAPrintf(&psz, "%RU64", i);
1015 AttributeNode *p = setAttribute(pcszName, psz);
1016 RTStrFree(psz);
1017 return p;
1018}
1019
1020AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t i)
1021{
1022 char *psz = NULL;
1023 RTStrAPrintf(&psz, "0x%RX32", i);
1024 AttributeNode *p = setAttribute(pcszName, psz);
1025 RTStrFree(psz);
1026 return p;
1027}
1028
1029AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1030{
1031 return setAttribute(pcszName, (f) ? "true" : "false");
1032}
1033
1034AttributeNode::AttributeNode()
1035 : Node(IsAttribute)
1036{
1037}
1038
1039ContentNode::ContentNode()
1040 : Node(IsContent)
1041{
1042}
1043
1044/*
1045 * NodesLoop
1046 *
1047 */
1048
1049struct NodesLoop::Data
1050{
1051 ElementNodesList listElements;
1052 ElementNodesList::const_iterator it;
1053};
1054
1055NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1056{
1057 m = new Data;
1058 node.getChildElements(m->listElements, pcszMatch);
1059 m->it = m->listElements.begin();
1060}
1061
1062NodesLoop::~NodesLoop()
1063{
1064 delete m;
1065}
1066
1067
1068/**
1069 * Handy convenience helper for looping over all child elements. Create an
1070 * instance of NodesLoop on the stack and call this method until it returns
1071 * NULL, like this:
1072 * <code>
1073 * xml::ElementNode node; // should point to an element
1074 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1075 * const xml::ElementNode *pChild = NULL;
1076 * while (pChild = loop.forAllNodes())
1077 * ...;
1078 * </code>
1079 * @param node
1080 * @param pcszMatch
1081 * @return
1082 */
1083const ElementNode* NodesLoop::forAllNodes() const
1084{
1085 const ElementNode *pNode = NULL;
1086
1087 if (m->it != m->listElements.end())
1088 {
1089 pNode = *(m->it);
1090 ++(m->it);
1091 }
1092
1093 return pNode;
1094}
1095
1096////////////////////////////////////////////////////////////////////////////////
1097//
1098// Document class
1099//
1100////////////////////////////////////////////////////////////////////////////////
1101
1102struct Document::Data
1103{
1104 xmlDocPtr plibDocument;
1105 ElementNode *pRootElement;
1106
1107 Data()
1108 {
1109 plibDocument = NULL;
1110 pRootElement = NULL;
1111 }
1112
1113 ~Data()
1114 {
1115 reset();
1116 }
1117
1118 void reset()
1119 {
1120 if (plibDocument)
1121 {
1122 xmlFreeDoc(plibDocument);
1123 plibDocument = NULL;
1124 }
1125 if (pRootElement)
1126 {
1127 delete pRootElement;
1128 pRootElement = NULL;
1129 }
1130 }
1131
1132 void copyFrom(const Document::Data *p)
1133 {
1134 if (p->plibDocument)
1135 {
1136 plibDocument = xmlCopyDoc(p->plibDocument,
1137 1); // recursive == copy all
1138 }
1139 }
1140};
1141
1142Document::Document()
1143 : m(new Data)
1144{
1145}
1146
1147Document::Document(const Document &x)
1148 : m(new Data)
1149{
1150 m->copyFrom(x.m);
1151}
1152
1153Document& Document::operator=(const Document &x)
1154{
1155 m->reset();
1156 m->copyFrom(x.m);
1157 return *this;
1158}
1159
1160Document::~Document()
1161{
1162 delete m;
1163}
1164
1165/**
1166 * private method to refresh all internal structures after the internal pDocument
1167 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1168 * called before to make sure all members except the internal pDocument are clean.
1169 */
1170void Document::refreshInternals() // private
1171{
1172 m->pRootElement = new ElementNode();
1173 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
1174 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
1175
1176 m->pRootElement->buildChildren();
1177}
1178
1179/**
1180 * Returns the root element of the document, or NULL if the document is empty.
1181 * Const variant.
1182 * @return
1183 */
1184const ElementNode* Document::getRootElement() const
1185{
1186 return m->pRootElement;
1187}
1188
1189/**
1190 * Returns the root element of the document, or NULL if the document is empty.
1191 * Non-const variant.
1192 * @return
1193 */
1194ElementNode* Document::getRootElement()
1195{
1196 return m->pRootElement;
1197}
1198
1199/**
1200 * Creates a new element node and sets it as the root element. This will
1201 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1202 */
1203ElementNode* Document::createRootElement(const char *pcszRootElementName)
1204{
1205 if (m->pRootElement || m->plibDocument)
1206 throw EDocumentNotEmpty(RT_SRC_POS);
1207
1208 // libxml side: create document, create root node
1209 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1210 xmlNode *plibRootNode;
1211 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1212 (const xmlChar*)pcszRootElementName)))
1213 throw std::bad_alloc();
1214 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1215
1216 // now wrap this in C++
1217 m->pRootElement = new ElementNode();
1218 m->pRootElement->m->plibNode = plibRootNode;
1219 m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
1220
1221 return m->pRootElement;
1222}
1223
1224////////////////////////////////////////////////////////////////////////////////
1225//
1226// XmlParserBase class
1227//
1228////////////////////////////////////////////////////////////////////////////////
1229
1230XmlParserBase::XmlParserBase()
1231{
1232 m_ctxt = xmlNewParserCtxt();
1233 if (m_ctxt == NULL)
1234 throw std::bad_alloc();
1235}
1236
1237XmlParserBase::~XmlParserBase()
1238{
1239 xmlFreeParserCtxt (m_ctxt);
1240 m_ctxt = NULL;
1241}
1242
1243////////////////////////////////////////////////////////////////////////////////
1244//
1245// XmlFileParser class
1246//
1247////////////////////////////////////////////////////////////////////////////////
1248
1249struct XmlFileParser::Data
1250{
1251 xmlParserCtxtPtr ctxt;
1252 iprt::MiniString strXmlFilename;
1253
1254 Data()
1255 {
1256 if (!(ctxt = xmlNewParserCtxt()))
1257 throw std::bad_alloc();
1258 }
1259
1260 ~Data()
1261 {
1262 xmlFreeParserCtxt(ctxt);
1263 ctxt = NULL;
1264 }
1265};
1266
1267XmlFileParser::XmlFileParser()
1268 : XmlParserBase(),
1269 m(new Data())
1270{
1271}
1272
1273XmlFileParser::~XmlFileParser()
1274{
1275 delete m;
1276 m = NULL;
1277}
1278
1279struct IOContext
1280{
1281 File file;
1282 iprt::MiniString error;
1283
1284 IOContext(const char *pcszFilename, File::Mode mode)
1285 : file(mode, pcszFilename)
1286 {
1287 }
1288
1289 void setError(const xml::Error &x)
1290 {
1291 error = x.what();
1292 }
1293
1294 void setError(const std::exception &x)
1295 {
1296 error = x.what();
1297 }
1298};
1299
1300struct ReadContext : IOContext
1301{
1302 ReadContext(const char *pcszFilename)
1303 : IOContext(pcszFilename, File::Mode_Read)
1304 {
1305 }
1306};
1307
1308struct WriteContext : IOContext
1309{
1310 WriteContext(const char *pcszFilename)
1311 : IOContext(pcszFilename, File::Mode_Overwrite)
1312 {
1313 }
1314};
1315
1316/**
1317 * Reads the given file and fills the given Document object with its contents.
1318 * Throws XmlError on parsing errors.
1319 *
1320 * The document that is passed in will be reset before being filled if not empty.
1321 *
1322 * @param pcszFilename in: name fo file to parse.
1323 * @param doc out: document to be reset and filled with data according to file contents.
1324 */
1325void XmlFileParser::read(const iprt::MiniString &strFilename,
1326 Document &doc)
1327{
1328 GlobalLock lock;
1329// global.setExternalEntityLoader(ExternalEntityLoader);
1330
1331 m->strXmlFilename = strFilename;
1332 const char *pcszFilename = strFilename.c_str();
1333
1334 ReadContext context(pcszFilename);
1335 doc.m->reset();
1336 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1337 ReadCallback,
1338 CloseCallback,
1339 &context,
1340 pcszFilename,
1341 NULL, // encoding = auto
1342 XML_PARSE_NOBLANKS)))
1343 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1344
1345 doc.refreshInternals();
1346}
1347
1348// static
1349int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1350{
1351 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1352
1353 /* To prevent throwing exceptions while inside libxml2 code, we catch
1354 * them and forward to our level using a couple of variables. */
1355
1356 try
1357 {
1358 return pContext->file.read(aBuf, aLen);
1359 }
1360 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1361 catch (const xml::Error &err) { pContext->setError(err); }
1362 catch (const std::exception &err) { pContext->setError(err); }
1363 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1364
1365 return -1 /* failure */;
1366}
1367
1368int XmlFileParser::CloseCallback(void *aCtxt)
1369{
1370 /// @todo to be written
1371
1372 return -1;
1373}
1374
1375////////////////////////////////////////////////////////////////////////////////
1376//
1377// XmlFileWriter class
1378//
1379////////////////////////////////////////////////////////////////////////////////
1380
1381struct XmlFileWriter::Data
1382{
1383 Document *pDoc;
1384};
1385
1386XmlFileWriter::XmlFileWriter(Document &doc)
1387{
1388 m = new Data();
1389 m->pDoc = &doc;
1390}
1391
1392XmlFileWriter::~XmlFileWriter()
1393{
1394 delete m;
1395}
1396
1397void XmlFileWriter::write(const char *pcszFilename)
1398{
1399 WriteContext context(pcszFilename);
1400
1401 GlobalLock lock;
1402
1403 /* serialize to the stream */
1404 xmlIndentTreeOutput = 1;
1405 xmlTreeIndentString = " ";
1406 xmlSaveNoEmptyTags = 0;
1407
1408 xmlSaveCtxtPtr saveCtxt;
1409 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1410 CloseCallback,
1411 &context,
1412 NULL,
1413 XML_SAVE_FORMAT)))
1414 throw xml::LogicError(RT_SRC_POS);
1415
1416 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1417 if (rc == -1)
1418 {
1419 /* look if there was a forwared exception from the lower level */
1420// if (m->trappedErr.get() != NULL)
1421// m->trappedErr->rethrow();
1422
1423 /* there must be an exception from the Output implementation,
1424 * otherwise the save operation must always succeed. */
1425 throw xml::LogicError(RT_SRC_POS);
1426 }
1427
1428 xmlSaveClose(saveCtxt);
1429}
1430
1431int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1432{
1433 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1434
1435 /* To prevent throwing exceptions while inside libxml2 code, we catch
1436 * them and forward to our level using a couple of variables. */
1437 try
1438 {
1439 return pContext->file.write(aBuf, aLen);
1440 }
1441 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1442 catch (const xml::Error &err) { pContext->setError(err); }
1443 catch (const std::exception &err) { pContext->setError(err); }
1444 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1445
1446 return -1 /* failure */;
1447}
1448
1449int XmlFileWriter::CloseCallback(void *aCtxt)
1450{
1451 /// @todo to be written
1452
1453 return -1;
1454}
1455
1456
1457} // end namespace xml
1458
1459
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette