VirtualBox

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

Last change on this file since 65008 was 65008, checked in by vboxsync, 8 years ago

r3/xml.cpp: Warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.8 KB
Line 
1/* $Id: xml.cpp 65008 2016-12-23 21:30:08Z vboxsync $ */
2/** @file
3 * IPRT - XML Manipulation API.
4 */
5
6/*
7 * Copyright (C) 2007-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/dir.h>
32#include <iprt/file.h>
33#include <iprt/err.h>
34#include <iprt/param.h>
35#include <iprt/path.h>
36#include <iprt/cpp/lock.h>
37#include <iprt/cpp/xml.h>
38
39#include <libxml/tree.h>
40#include <libxml/parser.h>
41#include <libxml/globals.h>
42#include <libxml/xmlIO.h>
43#include <libxml/xmlsave.h>
44#include <libxml/uri.h>
45
46#include <libxml/xmlschemas.h>
47
48#include <map>
49
50
51/*********************************************************************************************************************************
52* Global Variables *
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 initialization and cleanup. There must be only one global variable of
60 * this structure.
61 */
62static class Global
63{
64public:
65
66 Global()
67 {
68 /* Check the parser version. The docs say it will kill the app if
69 * there is a serious version mismatch, but I couldn't find it in the
70 * source code (it only prints the error/warning message to the console) so
71 * let's leave it as is for informational purposes. */
72 LIBXML_TEST_VERSION
73
74 /* Init libxml */
75 xmlInitParser();
76
77 /* Save the default entity resolver before someone has replaced it */
78 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
79 }
80
81 ~Global()
82 {
83 /* Shutdown libxml */
84 xmlCleanupParser();
85 }
86
87 struct
88 {
89 xmlExternalEntityLoader defaultEntityLoader;
90
91 /** Used to provide some thread safety missing in libxml2 (see e.g.
92 * XmlTreeBackend::read()) */
93 RTCLockMtx lock;
94 }
95 sxml; /* XXX naming this xml will break with gcc-3.3 */
96} gGlobal;
97
98
99
100namespace xml
101{
102
103////////////////////////////////////////////////////////////////////////////////
104//
105// Exceptions
106//
107////////////////////////////////////////////////////////////////////////////////
108
109LogicError::LogicError(RT_SRC_POS_DECL)
110 : RTCError(NULL)
111{
112 char *msg = NULL;
113 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
114 pszFunction, pszFile, iLine);
115 setWhat(msg);
116 RTStrFree(msg);
117}
118
119XmlError::XmlError(xmlErrorPtr aErr)
120{
121 if (!aErr)
122 throw EInvalidArg(RT_SRC_POS);
123
124 char *msg = Format(aErr);
125 setWhat(msg);
126 RTStrFree(msg);
127}
128
129/**
130 * Composes a single message for the given error. The caller must free the
131 * returned string using RTStrFree() when no more necessary.
132 */
133/* static */ char *XmlError::Format(xmlErrorPtr aErr)
134{
135 const char *msg = aErr->message ? aErr->message : "<none>";
136 size_t msgLen = strlen(msg);
137 /* strip spaces, trailing EOLs and dot-like char */
138 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
139 --msgLen;
140
141 char *finalMsg = NULL;
142 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
143 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
144
145 return finalMsg;
146}
147
148EIPRTFailure::EIPRTFailure(int aRC, const char *pcszContext, ...)
149 : RuntimeError(NULL),
150 mRC(aRC)
151{
152 char *pszContext2;
153 va_list args;
154 va_start(args, pcszContext);
155 RTStrAPrintfV(&pszContext2, pcszContext, args);
156 va_end(args);
157 char *newMsg;
158 RTStrAPrintf(&newMsg, "%s: %d (%s)", pszContext2, aRC, RTErrGetShort(aRC));
159 setWhat(newMsg);
160 RTStrFree(newMsg);
161 RTStrFree(pszContext2);
162}
163
164////////////////////////////////////////////////////////////////////////////////
165//
166// File Class
167//
168//////////////////////////////////////////////////////////////////////////////
169
170struct File::Data
171{
172 Data()
173 : handle(NIL_RTFILE), opened(false)
174 { }
175
176 RTCString strFileName;
177 RTFILE handle;
178 bool opened : 1;
179 bool flushOnClose : 1;
180};
181
182File::File(Mode aMode, const char *aFileName, bool aFlushIt /* = false */)
183 : m(new Data())
184{
185 m->strFileName = aFileName;
186 m->flushOnClose = aFlushIt;
187
188 uint32_t flags = 0;
189 const char *pcszMode = "???";
190 switch (aMode)
191 {
192 /** @todo change to RTFILE_O_DENY_WRITE where appropriate. */
193 case Mode_Read:
194 flags = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
195 pcszMode = "reading";
196 break;
197 case Mode_WriteCreate: // fail if file exists
198 flags = RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE;
199 pcszMode = "writing";
200 break;
201 case Mode_Overwrite: // overwrite if file exists
202 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE;
203 pcszMode = "overwriting";
204 break;
205 case Mode_ReadWrite:
206 flags = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
207 pcszMode = "reading/writing";
208 break;
209 }
210
211 int vrc = RTFileOpen(&m->handle, aFileName, flags);
212 if (RT_FAILURE(vrc))
213 throw EIPRTFailure(vrc, "Runtime error opening '%s' for %s", aFileName, pcszMode);
214
215 m->opened = true;
216 m->flushOnClose = aFlushIt && (flags & RTFILE_O_ACCESS_MASK) != RTFILE_O_READ;
217}
218
219File::File(RTFILE aHandle, const char *aFileName /* = NULL */, bool aFlushIt /* = false */)
220 : m(new Data())
221{
222 if (aHandle == NIL_RTFILE)
223 throw EInvalidArg(RT_SRC_POS);
224
225 m->handle = aHandle;
226
227 if (aFileName)
228 m->strFileName = aFileName;
229
230 m->flushOnClose = aFlushIt;
231
232 setPos(0);
233}
234
235File::~File()
236{
237 if (m->flushOnClose)
238 {
239 RTFileFlush(m->handle);
240 if (!m->strFileName.isEmpty())
241 RTDirFlushParent(m->strFileName.c_str());
242 }
243
244 if (m->opened)
245 RTFileClose(m->handle);
246 delete m;
247}
248
249const char *File::uri() const
250{
251 return m->strFileName.c_str();
252}
253
254uint64_t File::pos() const
255{
256 uint64_t p = 0;
257 int vrc = RTFileSeek(m->handle, 0, RTFILE_SEEK_CURRENT, &p);
258 if (RT_SUCCESS(vrc))
259 return p;
260
261 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
262}
263
264void File::setPos(uint64_t aPos)
265{
266 uint64_t p = 0;
267 unsigned method = RTFILE_SEEK_BEGIN;
268 int vrc = VINF_SUCCESS;
269
270 /* check if we overflow int64_t and move to INT64_MAX first */
271 if ((int64_t)aPos < 0)
272 {
273 vrc = RTFileSeek(m->handle, INT64_MAX, method, &p);
274 aPos -= (uint64_t)INT64_MAX;
275 method = RTFILE_SEEK_CURRENT;
276 }
277 /* seek the rest */
278 if (RT_SUCCESS(vrc))
279 vrc = RTFileSeek(m->handle, (int64_t) aPos, method, &p);
280 if (RT_SUCCESS(vrc))
281 return;
282
283 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
284}
285
286int File::read(char *aBuf, int aLen)
287{
288 size_t len = aLen;
289 int vrc = RTFileRead(m->handle, aBuf, len, &len);
290 if (RT_SUCCESS(vrc))
291 return (int)len;
292
293 throw EIPRTFailure(vrc, "Runtime error reading from file '%s'", m->strFileName.c_str());
294}
295
296int File::write(const char *aBuf, int aLen)
297{
298 size_t len = aLen;
299 int vrc = RTFileWrite(m->handle, aBuf, len, &len);
300 if (RT_SUCCESS(vrc))
301 return (int)len;
302
303 throw EIPRTFailure(vrc, "Runtime error writing to file '%s'", m->strFileName.c_str());
304}
305
306void File::truncate()
307{
308 int vrc = RTFileSetSize(m->handle, pos());
309 if (RT_SUCCESS(vrc))
310 return;
311
312 throw EIPRTFailure(vrc, "Runtime error truncating file '%s'", m->strFileName.c_str());
313}
314
315////////////////////////////////////////////////////////////////////////////////
316//
317// MemoryBuf Class
318//
319//////////////////////////////////////////////////////////////////////////////
320
321struct MemoryBuf::Data
322{
323 Data()
324 : buf(NULL), len(0), uri(NULL), pos(0) {}
325
326 const char *buf;
327 size_t len;
328 char *uri;
329
330 size_t pos;
331};
332
333MemoryBuf::MemoryBuf(const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
334 : m(new Data())
335{
336 if (aBuf == NULL)
337 throw EInvalidArg(RT_SRC_POS);
338
339 m->buf = aBuf;
340 m->len = aLen;
341 m->uri = RTStrDup(aURI);
342}
343
344MemoryBuf::~MemoryBuf()
345{
346 RTStrFree(m->uri);
347}
348
349const char *MemoryBuf::uri() const
350{
351 return m->uri;
352}
353
354uint64_t MemoryBuf::pos() const
355{
356 return m->pos;
357}
358
359void MemoryBuf::setPos(uint64_t aPos)
360{
361 size_t off = (size_t)aPos;
362 if ((uint64_t) off != aPos)
363 throw EInvalidArg();
364
365 if (off > m->len)
366 throw EInvalidArg();
367
368 m->pos = off;
369}
370
371int MemoryBuf::read(char *aBuf, int aLen)
372{
373 if (m->pos >= m->len)
374 return 0 /* nothing to read */;
375
376 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
377 memcpy(aBuf, m->buf + m->pos, len);
378 m->pos += len;
379
380 return (int)len;
381}
382
383////////////////////////////////////////////////////////////////////////////////
384//
385// GlobalLock class
386//
387////////////////////////////////////////////////////////////////////////////////
388
389struct GlobalLock::Data
390{
391 PFNEXTERNALENTITYLOADER pOldLoader;
392 RTCLock lock;
393
394 Data()
395 : pOldLoader(NULL),
396 lock(gGlobal.sxml.lock)
397 {
398 }
399};
400
401GlobalLock::GlobalLock()
402 : m(new Data())
403{
404}
405
406GlobalLock::~GlobalLock()
407{
408 if (m->pOldLoader)
409 xmlSetExternalEntityLoader(m->pOldLoader);
410 delete m;
411 m = NULL;
412}
413
414void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
415{
416 m->pOldLoader = xmlGetExternalEntityLoader();
417 xmlSetExternalEntityLoader(pLoader);
418}
419
420// static
421xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
422 const char *aID,
423 xmlParserCtxt *aCtxt)
424{
425 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
426}
427
428
429
430////////////////////////////////////////////////////////////////////////////////
431//
432// Node class
433//
434////////////////////////////////////////////////////////////////////////////////
435
436Node::Node(EnumType type,
437 Node *pParent,
438 PRTLISTANCHOR pListAnchor,
439 xmlNode *pLibNode,
440 xmlAttr *pLibAttr)
441 : m_Type(type)
442 , m_pParent(pParent)
443 , m_pLibNode(pLibNode)
444 , m_pLibAttr(pLibAttr)
445 , m_pcszNamespacePrefix(NULL)
446 , m_pcszNamespaceHref(NULL)
447 , m_pcszName(NULL)
448 , m_pParentListAnchor(pListAnchor)
449{
450 RTListInit(&m_listEntry);
451}
452
453Node::~Node()
454{
455}
456
457/**
458 * Returns the name of the node, which is either the element name or
459 * the attribute name. For other node types it probably returns NULL.
460 * @return
461 */
462const char *Node::getName() const
463{
464 return m_pcszName;
465}
466
467/**
468 * Returns the name of the node, which is either the element name or
469 * the attribute name. For other node types it probably returns NULL.
470 * @return
471 */
472const char *Node::getPrefix() const
473{
474 return m_pcszNamespacePrefix;
475}
476
477/**
478 * Returns the XML namespace URI, which is the attribute name. For other node types it probably
479 * returns NULL.
480 * @return
481 */
482const char *Node::getNamespaceURI() const
483{
484 return m_pcszNamespaceHref;
485}
486
487/**
488 * Variant of nameEquals that checks the namespace as well.
489 * @param pcszNamespace
490 * @param pcsz
491 * @return
492 */
493bool Node::nameEqualsNS(const char *pcszNamespace, const char *pcsz) const
494{
495 if (m_pcszName == pcsz)
496 return true;
497 if (m_pcszName == NULL)
498 return false;
499 if (pcsz == NULL)
500 return false;
501 if (strcmp(m_pcszName, pcsz))
502 return false;
503
504 // name matches: then check namespaces as well
505 if (!pcszNamespace)
506 return true;
507 // caller wants namespace:
508 if (!m_pcszNamespacePrefix)
509 // but node has no namespace:
510 return false;
511 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
512}
513
514/**
515 * Variant of nameEquals that checks the namespace as well.
516 *
517 * @returns true if equal, false if not.
518 * @param pcsz The element name.
519 * @param cchMax The maximum number of character from @a pcsz to
520 * match.
521 * @param pcszNamespace The name space prefix or NULL (default).
522 */
523bool Node::nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace /* = NULL*/) const
524{
525 /* Match the name. */
526 if (!m_pcszName)
527 return false;
528 if (!pcsz || cchMax == 0)
529 return false;
530 if (strncmp(m_pcszName, pcsz, cchMax))
531 return false;
532 if (strlen(m_pcszName) > cchMax)
533 return false;
534
535 /* Match name space. */
536 if (!pcszNamespace)
537 return true; /* NULL, anything goes. */
538 if (!m_pcszNamespacePrefix)
539 return false; /* Element has no namespace. */
540 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
541}
542
543/**
544 * Returns the value of a node. If this node is an attribute, returns
545 * the attribute value; if this node is an element, then this returns
546 * the element text content.
547 * @return
548 */
549const char *Node::getValue() const
550{
551 if ( m_pLibAttr
552 && m_pLibAttr->children
553 )
554 // libxml hides attribute values in another node created as a
555 // single child of the attribute node, and it's in the content field
556 return (const char *)m_pLibAttr->children->content;
557
558 if ( m_pLibNode
559 && m_pLibNode->children)
560 return (const char *)m_pLibNode->children->content;
561
562 return NULL;
563}
564
565/**
566 * Copies the value of a node into the given integer variable.
567 * Returns TRUE only if a value was found and was actually an
568 * integer of the given type.
569 * @return
570 */
571bool Node::copyValue(int32_t &i) const
572{
573 const char *pcsz;
574 if ( ((pcsz = getValue()))
575 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
576 )
577 return true;
578
579 return false;
580}
581
582/**
583 * Copies the value of a node into the given integer variable.
584 * Returns TRUE only if a value was found and was actually an
585 * integer of the given type.
586 * @return
587 */
588bool Node::copyValue(uint32_t &i) const
589{
590 const char *pcsz;
591 if ( ((pcsz = getValue()))
592 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
593 )
594 return true;
595
596 return false;
597}
598
599/**
600 * Copies the value of a node into the given integer variable.
601 * Returns TRUE only if a value was found and was actually an
602 * integer of the given type.
603 * @return
604 */
605bool Node::copyValue(int64_t &i) const
606{
607 const char *pcsz;
608 if ( ((pcsz = getValue()))
609 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
610 )
611 return true;
612
613 return false;
614}
615
616/**
617 * Copies the value of a node into the given integer variable.
618 * Returns TRUE only if a value was found and was actually an
619 * integer of the given type.
620 * @return
621 */
622bool Node::copyValue(uint64_t &i) const
623{
624 const char *pcsz;
625 if ( ((pcsz = getValue()))
626 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
627 )
628 return true;
629
630 return false;
631}
632
633/**
634 * Returns the line number of the current node in the source XML file.
635 * Useful for error messages.
636 * @return
637 */
638int Node::getLineNumber() const
639{
640 if (m_pLibAttr)
641 return m_pParent->m_pLibNode->line;
642
643 return m_pLibNode->line;
644}
645
646/**
647 * Private element constructor.
648 *
649 * @param pElmRoot Pointer to the root element.
650 * @param pParent Pointer to the parent element (always an ElementNode,
651 * despite the type). NULL for the root node.
652 * @param pListAnchor Pointer to the m_children member of the parent. NULL
653 * for the root node.
654 * @param pLibNode Pointer to the libxml2 node structure.
655 */
656ElementNode::ElementNode(const ElementNode *pElmRoot,
657 Node *pParent,
658 PRTLISTANCHOR pListAnchor,
659 xmlNode *pLibNode)
660 : Node(IsElement,
661 pParent,
662 pListAnchor,
663 pLibNode,
664 NULL)
665{
666 m_pElmRoot = pElmRoot ? pElmRoot : this; // If NULL is passed, then this is the root element.
667 m_pcszName = (const char *)pLibNode->name;
668
669 if (pLibNode->ns)
670 {
671 m_pcszNamespacePrefix = (const char *)m_pLibNode->ns->prefix;
672 m_pcszNamespaceHref = (const char *)m_pLibNode->ns->href;
673 }
674
675 RTListInit(&m_children);
676 RTListInit(&m_attributes);
677}
678
679ElementNode::~ElementNode()
680{
681 Node *pCur, *pNext;
682 RTListForEachSafeCpp(&m_children, pCur, pNext, Node, m_listEntry)
683 {
684 delete pCur;
685 }
686 RTListInit(&m_children);
687
688 RTListForEachSafeCpp(&m_attributes, pCur, pNext, Node, m_listEntry)
689 {
690 delete pCur;
691 }
692 RTListInit(&m_attributes);
693}
694
695
696/**
697 * Gets the next tree element in a full tree enumeration.
698 *
699 * @returns Pointer to the next element in the tree, NULL if we're done.
700 * @param pElmRoot The root of the tree we're enumerating. NULL if
701 * it's the entire tree.
702 */
703ElementNode const *ElementNode::getNextTreeElement(ElementNode const *pElmRoot /*= NULL */) const
704{
705 /*
706 * Consider children first.
707 */
708 ElementNode const *pChild = getFirstChildElement();
709 if (pChild)
710 return pChild;
711
712 /*
713 * Then siblings, aunts and uncles.
714 */
715 ElementNode const *pCur = this;
716 do
717 {
718 ElementNode const *pSibling = pCur->getNextSibilingElement();
719 if (pSibling != NULL)
720 return pSibling;
721
722 pCur = static_cast<const xml::ElementNode *>(pCur->m_pParent);
723 Assert(pCur || pCur == pElmRoot);
724 } while (pCur != pElmRoot);
725
726 return NULL;
727}
728
729
730/**
731 * Private implementation.
732 *
733 * @param pElmRoot The root element.
734 */
735/*static*/ void ElementNode::buildChildren(ElementNode *pElmRoot) // protected
736{
737 for (ElementNode *pCur = pElmRoot; pCur; pCur = pCur->getNextTreeElement(pElmRoot))
738 {
739 /*
740 * Go thru this element's attributes creating AttributeNodes for them.
741 */
742 for (xmlAttr *pLibAttr = pCur->m_pLibNode->properties; pLibAttr; pLibAttr = pLibAttr->next)
743 {
744 AttributeNode *pNew = new AttributeNode(pElmRoot, pCur, &pCur->m_attributes, pLibAttr);
745 RTListAppend(&pCur->m_attributes, &pNew->m_listEntry);
746 }
747
748 /*
749 * Go thru this element's child elements (element and text nodes).
750 */
751 for (xmlNodePtr pLibNode = pCur->m_pLibNode->children; pLibNode; pLibNode = pLibNode->next)
752 {
753 Node *pNew;
754 if (pLibNode->type == XML_ELEMENT_NODE)
755 pNew = new ElementNode(pElmRoot, pCur, &pCur->m_children, pLibNode);
756 else if (pLibNode->type == XML_TEXT_NODE)
757 pNew = new ContentNode(pCur, &pCur->m_children, pLibNode);
758 else
759 continue;
760 RTListAppend(&pCur->m_children, &pNew->m_listEntry);
761 }
762 }
763}
764
765
766/**
767 * Builds a list of direct child elements of the current element that
768 * match the given string; if pcszMatch is NULL, all direct child
769 * elements are returned.
770 * @param children out: list of nodes to which children will be appended.
771 * @param pcszMatch in: match string, or NULL to return all children.
772 * @return Number of items appended to the list (0 if none).
773 */
774int ElementNode::getChildElements(ElementNodesList &children,
775 const char *pcszMatch /*= NULL*/)
776 const
777{
778 int i = 0;
779 Node *p;
780 RTListForEachCpp(&m_children, p, Node, m_listEntry)
781 {
782 // export this child node if ...
783 if (p->isElement())
784 if ( !pcszMatch // ... the caller wants all nodes or ...
785 || !strcmp(pcszMatch, p->getName()) // ... the element name matches.
786 )
787 {
788 children.push_back(static_cast<ElementNode *>(p));
789 ++i;
790 }
791 }
792 return i;
793}
794
795/**
796 * Returns the first child element whose name matches pcszMatch.
797 *
798 * @param pcszNamespace Namespace prefix (e.g. "vbox") or NULL to match any namespace.
799 * @param pcszMatch Element name to match.
800 * @return
801 */
802const ElementNode *ElementNode::findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const
803{
804 Node *p;
805 RTListForEachCpp(&m_children, p, Node, m_listEntry)
806 {
807 if (p->isElement())
808 {
809 ElementNode *pelm = static_cast<ElementNode*>(p);
810 if (pelm->nameEqualsNS(pcszNamespace, pcszMatch))
811 return pelm;
812 }
813 }
814 return NULL;
815}
816
817/**
818 * Returns the first child element whose "id" attribute matches pcszId.
819 * @param pcszId identifier to look for.
820 * @return child element or NULL if not found.
821 */
822const ElementNode *ElementNode::findChildElementFromId(const char *pcszId) const
823{
824 const Node *p;
825 RTListForEachCpp(&m_children, p, Node, m_listEntry)
826 {
827 if (p->isElement())
828 {
829 const ElementNode *pElm = static_cast<const ElementNode *>(p);
830 const AttributeNode *pAttr = pElm->findAttribute("id");
831 if (pAttr && !strcmp(pAttr->getValue(), pcszId))
832 return pElm;
833 }
834 }
835 return NULL;
836}
837
838
839const ElementNode *ElementNode::findChildElementP(const char *pcszPath, const char *pcszNamespace /*= NULL*/) const
840{
841 size_t cchThis = strchr(pcszPath, '/') - pcszPath;
842 if (cchThis == (size_t)((const char *)0 - pcszPath))
843 return findChildElementNS(pcszNamespace, pcszPath);
844
845 /** @todo Can be done without recursion as we have both sibling lists and parent
846 * pointers in this variant. */
847 const Node *p;
848 RTListForEachCpp(&m_children, p, Node, m_listEntry)
849 {
850 if (p->isElement())
851 {
852 const ElementNode *pElm = static_cast<const ElementNode *>(p);
853 if (pElm->nameEqualsN(pcszPath, cchThis, pcszNamespace))
854 {
855 pElm = findChildElementP(pcszPath + cchThis, pcszNamespace);
856 if (pElm)
857 return pElm;
858 }
859 }
860 }
861
862 return NULL;
863}
864
865const ElementNode *ElementNode::getFirstChildElement() const
866{
867 const Node *p;
868 RTListForEachCpp(&m_children, p, Node, m_listEntry)
869 {
870 if (p->isElement())
871 return static_cast<const ElementNode *>(p);
872 }
873 return NULL;
874}
875
876const ElementNode *ElementNode::getLastChildElement() const
877{
878 const Node *p;
879 RTListForEachReverseCpp(&m_children, p, Node, m_listEntry)
880 {
881 if (p->isElement())
882 return static_cast<const ElementNode *>(p);
883 }
884 return NULL;
885}
886
887const ElementNode *ElementNode::getPrevSibilingElement() const
888{
889 if (!m_pParent)
890 return NULL;
891 const Node *pSibling = this;
892 for (;;)
893 {
894 pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
895 if (!pSibling)
896 return NULL;
897 if (pSibling->isElement())
898 return static_cast<const ElementNode *>(pSibling);
899 }
900}
901
902const ElementNode *ElementNode::getNextSibilingElement() const
903{
904 if (!m_pParent)
905 return NULL;
906 const Node *pSibling = this;
907 for (;;)
908 {
909 pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
910 if (!pSibling)
911 return NULL;
912 if (pSibling->isElement())
913 return static_cast<const ElementNode *>(pSibling);
914 }
915}
916
917const ElementNode *ElementNode::findPrevSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
918{
919 if (!m_pParent)
920 return NULL;
921 const Node *pSibling = this;
922 for (;;)
923 {
924 pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
925 if (!pSibling)
926 return NULL;
927 if (pSibling->isElement())
928 {
929 const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
930 if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
931 return pElem;
932 }
933 }
934}
935
936const ElementNode *ElementNode::findNextSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
937{
938 if (!m_pParent)
939 return NULL;
940 const Node *pSibling = this;
941 for (;;)
942 {
943 pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
944 if (!pSibling)
945 return NULL;
946 if (pSibling->isElement())
947 {
948 const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
949 if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
950 return pElem;
951 }
952 }
953}
954
955
956/**
957 * Looks up the given attribute node in this element's attribute map.
958 *
959 * @param pcszMatch The name of the attribute to find.
960 * @param pcszNamespace The attribute name space prefix or NULL.
961 */
962const AttributeNode *ElementNode::findAttribute(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
963{
964 AttributeNode *p;
965 RTListForEachCpp(&m_attributes, p, AttributeNode, m_listEntry)
966 {
967 if (p->nameEqualsNS(pcszNamespace, pcszMatch))
968 return p;
969 }
970 return NULL;
971}
972
973/**
974 * Convenience method which attempts to find the attribute with the given
975 * name and returns its value as a string.
976 *
977 * @param pcszMatch Name of attribute to find.
978 * @param ppcsz Where to return the attribute.
979 * @param pcszNamespace The attribute name space prefix or NULL.
980 * @returns Boolean success indicator.
981 */
982bool ElementNode::getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace /*= NULL*/) const
983{
984 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
985 if (pAttr)
986 {
987 *ppcsz = pAttr->getValue();
988 return true;
989 }
990 return false;
991}
992
993/**
994 * Convenience method which attempts to find the attribute with the given
995 * name and returns its value as a string.
996 *
997 * @param pcszMatch Name of attribute to find.
998 * @param pStr Pointer to the string object that should receive the
999 * attribute value.
1000 * @param pcszNamespace The attribute name space prefix or NULL.
1001 * @returns Boolean success indicator.
1002 *
1003 * @throws Whatever the string class may throw on assignment.
1004 */
1005bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
1006{
1007 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
1008 if (pAttr)
1009 {
1010 *pStr = pAttr->getValue();
1011 return true;
1012 }
1013
1014 return false;
1015}
1016
1017/**
1018 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
1019 * are converted to forward slashes.
1020 *
1021 * @param pcszMatch Name of attribute to find.
1022 * @param pStr Pointer to the string object that should
1023 * receive the attribute path value.
1024 * @param pcszNamespace The attribute name space prefix or NULL.
1025 * @returns Boolean success indicator.
1026 */
1027bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
1028{
1029 if (getAttributeValue(pcszMatch, pStr, pcszNamespace))
1030 {
1031 pStr->findReplace('\\', '/');
1032 return true;
1033 }
1034
1035 return false;
1036}
1037
1038/**
1039 * Convenience method which attempts to find the attribute with the given
1040 * name and returns its value as a signed 32-bit integer.
1041 *
1042 * @param pcszMatch Name of attribute to find.
1043 * @param piValue Where to return the value.
1044 * @param pcszNamespace The attribute name space prefix or NULL.
1045 * @returns Boolean success indicator.
1046 */
1047bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t *piValue, const char *pcszNamespace /*= NULL*/) const
1048{
1049 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1050 if (pcsz)
1051 {
1052 int rc = RTStrToInt32Ex(pcsz, NULL, 0, piValue);
1053 if (rc == VINF_SUCCESS)
1054 return true;
1055 }
1056 return false;
1057}
1058
1059/**
1060 * Convenience method which attempts to find the attribute with the given
1061 * name and returns its value as an unsigned 32-bit integer.
1062 *
1063 * @param pcszMatch Name of attribute to find.
1064 * @param puValue Where to return the value.
1065 * @param pcszNamespace The attribute name space prefix or NULL.
1066 * @returns Boolean success indicator.
1067 */
1068bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t *puValue, const char *pcszNamespace /*= NULL*/) const
1069{
1070 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1071 if (pcsz)
1072 {
1073 int rc = RTStrToUInt32Ex(pcsz, NULL, 0, puValue);
1074 if (rc == VINF_SUCCESS)
1075 return true;
1076 }
1077 return false;
1078}
1079
1080/**
1081 * Convenience method which attempts to find the attribute with the given
1082 * name and returns its value as a signed 64-bit integer.
1083 *
1084 * @param pcszMatch Name of attribute to find.
1085 * @param piValue Where to return the value.
1086 * @param pcszNamespace The attribute name space prefix or NULL.
1087 * @returns Boolean success indicator.
1088 */
1089bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace /*= NULL*/) const
1090{
1091 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1092 if (pcsz)
1093 {
1094 int rc = RTStrToInt64Ex(pcsz, NULL, 0, piValue);
1095 if (rc == VINF_SUCCESS)
1096 return true;
1097 }
1098 return false;
1099}
1100
1101/**
1102 * Convenience method which attempts to find the attribute with the given
1103 * name and returns its value as an unsigned 64-bit integer.
1104 *
1105 * @param pcszMatch Name of attribute to find.
1106 * @param puValue Where to return the value.
1107 * @param pcszNamespace The attribute name space prefix or NULL.
1108 * @returns Boolean success indicator.
1109 */
1110bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t *puValue, const char *pcszNamespace /*= NULL*/) const
1111{
1112 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1113 if (pcsz)
1114 {
1115 int rc = RTStrToUInt64Ex(pcsz, NULL, 0, puValue);
1116 if (rc == VINF_SUCCESS)
1117 return true;
1118 }
1119 return false;
1120}
1121
1122/**
1123 * Convenience method which attempts to find the attribute with the given
1124 * name and returns its value as a boolean. This accepts "true", "false",
1125 * "yes", "no", "1" or "0" as valid values.
1126 *
1127 * @param pcszMatch Name of attribute to find.
1128 * @param pfValue Where to return the value.
1129 * @param pcszNamespace The attribute name space prefix or NULL.
1130 * @returns Boolean success indicator.
1131 */
1132bool ElementNode::getAttributeValue(const char *pcszMatch, bool *pfValue, const char *pcszNamespace /*= NULL*/) const
1133{
1134 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1135 if (pcsz)
1136 {
1137 if ( !strcmp(pcsz, "true")
1138 || !strcmp(pcsz, "yes")
1139 || !strcmp(pcsz, "1")
1140 )
1141 {
1142 *pfValue = true;
1143 return true;
1144 }
1145 if ( !strcmp(pcsz, "false")
1146 || !strcmp(pcsz, "no")
1147 || !strcmp(pcsz, "0")
1148 )
1149 {
1150 *pfValue = false;
1151 return true;
1152 }
1153 }
1154
1155 return false;
1156}
1157
1158
1159bool ElementNode::getElementValue(int32_t *piValue) const
1160{
1161 const char *pszValue = getValue();
1162 if (pszValue)
1163 {
1164 int rc = RTStrToInt32Ex(pszValue, NULL, 0, piValue);
1165 if (rc == VINF_SUCCESS)
1166 return true;
1167 }
1168 return false;
1169}
1170
1171bool ElementNode::getElementValue(uint32_t *puValue) const
1172{
1173 const char *pszValue = getValue();
1174 if (pszValue)
1175 {
1176 int rc = RTStrToUInt32Ex(pszValue, NULL, 0, puValue);
1177 if (rc == VINF_SUCCESS)
1178 return true;
1179 }
1180 return false;
1181}
1182
1183bool ElementNode::getElementValue(int64_t *piValue) const
1184{
1185 const char *pszValue = getValue();
1186 if (pszValue)
1187 {
1188 int rc = RTStrToInt64Ex(pszValue, NULL, 0, piValue);
1189 if (rc == VINF_SUCCESS)
1190 return true;
1191 }
1192 return false;
1193}
1194
1195bool ElementNode::getElementValue(uint64_t *puValue) const
1196{
1197 const char *pszValue = getValue();
1198 if (pszValue)
1199 {
1200 int rc = RTStrToUInt64Ex(pszValue, NULL, 0, puValue);
1201 if (rc == VINF_SUCCESS)
1202 return true;
1203 }
1204 return false;
1205}
1206
1207bool ElementNode::getElementValue(bool *pfValue) const
1208{
1209 const char *pszValue = getValue();
1210 if (pszValue)
1211 {
1212 if ( !strcmp(pszValue, "true")
1213 || !strcmp(pszValue, "yes")
1214 || !strcmp(pszValue, "1")
1215 )
1216 {
1217 *pfValue = true;
1218 return true;
1219 }
1220 if ( !strcmp(pszValue, "false")
1221 || !strcmp(pszValue, "no")
1222 || !strcmp(pszValue, "0")
1223 )
1224 {
1225 *pfValue = true;
1226 return true;
1227 }
1228 }
1229 return false;
1230}
1231
1232
1233/**
1234 * Creates a new child element node and appends it to the list
1235 * of children in "this".
1236 *
1237 * @param pcszElementName
1238 * @return
1239 */
1240ElementNode *ElementNode::createChild(const char *pcszElementName)
1241{
1242 // we must be an element, not an attribute
1243 if (!m_pLibNode)
1244 throw ENodeIsNotElement(RT_SRC_POS);
1245
1246 // libxml side: create new node
1247 xmlNode *pLibNode;
1248 if (!(pLibNode = xmlNewNode(NULL, // namespace
1249 (const xmlChar*)pcszElementName)))
1250 throw std::bad_alloc();
1251 xmlAddChild(m_pLibNode, pLibNode);
1252
1253 // now wrap this in C++
1254 ElementNode *p = new ElementNode(m_pElmRoot, this, &m_children, pLibNode);
1255 RTListAppend(&m_children, &p->m_listEntry);
1256
1257 return p;
1258}
1259
1260
1261/**
1262 * Creates a content node and appends it to the list of children
1263 * in "this".
1264 *
1265 * @param pcszContent
1266 * @return
1267 */
1268ContentNode *ElementNode::addContent(const char *pcszContent)
1269{
1270 // libxml side: create new node
1271 xmlNode *pLibNode = xmlNewText((const xmlChar*)pcszContent);
1272 if (!pLibNode)
1273 throw std::bad_alloc();
1274 xmlAddChild(m_pLibNode, pLibNode);
1275
1276 // now wrap this in C++
1277 ContentNode *p = new ContentNode(this, &m_children, pLibNode);
1278 RTListAppend(&m_children, &p->m_listEntry);
1279
1280 return p;
1281}
1282
1283/**
1284 * Changes the contents of node and appends it to the list of
1285 * children
1286 *
1287 * @param pcszContent
1288 * @return
1289 */
1290ContentNode *ElementNode::setContent(const char *pcszContent)
1291{
1292// 1. Update content
1293 xmlNodeSetContent(m_pLibNode, (const xmlChar*)pcszContent);
1294
1295// 2. Remove Content node from the list
1296 /* Check that the order is right. */
1297 xml::Node * pNode;
1298 RTListForEachCpp(&m_children, pNode, xml::Node, m_listEntry)
1299 {
1300 bool fLast = RTListNodeIsLast(&m_children, &pNode->m_listEntry);
1301
1302 if (pNode->isContent())
1303 {
1304 RTListNodeRemove(&pNode->m_listEntry);
1305 }
1306
1307 if (fLast)
1308 break;
1309 }
1310
1311// 3. Create a new node and append to the list
1312 // now wrap this in C++
1313 ContentNode *pCNode = new ContentNode(this, &m_children, m_pLibNode);
1314 RTListAppend(&m_children, &pCNode->m_listEntry);
1315
1316 return pCNode;
1317}
1318
1319/**
1320 * Sets the given attribute; overloaded version for const char *.
1321 *
1322 * If an attribute with the given name exists, it is overwritten,
1323 * otherwise a new attribute is created. Returns the attribute node
1324 * that was either created or changed.
1325 *
1326 * @param pcszName The attribute name.
1327 * @param pcszValue The attribute value.
1328 * @return Pointer to the attribute node that was created or modified.
1329 */
1330AttributeNode *ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
1331{
1332 /*
1333 * Do we already have an attribute and should we just update it?
1334 */
1335 AttributeNode *pAttr;
1336 RTListForEachCpp(&m_attributes, pAttr, AttributeNode, m_listEntry)
1337 {
1338 if (pAttr->nameEquals(pcszName))
1339 {
1340 /* Overwrite existing libxml attribute node ... */
1341 xmlAttrPtr pLibAttr = xmlSetProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
1342
1343 /* ... and update our C++ wrapper in case the attrib pointer changed. */
1344 pAttr->m_pLibAttr = pLibAttr;
1345 return pAttr;
1346 }
1347 }
1348
1349 /*
1350 * No existing attribute, create a new one.
1351 */
1352 /* libxml side: xmlNewProp creates an attribute. */
1353 xmlAttr *pLibAttr = xmlNewProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
1354
1355 /* C++ side: Create an attribute node around it. */
1356 pAttr = new AttributeNode(m_pElmRoot, this, &m_attributes, pLibAttr);
1357 RTListAppend(&m_attributes, &pAttr->m_listEntry);
1358
1359 return pAttr;
1360}
1361
1362/**
1363 * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
1364 * before calling that one.
1365 * @param pcszName
1366 * @param strValue
1367 * @return
1368 */
1369AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
1370{
1371 RTCString strTemp(strValue);
1372 strTemp.findReplace('\\', '/');
1373 return setAttribute(pcszName, strTemp.c_str());
1374}
1375
1376/**
1377 * Sets the given attribute; overloaded version for int32_t.
1378 *
1379 * If an attribute with the given name exists, it is overwritten,
1380 * otherwise a new attribute is created. Returns the attribute node
1381 * that was either created or changed.
1382 *
1383 * @param pcszName
1384 * @param i
1385 * @return
1386 */
1387AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
1388{
1389 char szValue[12]; // negative sign + 10 digits + \0
1390 RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
1391 AttributeNode *p = setAttribute(pcszName, szValue);
1392 return p;
1393}
1394
1395/**
1396 * Sets the given attribute; overloaded version for uint32_t.
1397 *
1398 * If an attribute with the given name exists, it is overwritten,
1399 * otherwise a new attribute is created. Returns the attribute node
1400 * that was either created or changed.
1401 *
1402 * @param pcszName
1403 * @param u
1404 * @return
1405 */
1406AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
1407{
1408 char szValue[11]; // 10 digits + \0
1409 RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
1410 AttributeNode *p = setAttribute(pcszName, szValue);
1411 return p;
1412}
1413
1414/**
1415 * Sets the given attribute; overloaded version for int64_t.
1416 *
1417 * If an attribute with the given name exists, it is overwritten,
1418 * otherwise a new attribute is created. Returns the attribute node
1419 * that was either created or changed.
1420 *
1421 * @param pcszName
1422 * @param i
1423 * @return
1424 */
1425AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1426{
1427 char szValue[21]; // negative sign + 19 digits + \0
1428 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
1429 AttributeNode *p = setAttribute(pcszName, szValue);
1430 return p;
1431}
1432
1433/**
1434 * Sets the given attribute; overloaded version for uint64_t.
1435 *
1436 * If an attribute with the given name exists, it is overwritten,
1437 * otherwise a new attribute is created. Returns the attribute node
1438 * that was either created or changed.
1439 *
1440 * @param pcszName
1441 * @param u
1442 * @return
1443 */
1444AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
1445{
1446 char szValue[21]; // 20 digits + \0
1447 RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
1448 AttributeNode *p = setAttribute(pcszName, szValue);
1449 return p;
1450}
1451
1452/**
1453 * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
1454 *
1455 * If an attribute with the given name exists, it is overwritten,
1456 * otherwise a new attribute is created. Returns the attribute node
1457 * that was either created or changed.
1458 *
1459 * @param pcszName
1460 * @param u
1461 * @return
1462 */
1463AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
1464{
1465 char szValue[11]; // "0x" + 8 digits + \0
1466 RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
1467 AttributeNode *p = setAttribute(pcszName, szValue);
1468 return p;
1469}
1470
1471/**
1472 * Sets the given attribute; overloaded version for bool.
1473 *
1474 * If an attribute with the given name exists, it is overwritten,
1475 * otherwise a new attribute is created. Returns the attribute node
1476 * that was either created or changed.
1477 *
1478 * @param pcszName The attribute name.
1479 * @param f The attribute value.
1480 * @return
1481 */
1482AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1483{
1484 return setAttribute(pcszName, (f) ? "true" : "false");
1485}
1486
1487/**
1488 * Private constructor for a new attribute node.
1489 *
1490 * @param pElmRoot Pointer to the root element. Needed for getting the
1491 * default name space.
1492 * @param pParent Pointer to the parent element (always an ElementNode,
1493 * despite the type). NULL for the root node.
1494 * @param pListAnchor Pointer to the m_children member of the parent. NULL
1495 * for the root node.
1496 * @param pLibAttr Pointer to the libxml2 attribute structure.
1497 */
1498AttributeNode::AttributeNode(const ElementNode *pElmRoot,
1499 Node *pParent,
1500 PRTLISTANCHOR pListAnchor,
1501 xmlAttr *pLibAttr)
1502 : Node(IsAttribute,
1503 pParent,
1504 pListAnchor,
1505 NULL,
1506 pLibAttr)
1507{
1508 m_pcszName = (const char *)pLibAttr->name;
1509 RT_NOREF_PV(pElmRoot);
1510
1511 if ( pLibAttr->ns
1512 && pLibAttr->ns->prefix)
1513 {
1514 m_pcszNamespacePrefix = (const char *)pLibAttr->ns->prefix;
1515 m_pcszNamespaceHref = (const char *)pLibAttr->ns->href;
1516 }
1517}
1518
1519ContentNode::ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode)
1520 : Node(IsContent,
1521 pParent,
1522 pListAnchor,
1523 pLibNode,
1524 NULL)
1525{
1526}
1527
1528/*
1529 * NodesLoop
1530 *
1531 */
1532
1533struct NodesLoop::Data
1534{
1535 ElementNodesList listElements;
1536 ElementNodesList::const_iterator it;
1537};
1538
1539NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1540{
1541 m = new Data;
1542 node.getChildElements(m->listElements, pcszMatch);
1543 m->it = m->listElements.begin();
1544}
1545
1546NodesLoop::~NodesLoop()
1547{
1548 delete m;
1549}
1550
1551
1552/**
1553 * Handy convenience helper for looping over all child elements. Create an
1554 * instance of NodesLoop on the stack and call this method until it returns
1555 * NULL, like this:
1556 * <code>
1557 * xml::ElementNode node; // should point to an element
1558 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1559 * const xml::ElementNode *pChild = NULL;
1560 * while (pChild = loop.forAllNodes())
1561 * ...;
1562 * </code>
1563 * @return
1564 */
1565const ElementNode* NodesLoop::forAllNodes() const
1566{
1567 const ElementNode *pNode = NULL;
1568
1569 if (m->it != m->listElements.end())
1570 {
1571 pNode = *(m->it);
1572 ++(m->it);
1573 }
1574
1575 return pNode;
1576}
1577
1578////////////////////////////////////////////////////////////////////////////////
1579//
1580// Document class
1581//
1582////////////////////////////////////////////////////////////////////////////////
1583
1584struct Document::Data
1585{
1586 xmlDocPtr plibDocument;
1587 ElementNode *pRootElement;
1588 ElementNode *pComment;
1589
1590 Data()
1591 {
1592 plibDocument = NULL;
1593 pRootElement = NULL;
1594 pComment = NULL;
1595 }
1596
1597 ~Data()
1598 {
1599 reset();
1600 }
1601
1602 void reset()
1603 {
1604 if (plibDocument)
1605 {
1606 xmlFreeDoc(plibDocument);
1607 plibDocument = NULL;
1608 }
1609 if (pRootElement)
1610 {
1611 delete pRootElement;
1612 pRootElement = NULL;
1613 }
1614 if (pComment)
1615 {
1616 delete pComment;
1617 pComment = NULL;
1618 }
1619 }
1620
1621 void copyFrom(const Document::Data *p)
1622 {
1623 if (p->plibDocument)
1624 {
1625 plibDocument = xmlCopyDoc(p->plibDocument,
1626 1); // recursive == copy all
1627 }
1628 }
1629};
1630
1631Document::Document()
1632 : m(new Data)
1633{
1634}
1635
1636Document::Document(const Document &x)
1637 : m(new Data)
1638{
1639 m->copyFrom(x.m);
1640}
1641
1642Document& Document::operator=(const Document &x)
1643{
1644 m->reset();
1645 m->copyFrom(x.m);
1646 return *this;
1647}
1648
1649Document::~Document()
1650{
1651 delete m;
1652}
1653
1654/**
1655 * private method to refresh all internal structures after the internal pDocument
1656 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1657 * called before to make sure all members except the internal pDocument are clean.
1658 */
1659void Document::refreshInternals() // private
1660{
1661 m->pRootElement = new ElementNode(NULL, NULL, NULL, xmlDocGetRootElement(m->plibDocument));
1662
1663 ElementNode::buildChildren(m->pRootElement);
1664}
1665
1666/**
1667 * Returns the root element of the document, or NULL if the document is empty.
1668 * Const variant.
1669 * @return
1670 */
1671const ElementNode *Document::getRootElement() const
1672{
1673 return m->pRootElement;
1674}
1675
1676/**
1677 * Returns the root element of the document, or NULL if the document is empty.
1678 * Non-const variant.
1679 * @return
1680 */
1681ElementNode *Document::getRootElement()
1682{
1683 return m->pRootElement;
1684}
1685
1686/**
1687 * Creates a new element node and sets it as the root element.
1688 *
1689 * This will only work if the document is empty; otherwise EDocumentNotEmpty is
1690 * thrown.
1691 */
1692ElementNode *Document::createRootElement(const char *pcszRootElementName,
1693 const char *pcszComment /* = NULL */)
1694{
1695 if (m->pRootElement || m->plibDocument)
1696 throw EDocumentNotEmpty(RT_SRC_POS);
1697
1698 // libxml side: create document, create root node
1699 m->plibDocument = xmlNewDoc((const xmlChar *)"1.0");
1700 xmlNode *plibRootNode = xmlNewNode(NULL /*namespace*/ , (const xmlChar *)pcszRootElementName);
1701 if (!plibRootNode)
1702 throw std::bad_alloc();
1703 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1704
1705 // now wrap this in C++
1706 m->pRootElement = new ElementNode(NULL, NULL, NULL, plibRootNode);
1707
1708 // add document global comment if specified
1709 if (pcszComment != NULL)
1710 {
1711 xmlNode *pComment = xmlNewDocComment(m->plibDocument, (const xmlChar *)pcszComment);
1712 if (!pComment)
1713 throw std::bad_alloc();
1714 xmlAddPrevSibling(plibRootNode, pComment);
1715
1716 // now wrap this in C++
1717 m->pComment = new ElementNode(NULL, NULL, NULL, pComment);
1718 }
1719
1720 return m->pRootElement;
1721}
1722
1723////////////////////////////////////////////////////////////////////////////////
1724//
1725// XmlParserBase class
1726//
1727////////////////////////////////////////////////////////////////////////////////
1728
1729XmlParserBase::XmlParserBase()
1730{
1731 m_ctxt = xmlNewParserCtxt();
1732 if (m_ctxt == NULL)
1733 throw std::bad_alloc();
1734}
1735
1736XmlParserBase::~XmlParserBase()
1737{
1738 xmlFreeParserCtxt (m_ctxt);
1739 m_ctxt = NULL;
1740}
1741
1742////////////////////////////////////////////////////////////////////////////////
1743//
1744// XmlMemParser class
1745//
1746////////////////////////////////////////////////////////////////////////////////
1747
1748XmlMemParser::XmlMemParser()
1749 : XmlParserBase()
1750{
1751}
1752
1753XmlMemParser::~XmlMemParser()
1754{
1755}
1756
1757/**
1758 * Parse the given buffer and fills the given Document object with its contents.
1759 * Throws XmlError on parsing errors.
1760 *
1761 * The document that is passed in will be reset before being filled if not empty.
1762 *
1763 * @param pvBuf Memory buffer to parse.
1764 * @param cbSize Size of the memory buffer.
1765 * @param strFilename Refernece to the name of the file we're parsing.
1766 * @param doc Reference to the output document. This will be reset
1767 * and filled with data according to file contents.
1768 */
1769void XmlMemParser::read(const void *pvBuf, size_t cbSize,
1770 const RTCString &strFilename,
1771 Document &doc)
1772{
1773 GlobalLock lock;
1774// global.setExternalEntityLoader(ExternalEntityLoader);
1775
1776 const char *pcszFilename = strFilename.c_str();
1777
1778 doc.m->reset();
1779 const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
1780 | XML_PARSE_NONET /* forbit any network access */
1781#if LIBXML_VERSION >= 20700
1782 | XML_PARSE_HUGE /* don't restrict the node depth
1783 to 256 (bad for snapshots!) */
1784#endif
1785 ;
1786 if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
1787 (const char*)pvBuf,
1788 (int)cbSize,
1789 pcszFilename,
1790 NULL, // encoding = auto
1791 options)))
1792 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1793
1794 doc.refreshInternals();
1795}
1796
1797////////////////////////////////////////////////////////////////////////////////
1798//
1799// XmlMemWriter class
1800//
1801////////////////////////////////////////////////////////////////////////////////
1802
1803XmlMemWriter::XmlMemWriter()
1804 : m_pBuf(0)
1805{
1806}
1807
1808XmlMemWriter::~XmlMemWriter()
1809{
1810 if (m_pBuf)
1811 xmlFree(m_pBuf);
1812}
1813
1814void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
1815{
1816 if (m_pBuf)
1817 {
1818 xmlFree(m_pBuf);
1819 m_pBuf = 0;
1820 }
1821 int size;
1822 xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
1823 *ppvBuf = m_pBuf;
1824 *pcbSize = size;
1825}
1826
1827////////////////////////////////////////////////////////////////////////////////
1828//
1829// XmlFileParser class
1830//
1831////////////////////////////////////////////////////////////////////////////////
1832
1833struct XmlFileParser::Data
1834{
1835 RTCString strXmlFilename;
1836
1837 Data()
1838 {
1839 }
1840
1841 ~Data()
1842 {
1843 }
1844};
1845
1846XmlFileParser::XmlFileParser()
1847 : XmlParserBase(),
1848 m(new Data())
1849{
1850}
1851
1852XmlFileParser::~XmlFileParser()
1853{
1854 delete m;
1855 m = NULL;
1856}
1857
1858struct IOContext
1859{
1860 File file;
1861 RTCString error;
1862
1863 IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
1864 : file(mode, pcszFilename, fFlush)
1865 {
1866 }
1867
1868 void setError(const RTCError &x)
1869 {
1870 error = x.what();
1871 }
1872
1873 void setError(const std::exception &x)
1874 {
1875 error = x.what();
1876 }
1877
1878private:
1879 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(IOContext); /* (shuts up C4626 and C4625 MSC warnings) */
1880};
1881
1882struct ReadContext : IOContext
1883{
1884 ReadContext(const char *pcszFilename)
1885 : IOContext(pcszFilename, File::Mode_Read)
1886 {
1887 }
1888
1889private:
1890 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(ReadContext); /* (shuts up C4626 and C4625 MSC warnings) */
1891};
1892
1893struct WriteContext : IOContext
1894{
1895 WriteContext(const char *pcszFilename, bool fFlush)
1896 : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
1897 {
1898 }
1899
1900private:
1901 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteContext); /* (shuts up C4626 and C4625 MSC warnings) */
1902};
1903
1904/**
1905 * Reads the given file and fills the given Document object with its contents.
1906 * Throws XmlError on parsing errors.
1907 *
1908 * The document that is passed in will be reset before being filled if not empty.
1909 *
1910 * @param strFilename in: name fo file to parse.
1911 * @param doc out: document to be reset and filled with data according to file contents.
1912 */
1913void XmlFileParser::read(const RTCString &strFilename,
1914 Document &doc)
1915{
1916 GlobalLock lock;
1917// global.setExternalEntityLoader(ExternalEntityLoader);
1918
1919 m->strXmlFilename = strFilename;
1920 const char *pcszFilename = strFilename.c_str();
1921
1922 ReadContext context(pcszFilename);
1923 doc.m->reset();
1924 const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
1925 | XML_PARSE_NONET /* forbit any network access */
1926#if LIBXML_VERSION >= 20700
1927 | XML_PARSE_HUGE /* don't restrict the node depth
1928 to 256 (bad for snapshots!) */
1929#endif
1930 ;
1931 if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
1932 ReadCallback,
1933 CloseCallback,
1934 &context,
1935 pcszFilename,
1936 NULL, // encoding = auto
1937 options)))
1938 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1939
1940 doc.refreshInternals();
1941}
1942
1943// static
1944int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1945{
1946 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1947
1948 /* To prevent throwing exceptions while inside libxml2 code, we catch
1949 * them and forward to our level using a couple of variables. */
1950
1951 try
1952 {
1953 return pContext->file.read(aBuf, aLen);
1954 }
1955 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1956 catch (const RTCError &err) { pContext->setError(err); }
1957 catch (const std::exception &err) { pContext->setError(err); }
1958 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1959
1960 return -1 /* failure */;
1961}
1962
1963int XmlFileParser::CloseCallback(void *aCtxt)
1964{
1965 /// @todo to be written
1966 NOREF(aCtxt);
1967
1968 return -1;
1969}
1970
1971////////////////////////////////////////////////////////////////////////////////
1972//
1973// XmlFileWriter class
1974//
1975////////////////////////////////////////////////////////////////////////////////
1976
1977struct XmlFileWriter::Data
1978{
1979 Document *pDoc;
1980};
1981
1982XmlFileWriter::XmlFileWriter(Document &doc)
1983{
1984 m = new Data();
1985 m->pDoc = &doc;
1986}
1987
1988XmlFileWriter::~XmlFileWriter()
1989{
1990 delete m;
1991}
1992
1993void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
1994{
1995 WriteContext context(pcszFilename, fSafe);
1996
1997 GlobalLock lock;
1998
1999 /* serialize to the stream */
2000 xmlIndentTreeOutput = 1;
2001 xmlTreeIndentString = " ";
2002 xmlSaveNoEmptyTags = 0;
2003
2004 xmlSaveCtxtPtr saveCtxt;
2005 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
2006 CloseCallback,
2007 &context,
2008 NULL,
2009 XML_SAVE_FORMAT)))
2010 throw xml::LogicError(RT_SRC_POS);
2011
2012 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
2013 if (rc == -1)
2014 {
2015 /* look if there was a forwarded exception from the lower level */
2016// if (m->trappedErr.get() != NULL)
2017// m->trappedErr->rethrow();
2018
2019 /* there must be an exception from the Output implementation,
2020 * otherwise the save operation must always succeed. */
2021 throw xml::LogicError(RT_SRC_POS);
2022 }
2023
2024 xmlSaveClose(saveCtxt);
2025}
2026
2027void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
2028{
2029 if (!fSafe)
2030 writeInternal(pcszFilename, fSafe);
2031 else
2032 {
2033 /* Empty string and directory spec must be avoid. */
2034 if (RTPathFilename(pcszFilename) == NULL)
2035 throw xml::LogicError(RT_SRC_POS);
2036
2037 /* Construct both filenames first to ease error handling. */
2038 char szTmpFilename[RTPATH_MAX];
2039 int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
2040 if (RT_FAILURE(rc))
2041 throw EIPRTFailure(rc, "RTStrCopy");
2042 strcat(szTmpFilename, s_pszTmpSuff);
2043
2044 char szPrevFilename[RTPATH_MAX];
2045 rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
2046 if (RT_FAILURE(rc))
2047 throw EIPRTFailure(rc, "RTStrCopy");
2048 strcat(szPrevFilename, s_pszPrevSuff);
2049
2050 /* Write the XML document to the temporary file. */
2051 writeInternal(szTmpFilename, fSafe);
2052
2053 /* Make a backup of any existing file (ignore failure). */
2054 uint64_t cbPrevFile;
2055 rc = RTFileQuerySize(pcszFilename, &cbPrevFile);
2056 if (RT_SUCCESS(rc) && cbPrevFile >= 16)
2057 RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
2058
2059 /* Commit the temporary file. Just leave the tmp file behind on failure. */
2060 rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
2061 if (RT_FAILURE(rc))
2062 throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
2063
2064 /* Flush the directory changes (required on linux at least). */
2065 RTPathStripFilename(szTmpFilename);
2066 rc = RTDirFlush(szTmpFilename);
2067 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
2068 }
2069}
2070
2071int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
2072{
2073 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
2074
2075 /* To prevent throwing exceptions while inside libxml2 code, we catch
2076 * them and forward to our level using a couple of variables. */
2077 try
2078 {
2079 return pContext->file.write(aBuf, aLen);
2080 }
2081 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
2082 catch (const RTCError &err) { pContext->setError(err); }
2083 catch (const std::exception &err) { pContext->setError(err); }
2084 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
2085
2086 return -1 /* failure */;
2087}
2088
2089int XmlFileWriter::CloseCallback(void *aCtxt)
2090{
2091 /// @todo to be written
2092 NOREF(aCtxt);
2093
2094 return -1;
2095}
2096
2097/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
2098/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
2099
2100
2101} // end namespace xml
2102
Note: See TracBrowser for help on using the repository browser.

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