VirtualBox

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

Last change on this file since 53597 was 53597, checked in by vboxsync, 10 years ago

Runtime/xml: improved log statement

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