VirtualBox

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

Last change on this file since 52563 was 49028, checked in by vboxsync, 11 years ago

iprt/cpp/xml: Fixed attribute lookup with namespace by doing the same way as for elements. Also renamed the two methods with namespace prefix and name in the 'logical' order so they can safely be changed to the order dictated C++ default parameter value handling (name first, then optionally ns-prefix), like the rest.

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