VirtualBox

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

Last change on this file since 48825 was 48797, checked in by vboxsync, 11 years ago

A bit more XAR hacking.

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

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