VirtualBox

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

Last change on this file since 48785 was 48785, checked in by vboxsync, 12 years ago

warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.1 KB
Line 
1/* $Id: xml.cpp 48785 2013-10-01 02:56:28Z 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.215389.xyz. 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/**
867 * Recursively find the first matching child element.
868 *
869 * @returns child element or NULL if not found.
870 * @param pcszNamespace The Namespace prefix or NULL.
871 * @param pcszPath Simple element path, with parent and child elements
872 * separated by a forward slash ('/').
873 */
874const ElementNode *ElementNode::findChildElementDeep(const char *pcszNamespace, const char *pcszPath) const
875{
876 size_t cchThis = strchr(pcszPath, '/') - pcszPath;
877 if (cchThis == (size_t)((const char *)0 - pcszPath))
878 return this->findChildElement(pcszNamespace, pcszPath);
879
880#ifndef USE_STD_LIST_FOR_CHILDREN
881 /** @todo Can be done without recursion as we have both sibling lists and parent
882 * pointers in this variant. */
883 Node *p;
884 RTListForEachCpp(&m_children, p, Node, m_childEntry)
885 {
886 if (p->isElement())
887 {
888 const ElementNode *pElm = static_cast<ElementNode*>(p);
889 if (pElm->nameEqualsN(pcszNamespace, pcszPath, cchThis))
890 {
891 pElm = findChildElementDeep(pcszNamespace, pcszPath + cchThis);
892 if (pElm)
893 return pElm;
894 }
895 }
896 }
897#else
898 Data::InternalNodesList::const_iterator itLast = m->children.end();
899 for (Data::InternalNodesList::const_iterator it = m->children.begin(); it != itLast; ++it)
900 {
901 if ((**it).isElement())
902 {
903 const ElementNode *pElm = static_cast<ElementNode*>((*it).get());
904 if (pElm->nameEqualsN(pcszNamespace, pcszPath, cchThis))
905 {
906 pElm = findChildElementDeep(pcszNamespace, pcszPath + cchThis);
907 if (pElm)
908 return pElm;
909 }
910 }
911 }
912#endif
913
914 return NULL;
915}
916
917/**
918 * Looks up the given attribute node in this element's attribute map.
919 *
920 * With respect to namespaces, the internal attributes map stores namespace
921 * prefixes with attribute names only if the attribute uses a non-default
922 * namespace. As a result, the following rules apply:
923 *
924 * -- To find attributes from a non-default namespace, pcszMatch must not
925 * be prefixed with a namespace.
926 *
927 * -- To find attributes from the default namespace (or if the document does
928 * not use namespaces), pcszMatch must be prefixed with the namespace
929 * prefix and a colon.
930 *
931 * For example, if the document uses the "vbox:" namespace by default, you
932 * must omit "vbox:" from pcszMatch to find such attributes, whether they
933 * are specifed in the xml or not.
934 *
935 * @param pcszMatch
936 * @return
937 */
938const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
939{
940 Data::AttributesMap::const_iterator it;
941
942 it = m->attribs.find(pcszMatch);
943 if (it != m->attribs.end())
944 return it->second.get();
945
946 return NULL;
947}
948
949/**
950 * Convenience method which attempts to find the attribute with the given
951 * name and returns its value as a string.
952 *
953 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
954 * @param ppcsz out: attribute value
955 * @return TRUE if attribute was found and str was thus updated.
956 */
957bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
958{
959 const Node* pAttr;
960 if ((pAttr = findAttribute(pcszMatch)))
961 {
962 ppcsz = pAttr->getValue();
963 return true;
964 }
965
966 return false;
967}
968
969/**
970 * Convenience method which attempts to find the attribute with the given
971 * name and returns its value as a string.
972 *
973 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
974 * @param str out: attribute value; overwritten only if attribute was found
975 * @return TRUE if attribute was found and str was thus updated.
976 */
977bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString &str) const
978{
979 const Node* pAttr;
980 if ((pAttr = findAttribute(pcszMatch)))
981 {
982 str = pAttr->getValue();
983 return true;
984 }
985
986 return false;
987}
988
989/**
990 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
991 * are converted to forward slashes.
992 * @param pcszMatch
993 * @param str
994 * @return
995 */
996bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString &str) const
997{
998 if (getAttributeValue(pcszMatch, str))
999 {
1000 str.findReplace('\\', '/');
1001 return true;
1002 }
1003
1004 return false;
1005}
1006
1007/**
1008 * Convenience method which attempts to find the attribute with the given
1009 * name and returns its value as a signed integer. This calls
1010 * RTStrToInt32Ex internally and will only output the integer if that
1011 * function returns no error.
1012 *
1013 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
1014 * @param i out: attribute value; overwritten only if attribute was found
1015 * @return TRUE if attribute was found and str was thus updated.
1016 */
1017bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
1018{
1019 const char *pcsz;
1020 if ( (getAttributeValue(pcszMatch, pcsz))
1021 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
1022 )
1023 return true;
1024
1025 return false;
1026}
1027
1028/**
1029 * Convenience method which attempts to find the attribute with the given
1030 * name and returns its value as an unsigned integer.This calls
1031 * RTStrToUInt32Ex internally and will only output the integer if that
1032 * function returns no error.
1033 *
1034 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
1035 * @param i out: attribute value; overwritten only if attribute was found
1036 * @return TRUE if attribute was found and str was thus updated.
1037 */
1038bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
1039{
1040 const char *pcsz;
1041 if ( (getAttributeValue(pcszMatch, pcsz))
1042 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
1043 )
1044 return true;
1045
1046 return false;
1047}
1048
1049/**
1050 * Convenience method which attempts to find the attribute with the given
1051 * name and returns its value as a signed long integer. This calls
1052 * RTStrToInt64Ex internally and will only output the integer if that
1053 * function returns no error.
1054 *
1055 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
1056 * @param i out: attribute value
1057 * @return TRUE if attribute was found and str was thus updated.
1058 */
1059bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
1060{
1061 const char *pcsz;
1062 if ( (getAttributeValue(pcszMatch, pcsz))
1063 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
1064 )
1065 return true;
1066
1067 return false;
1068}
1069
1070/**
1071 * Convenience method which attempts to find the attribute with the given
1072 * name and returns its value as an unsigned long integer.This calls
1073 * RTStrToUInt64Ex internally and will only output the integer if that
1074 * function returns no error.
1075 *
1076 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
1077 * @param i out: attribute value; overwritten only if attribute was found
1078 * @return TRUE if attribute was found and str was thus updated.
1079 */
1080bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
1081{
1082 const char *pcsz;
1083 if ( (getAttributeValue(pcszMatch, pcsz))
1084 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
1085 )
1086 return true;
1087
1088 return false;
1089}
1090
1091/**
1092 * Convenience method which attempts to find the attribute with the given
1093 * name and returns its value as a boolean. This accepts "true", "false",
1094 * "yes", "no", "1" or "0" as valid values.
1095 *
1096 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
1097 * @param f 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, bool &f) const
1101{
1102 const char *pcsz;
1103 if (getAttributeValue(pcszMatch, pcsz))
1104 {
1105 if ( (!strcmp(pcsz, "true"))
1106 || (!strcmp(pcsz, "yes"))
1107 || (!strcmp(pcsz, "1"))
1108 )
1109 {
1110 f = true;
1111 return true;
1112 }
1113 if ( (!strcmp(pcsz, "false"))
1114 || (!strcmp(pcsz, "no"))
1115 || (!strcmp(pcsz, "0"))
1116 )
1117 {
1118 f = false;
1119 return true;
1120 }
1121 }
1122
1123 return false;
1124}
1125
1126/**
1127 * Creates a new child element node and appends it to the list
1128 * of children in "this".
1129 *
1130 * @param pcszElementName
1131 * @return
1132 */
1133ElementNode *ElementNode::createChild(const char *pcszElementName)
1134{
1135 // we must be an element, not an attribute
1136 if (!m_plibNode)
1137 throw ENodeIsNotElement(RT_SRC_POS);
1138
1139 // libxml side: create new node
1140 xmlNode *plibNode;
1141 if (!(plibNode = xmlNewNode(NULL, // namespace
1142 (const xmlChar*)pcszElementName)))
1143 throw std::bad_alloc();
1144 xmlAddChild(m_plibNode, plibNode);
1145
1146 // now wrap this in C++
1147 ElementNode *p = new ElementNode(m_pelmRoot, this, plibNode);
1148#ifndef USE_STD_LIST_FOR_CHILDREN
1149 RTListAppend(&m_children, &p->m_childEntry);
1150#else
1151 boost::shared_ptr<ElementNode> pNew(p);
1152 m->children.push_back(pNew);
1153#endif
1154
1155 return p;
1156}
1157
1158
1159/**
1160 * Creates a content node and appends it to the list of children
1161 * in "this".
1162 *
1163 * @param pcszContent
1164 * @return
1165 */
1166ContentNode *ElementNode::addContent(const char *pcszContent)
1167{
1168 // libxml side: create new node
1169 xmlNode *plibNode = xmlNewText((const xmlChar*)pcszContent);
1170 if (!plibNode)
1171 throw std::bad_alloc();
1172 xmlAddChild(m_plibNode, plibNode);
1173
1174 // now wrap this in C++
1175 ContentNode *p = new ContentNode(this, plibNode);
1176#ifndef USE_STD_LIST_FOR_CHILDREN
1177 RTListAppend(&m_children, &p->m_childEntry);
1178#else
1179 boost::shared_ptr<ContentNode> pNew(p);
1180 m->children.push_back(pNew);
1181#endif
1182
1183 return p;
1184}
1185
1186/**
1187 * Sets the given attribute; overloaded version for const char *.
1188 *
1189 * If an attribute with the given name exists, it is overwritten,
1190 * otherwise a new attribute is created. Returns the attribute node
1191 * that was either created or changed.
1192 *
1193 * @param pcszName
1194 * @param pcszValue
1195 * @return
1196 */
1197AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
1198{
1199 AttributeNode *pattrReturn;
1200 Data::AttributesMap::const_iterator it;
1201
1202 it = m->attribs.find(pcszName);
1203 if (it == m->attribs.end())
1204 {
1205 // libxml side: xmlNewProp creates an attribute
1206 xmlAttr *plibAttr = xmlNewProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1207
1208 // C++ side: create an attribute node around it
1209 const char *pcszKey;
1210 boost::shared_ptr<AttributeNode> pNew(new AttributeNode(*m_pelmRoot, this, plibAttr, &pcszKey));
1211 // store
1212 m->attribs[pcszKey] = pNew;
1213 pattrReturn = pNew.get();
1214 }
1215 else
1216 {
1217 // overwrite existing libxml attribute node
1218 xmlAttrPtr plibAttr = xmlSetProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1219
1220 // and fix our existing C++ side around it
1221 boost::shared_ptr<AttributeNode> pattr = it->second;
1222 pattr->m_plibAttr = plibAttr; // in case the xmlAttrPtr is different, I'm not sure
1223
1224 pattrReturn = pattr.get();
1225 }
1226
1227 return pattrReturn;
1228
1229}
1230
1231/**
1232 * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
1233 * before calling that one.
1234 * @param pcszName
1235 * @param strValue
1236 * @return
1237 */
1238AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
1239{
1240 RTCString strTemp(strValue);
1241 strTemp.findReplace('\\', '/');
1242 return setAttribute(pcszName, strTemp.c_str());
1243}
1244
1245/**
1246 * Sets the given attribute; overloaded version for int32_t.
1247 *
1248 * If an attribute with the given name exists, it is overwritten,
1249 * otherwise a new attribute is created. Returns the attribute node
1250 * that was either created or changed.
1251 *
1252 * @param pcszName
1253 * @param i
1254 * @return
1255 */
1256AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
1257{
1258 char szValue[12]; // negative sign + 10 digits + \0
1259 RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
1260 AttributeNode *p = setAttribute(pcszName, szValue);
1261 return p;
1262}
1263
1264/**
1265 * Sets the given attribute; overloaded version for uint32_t.
1266 *
1267 * If an attribute with the given name exists, it is overwritten,
1268 * otherwise a new attribute is created. Returns the attribute node
1269 * that was either created or changed.
1270 *
1271 * @param pcszName
1272 * @param u
1273 * @return
1274 */
1275AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
1276{
1277 char szValue[11]; // 10 digits + \0
1278 RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
1279 AttributeNode *p = setAttribute(pcszName, szValue);
1280 return p;
1281}
1282
1283/**
1284 * Sets the given attribute; overloaded version for int64_t.
1285 *
1286 * If an attribute with the given name exists, it is overwritten,
1287 * otherwise a new attribute is created. Returns the attribute node
1288 * that was either created or changed.
1289 *
1290 * @param pcszName
1291 * @param i
1292 * @return
1293 */
1294AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1295{
1296 char szValue[21]; // negative sign + 19 digits + \0
1297 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
1298 AttributeNode *p = setAttribute(pcszName, szValue);
1299 return p;
1300}
1301
1302/**
1303 * Sets the given attribute; overloaded version for uint64_t.
1304 *
1305 * If an attribute with the given name exists, it is overwritten,
1306 * otherwise a new attribute is created. Returns the attribute node
1307 * that was either created or changed.
1308 *
1309 * @param pcszName
1310 * @param u
1311 * @return
1312 */
1313AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
1314{
1315 char szValue[21]; // 20 digits + \0
1316 RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
1317 AttributeNode *p = setAttribute(pcszName, szValue);
1318 return p;
1319}
1320
1321/**
1322 * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
1323 *
1324 * If an attribute with the given name exists, it is overwritten,
1325 * otherwise a new attribute is created. Returns the attribute node
1326 * that was either created or changed.
1327 *
1328 * @param pcszName
1329 * @param u
1330 * @return
1331 */
1332AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
1333{
1334 char szValue[11]; // "0x" + 8 digits + \0
1335 RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
1336 AttributeNode *p = setAttribute(pcszName, szValue);
1337 return p;
1338}
1339
1340/**
1341 * Sets the given attribute; overloaded version for bool.
1342 *
1343 * If an attribute with the given name exists, it is overwritten,
1344 * otherwise a new attribute is created. Returns the attribute node
1345 * that was either created or changed.
1346 *
1347 * @param pcszName
1348 * @param i
1349 * @return
1350 */
1351AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1352{
1353 return setAttribute(pcszName, (f) ? "true" : "false");
1354}
1355
1356/**
1357 * Private constructor for a new attribute node. This one is special:
1358 * in ppcszKey, it returns a pointer to a string buffer that should be
1359 * used to index the attribute correctly with namespaces.
1360 *
1361 * @param pParent
1362 * @param elmRoot
1363 * @param plibAttr
1364 * @param ppcszKey
1365 */
1366AttributeNode::AttributeNode(const ElementNode &elmRoot,
1367 Node *pParent,
1368 xmlAttr *plibAttr,
1369 const char **ppcszKey)
1370 : Node(IsAttribute,
1371 pParent,
1372 NULL,
1373 plibAttr)
1374{
1375 m_pcszName = (const char*)plibAttr->name;
1376
1377 *ppcszKey = m_pcszName;
1378
1379 if ( plibAttr->ns
1380 && plibAttr->ns->prefix
1381 )
1382 {
1383 m_pcszNamespacePrefix = (const char*)plibAttr->ns->prefix;
1384 m_pcszNamespaceHref = (const char*)plibAttr->ns->href;
1385
1386 if ( !elmRoot.m_pcszNamespaceHref
1387 || (strcmp(m_pcszNamespaceHref, elmRoot.m_pcszNamespaceHref))
1388 )
1389 {
1390 // not default namespace:
1391 m_strKey = m_pcszNamespacePrefix;
1392 m_strKey.append(':');
1393 m_strKey.append(m_pcszName);
1394
1395 *ppcszKey = m_strKey.c_str();
1396 }
1397 }
1398}
1399
1400ContentNode::ContentNode(Node *pParent, xmlNode *plibNode)
1401 : Node(IsContent,
1402 pParent,
1403 plibNode,
1404 NULL)
1405{
1406}
1407
1408/*
1409 * NodesLoop
1410 *
1411 */
1412
1413struct NodesLoop::Data
1414{
1415 ElementNodesList listElements;
1416 ElementNodesList::const_iterator it;
1417};
1418
1419NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1420{
1421 m = new Data;
1422 node.getChildElements(m->listElements, pcszMatch);
1423 m->it = m->listElements.begin();
1424}
1425
1426NodesLoop::~NodesLoop()
1427{
1428 delete m;
1429}
1430
1431
1432/**
1433 * Handy convenience helper for looping over all child elements. Create an
1434 * instance of NodesLoop on the stack and call this method until it returns
1435 * NULL, like this:
1436 * <code>
1437 * xml::ElementNode node; // should point to an element
1438 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1439 * const xml::ElementNode *pChild = NULL;
1440 * while (pChild = loop.forAllNodes())
1441 * ...;
1442 * </code>
1443 * @return
1444 */
1445const ElementNode* NodesLoop::forAllNodes() const
1446{
1447 const ElementNode *pNode = NULL;
1448
1449 if (m->it != m->listElements.end())
1450 {
1451 pNode = *(m->it);
1452 ++(m->it);
1453 }
1454
1455 return pNode;
1456}
1457
1458////////////////////////////////////////////////////////////////////////////////
1459//
1460// Document class
1461//
1462////////////////////////////////////////////////////////////////////////////////
1463
1464struct Document::Data
1465{
1466 xmlDocPtr plibDocument;
1467 ElementNode *pRootElement;
1468 ElementNode *pComment;
1469
1470 Data()
1471 {
1472 plibDocument = NULL;
1473 pRootElement = NULL;
1474 pComment = NULL;
1475 }
1476
1477 ~Data()
1478 {
1479 reset();
1480 }
1481
1482 void reset()
1483 {
1484 if (plibDocument)
1485 {
1486 xmlFreeDoc(plibDocument);
1487 plibDocument = NULL;
1488 }
1489 if (pRootElement)
1490 {
1491 delete pRootElement;
1492 pRootElement = NULL;
1493 }
1494 if (pComment)
1495 {
1496 delete pComment;
1497 pComment = NULL;
1498 }
1499 }
1500
1501 void copyFrom(const Document::Data *p)
1502 {
1503 if (p->plibDocument)
1504 {
1505 plibDocument = xmlCopyDoc(p->plibDocument,
1506 1); // recursive == copy all
1507 }
1508 }
1509};
1510
1511Document::Document()
1512 : m(new Data)
1513{
1514}
1515
1516Document::Document(const Document &x)
1517 : m(new Data)
1518{
1519 m->copyFrom(x.m);
1520}
1521
1522Document& Document::operator=(const Document &x)
1523{
1524 m->reset();
1525 m->copyFrom(x.m);
1526 return *this;
1527}
1528
1529Document::~Document()
1530{
1531 delete m;
1532}
1533
1534/**
1535 * private method to refresh all internal structures after the internal pDocument
1536 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1537 * called before to make sure all members except the internal pDocument are clean.
1538 */
1539void Document::refreshInternals() // private
1540{
1541 m->pRootElement = new ElementNode(NULL, NULL, xmlDocGetRootElement(m->plibDocument));
1542
1543 m->pRootElement->buildChildren(*m->pRootElement);
1544}
1545
1546/**
1547 * Returns the root element of the document, or NULL if the document is empty.
1548 * Const variant.
1549 * @return
1550 */
1551const ElementNode* Document::getRootElement() const
1552{
1553 return m->pRootElement;
1554}
1555
1556/**
1557 * Returns the root element of the document, or NULL if the document is empty.
1558 * Non-const variant.
1559 * @return
1560 */
1561ElementNode* Document::getRootElement()
1562{
1563 return m->pRootElement;
1564}
1565
1566/**
1567 * Creates a new element node and sets it as the root element. This will
1568 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1569 */
1570ElementNode* Document::createRootElement(const char *pcszRootElementName,
1571 const char *pcszComment /* = NULL */)
1572{
1573 if (m->pRootElement || m->plibDocument)
1574 throw EDocumentNotEmpty(RT_SRC_POS);
1575
1576 // libxml side: create document, create root node
1577 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1578 xmlNode *plibRootNode;
1579 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1580 (const xmlChar*)pcszRootElementName)))
1581 throw std::bad_alloc();
1582 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1583 // now wrap this in C++
1584 m->pRootElement = new ElementNode(NULL, NULL, plibRootNode);
1585
1586 // add document global comment if specified
1587 if (pcszComment != NULL)
1588 {
1589 xmlNode *pComment;
1590 if (!(pComment = xmlNewDocComment(m->plibDocument,
1591 (const xmlChar *)pcszComment)))
1592 throw std::bad_alloc();
1593 xmlAddPrevSibling(plibRootNode, pComment);
1594 // now wrap this in C++
1595 m->pComment = new ElementNode(NULL, NULL, pComment);
1596 }
1597
1598 return m->pRootElement;
1599}
1600
1601////////////////////////////////////////////////////////////////////////////////
1602//
1603// XmlParserBase class
1604//
1605////////////////////////////////////////////////////////////////////////////////
1606
1607XmlParserBase::XmlParserBase()
1608{
1609 m_ctxt = xmlNewParserCtxt();
1610 if (m_ctxt == NULL)
1611 throw std::bad_alloc();
1612}
1613
1614XmlParserBase::~XmlParserBase()
1615{
1616 xmlFreeParserCtxt (m_ctxt);
1617 m_ctxt = NULL;
1618}
1619
1620////////////////////////////////////////////////////////////////////////////////
1621//
1622// XmlMemParser class
1623//
1624////////////////////////////////////////////////////////////////////////////////
1625
1626XmlMemParser::XmlMemParser()
1627 : XmlParserBase()
1628{
1629}
1630
1631XmlMemParser::~XmlMemParser()
1632{
1633}
1634
1635/**
1636 * Parse the given buffer and fills the given Document object with its contents.
1637 * Throws XmlError on parsing errors.
1638 *
1639 * The document that is passed in will be reset before being filled if not empty.
1640 *
1641 * @param pvBuf Memory buffer to parse.
1642 * @param cbSize Size of the memory buffer.
1643 * @param strFilename Refernece to the name of the file we're parsing.
1644 * @param doc Reference to the output document. This will be reset
1645 * and filled with data according to file contents.
1646 */
1647void XmlMemParser::read(const void *pvBuf, size_t cbSize,
1648 const RTCString &strFilename,
1649 Document &doc)
1650{
1651 GlobalLock lock;
1652// global.setExternalEntityLoader(ExternalEntityLoader);
1653
1654 const char *pcszFilename = strFilename.c_str();
1655
1656 doc.m->reset();
1657 if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
1658 (const char*)pvBuf,
1659 (int)cbSize,
1660 pcszFilename,
1661 NULL, // encoding = auto
1662 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1663 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1664
1665 doc.refreshInternals();
1666}
1667
1668////////////////////////////////////////////////////////////////////////////////
1669//
1670// XmlMemWriter class
1671//
1672////////////////////////////////////////////////////////////////////////////////
1673
1674XmlMemWriter::XmlMemWriter()
1675 : m_pBuf(0)
1676{
1677}
1678
1679XmlMemWriter::~XmlMemWriter()
1680{
1681 if (m_pBuf)
1682 xmlFree(m_pBuf);
1683}
1684
1685void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
1686{
1687 if (m_pBuf)
1688 {
1689 xmlFree(m_pBuf);
1690 m_pBuf = 0;
1691 }
1692 int size;
1693 xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
1694 *ppvBuf = m_pBuf;
1695 *pcbSize = size;
1696}
1697
1698////////////////////////////////////////////////////////////////////////////////
1699//
1700// XmlFileParser class
1701//
1702////////////////////////////////////////////////////////////////////////////////
1703
1704struct XmlFileParser::Data
1705{
1706 RTCString strXmlFilename;
1707
1708 Data()
1709 {
1710 }
1711
1712 ~Data()
1713 {
1714 }
1715};
1716
1717XmlFileParser::XmlFileParser()
1718 : XmlParserBase(),
1719 m(new Data())
1720{
1721}
1722
1723XmlFileParser::~XmlFileParser()
1724{
1725 delete m;
1726 m = NULL;
1727}
1728
1729struct IOContext
1730{
1731 File file;
1732 RTCString error;
1733
1734 IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
1735 : file(mode, pcszFilename, fFlush)
1736 {
1737 }
1738
1739 void setError(const RTCError &x)
1740 {
1741 error = x.what();
1742 }
1743
1744 void setError(const std::exception &x)
1745 {
1746 error = x.what();
1747 }
1748};
1749
1750struct ReadContext : IOContext
1751{
1752 ReadContext(const char *pcszFilename)
1753 : IOContext(pcszFilename, File::Mode_Read)
1754 {
1755 }
1756};
1757
1758struct WriteContext : IOContext
1759{
1760 WriteContext(const char *pcszFilename, bool fFlush)
1761 : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
1762 {
1763 }
1764};
1765
1766/**
1767 * Reads the given file and fills the given Document object with its contents.
1768 * Throws XmlError on parsing errors.
1769 *
1770 * The document that is passed in will be reset before being filled if not empty.
1771 *
1772 * @param strFilename in: name fo file to parse.
1773 * @param doc out: document to be reset and filled with data according to file contents.
1774 */
1775void XmlFileParser::read(const RTCString &strFilename,
1776 Document &doc)
1777{
1778 GlobalLock lock;
1779// global.setExternalEntityLoader(ExternalEntityLoader);
1780
1781 m->strXmlFilename = strFilename;
1782 const char *pcszFilename = strFilename.c_str();
1783
1784 ReadContext context(pcszFilename);
1785 doc.m->reset();
1786 if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
1787 ReadCallback,
1788 CloseCallback,
1789 &context,
1790 pcszFilename,
1791 NULL, // encoding = auto
1792 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1793 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1794
1795 doc.refreshInternals();
1796}
1797
1798// static
1799int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1800{
1801 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1802
1803 /* To prevent throwing exceptions while inside libxml2 code, we catch
1804 * them and forward to our level using a couple of variables. */
1805
1806 try
1807 {
1808 return pContext->file.read(aBuf, aLen);
1809 }
1810 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1811 catch (const RTCError &err) { pContext->setError(err); }
1812 catch (const std::exception &err) { pContext->setError(err); }
1813 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1814
1815 return -1 /* failure */;
1816}
1817
1818int XmlFileParser::CloseCallback(void *aCtxt)
1819{
1820 /// @todo to be written
1821 NOREF(aCtxt);
1822
1823 return -1;
1824}
1825
1826////////////////////////////////////////////////////////////////////////////////
1827//
1828// XmlFileWriter class
1829//
1830////////////////////////////////////////////////////////////////////////////////
1831
1832struct XmlFileWriter::Data
1833{
1834 Document *pDoc;
1835};
1836
1837XmlFileWriter::XmlFileWriter(Document &doc)
1838{
1839 m = new Data();
1840 m->pDoc = &doc;
1841}
1842
1843XmlFileWriter::~XmlFileWriter()
1844{
1845 delete m;
1846}
1847
1848void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
1849{
1850 WriteContext context(pcszFilename, fSafe);
1851
1852 GlobalLock lock;
1853
1854 /* serialize to the stream */
1855 xmlIndentTreeOutput = 1;
1856 xmlTreeIndentString = " ";
1857 xmlSaveNoEmptyTags = 0;
1858
1859 xmlSaveCtxtPtr saveCtxt;
1860 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1861 CloseCallback,
1862 &context,
1863 NULL,
1864 XML_SAVE_FORMAT)))
1865 throw xml::LogicError(RT_SRC_POS);
1866
1867 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1868 if (rc == -1)
1869 {
1870 /* look if there was a forwarded exception from the lower level */
1871// if (m->trappedErr.get() != NULL)
1872// m->trappedErr->rethrow();
1873
1874 /* there must be an exception from the Output implementation,
1875 * otherwise the save operation must always succeed. */
1876 throw xml::LogicError(RT_SRC_POS);
1877 }
1878
1879 xmlSaveClose(saveCtxt);
1880}
1881
1882void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
1883{
1884 if (!fSafe)
1885 writeInternal(pcszFilename, fSafe);
1886 else
1887 {
1888 /* Empty string and directory spec must be avoid. */
1889 if (RTPathFilename(pcszFilename) == NULL)
1890 throw xml::LogicError(RT_SRC_POS);
1891
1892 /* Construct both filenames first to ease error handling. */
1893 char szTmpFilename[RTPATH_MAX];
1894 int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
1895 if (RT_FAILURE(rc))
1896 throw EIPRTFailure(rc, "RTStrCopy");
1897 strcat(szTmpFilename, s_pszTmpSuff);
1898
1899 char szPrevFilename[RTPATH_MAX];
1900 rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
1901 if (RT_FAILURE(rc))
1902 throw EIPRTFailure(rc, "RTStrCopy");
1903 strcat(szPrevFilename, s_pszPrevSuff);
1904
1905 /* Write the XML document to the temporary file. */
1906 writeInternal(szTmpFilename, fSafe);
1907
1908 /* Make a backup of any existing file (ignore failure). */
1909 uint64_t cbPrevFile;
1910 rc = RTFileQuerySize(pcszFilename, &cbPrevFile);
1911 if (RT_SUCCESS(rc) && cbPrevFile >= 16)
1912 RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
1913
1914 /* Commit the temporary file. Just leave the tmp file behind on failure. */
1915 rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
1916 if (RT_FAILURE(rc))
1917 throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
1918
1919 /* Flush the directory changes (required on linux at least). */
1920 RTPathStripFilename(szTmpFilename);
1921 rc = RTDirFlush(szTmpFilename);
1922 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1923 }
1924}
1925
1926int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1927{
1928 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1929
1930 /* To prevent throwing exceptions while inside libxml2 code, we catch
1931 * them and forward to our level using a couple of variables. */
1932 try
1933 {
1934 return pContext->file.write(aBuf, aLen);
1935 }
1936 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1937 catch (const RTCError &err) { pContext->setError(err); }
1938 catch (const std::exception &err) { pContext->setError(err); }
1939 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1940
1941 return -1 /* failure */;
1942}
1943
1944int XmlFileWriter::CloseCallback(void *aCtxt)
1945{
1946 /// @todo to be written
1947 NOREF(aCtxt);
1948
1949 return -1;
1950}
1951
1952/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
1953/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
1954
1955
1956} // end namespace xml
1957
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