VirtualBox

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

Last change on this file since 22173 was 22173, checked in by vboxsync, 15 years ago

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/** @file
2 * VirtualBox XML Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 *
25 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
26 * Clara, CA 95054 USA or visit http://www.sun.com if you need
27 * additional information or have any questions.
28 */
29
30#include <iprt/cdefs.h>
31#include <iprt/err.h>
32#include <iprt/file.h>
33#include <iprt/lock.h>
34#include <iprt/xml_cpp.h>
35
36#include <libxml/tree.h>
37#include <libxml/parser.h>
38#include <libxml/globals.h>
39#include <libxml/xmlIO.h>
40#include <libxml/xmlsave.h>
41#include <libxml/uri.h>
42
43#include <libxml/xmlschemas.h>
44
45#include <map>
46#include <boost/shared_ptr.hpp>
47
48////////////////////////////////////////////////////////////////////////////////
49//
50// globals
51//
52////////////////////////////////////////////////////////////////////////////////
53
54/**
55 * Global module initialization structure. This is to wrap non-reentrant bits
56 * of libxml, among other things.
57 *
58 * The constructor and destructor of this structure are used to perform global
59 * module initiaizaton and cleanup. Thee must be only one global variable of
60 * this structure.
61 */
62static
63class 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 RTLockMtx lock;
95 }
96 sxml; /* XXX naming this xml will break with gcc-3.3 */
97}
98gGlobal;
99
100
101
102namespace xml
103{
104
105////////////////////////////////////////////////////////////////////////////////
106//
107// Exceptions
108//
109////////////////////////////////////////////////////////////////////////////////
110
111LogicError::LogicError(RT_SRC_POS_DECL)
112 : Error(NULL)
113{
114 char *msg = NULL;
115 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
116 pszFunction, pszFile, iLine);
117 setWhat(msg);
118 RTStrFree(msg);
119}
120
121XmlError::XmlError(xmlErrorPtr aErr)
122{
123 if (!aErr)
124 throw EInvalidArg(RT_SRC_POS);
125
126 char *msg = Format(aErr);
127 setWhat(msg);
128 RTStrFree(msg);
129}
130
131/**
132 * Composes a single message for the given error. The caller must free the
133 * returned string using RTStrFree() when no more necessary.
134 */
135// static
136char *XmlError::Format(xmlErrorPtr aErr)
137{
138 const char *msg = aErr->message ? aErr->message : "<none>";
139 size_t msgLen = strlen(msg);
140 /* strip spaces, trailing EOLs and dot-like char */
141 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
142 --msgLen;
143
144 char *finalMsg = NULL;
145 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
146 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
147
148 return finalMsg;
149}
150
151EIPRTFailure::EIPRTFailure(int aRC)
152 : RuntimeError(NULL),
153 mRC(aRC)
154{
155 char *newMsg = NULL;
156 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
157 setWhat(newMsg);
158 RTStrFree(newMsg);
159}
160
161////////////////////////////////////////////////////////////////////////////////
162//
163// File Class
164//
165//////////////////////////////////////////////////////////////////////////////
166
167struct File::Data
168{
169 Data()
170 : handle(NIL_RTFILE), opened(false)
171 { }
172
173 iprt::MiniString strFileName;
174 RTFILE handle;
175 bool opened : 1;
176};
177
178File::File(Mode aMode, const char *aFileName)
179 : m(new Data())
180{
181 m->strFileName = aFileName;
182
183 unsigned flags = 0;
184 switch (aMode)
185 {
186 case Mode_Read:
187 flags = RTFILE_O_READ;
188 break;
189 case Mode_WriteCreate: // fail if file exists
190 flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
191 break;
192 case Mode_Overwrite: // overwrite if file exists
193 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE;
194 break;
195 case Mode_ReadWrite:
196 flags = RTFILE_O_READ | RTFILE_O_WRITE;
197 }
198
199 int vrc = RTFileOpen (&m->handle, aFileName, flags);
200 if (RT_FAILURE (vrc))
201 throw EIPRTFailure (vrc);
202
203 m->opened = true;
204}
205
206File::File(RTFILE aHandle, const char *aFileName /* = NULL */)
207 : m(new Data())
208{
209 if (aHandle == NIL_RTFILE)
210 throw EInvalidArg (RT_SRC_POS);
211
212 m->handle = aHandle;
213
214 if (aFileName)
215 m->strFileName = aFileName;
216
217 setPos (0);
218}
219
220File::~File()
221{
222 if (m->opened)
223 RTFileClose(m->handle);
224}
225
226const char* File::uri() const
227{
228 return m->strFileName.c_str();
229}
230
231uint64_t File::pos() const
232{
233 uint64_t p = 0;
234 int vrc = RTFileSeek (m->handle, 0, RTFILE_SEEK_CURRENT, &p);
235 if (RT_SUCCESS (vrc))
236 return p;
237
238 throw EIPRTFailure (vrc);
239}
240
241void File::setPos (uint64_t aPos)
242{
243 uint64_t p = 0;
244 unsigned method = RTFILE_SEEK_BEGIN;
245 int vrc = VINF_SUCCESS;
246
247 /* check if we overflow int64_t and move to INT64_MAX first */
248 if (((int64_t) aPos) < 0)
249 {
250 vrc = RTFileSeek (m->handle, INT64_MAX, method, &p);
251 aPos -= (uint64_t) INT64_MAX;
252 method = RTFILE_SEEK_CURRENT;
253 }
254 /* seek the rest */
255 if (RT_SUCCESS (vrc))
256 vrc = RTFileSeek (m->handle, (int64_t) aPos, method, &p);
257 if (RT_SUCCESS (vrc))
258 return;
259
260 throw EIPRTFailure (vrc);
261}
262
263int File::read (char *aBuf, int aLen)
264{
265 size_t len = aLen;
266 int vrc = RTFileRead (m->handle, aBuf, len, &len);
267 if (RT_SUCCESS (vrc))
268 return (int)len;
269
270 throw EIPRTFailure (vrc);
271}
272
273int File::write (const char *aBuf, int aLen)
274{
275 size_t len = aLen;
276 int vrc = RTFileWrite (m->handle, aBuf, len, &len);
277 if (RT_SUCCESS (vrc))
278 return (int)len;
279
280 throw EIPRTFailure (vrc);
281
282 return -1 /* failure */;
283}
284
285void File::truncate()
286{
287 int vrc = RTFileSetSize (m->handle, pos());
288 if (RT_SUCCESS (vrc))
289 return;
290
291 throw EIPRTFailure (vrc);
292}
293
294////////////////////////////////////////////////////////////////////////////////
295//
296// MemoryBuf Class
297//
298//////////////////////////////////////////////////////////////////////////////
299
300struct MemoryBuf::Data
301{
302 Data()
303 : buf (NULL), len (0), uri (NULL), pos (0) {}
304
305 const char *buf;
306 size_t len;
307 char *uri;
308
309 size_t pos;
310};
311
312MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
313 : m (new Data())
314{
315 if (aBuf == NULL)
316 throw EInvalidArg (RT_SRC_POS);
317
318 m->buf = aBuf;
319 m->len = aLen;
320 m->uri = RTStrDup (aURI);
321}
322
323MemoryBuf::~MemoryBuf()
324{
325 RTStrFree (m->uri);
326}
327
328const char *MemoryBuf::uri() const
329{
330 return m->uri;
331}
332
333uint64_t MemoryBuf::pos() const
334{
335 return m->pos;
336}
337
338void MemoryBuf::setPos (uint64_t aPos)
339{
340 size_t pos = (size_t) aPos;
341 if ((uint64_t) pos != aPos)
342 throw EInvalidArg();
343
344 if (pos > m->len)
345 throw EInvalidArg();
346
347 m->pos = pos;
348}
349
350int MemoryBuf::read (char *aBuf, int aLen)
351{
352 if (m->pos >= m->len)
353 return 0 /* nothing to read */;
354
355 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
356 memcpy (aBuf, m->buf + m->pos, len);
357 m->pos += len;
358
359 return (int)len;
360}
361
362////////////////////////////////////////////////////////////////////////////////
363//
364// GlobalLock class
365//
366////////////////////////////////////////////////////////////////////////////////
367
368struct GlobalLock::Data
369{
370 PFNEXTERNALENTITYLOADER pOldLoader;
371 RTLock lock;
372
373 Data()
374 : pOldLoader(NULL),
375 lock(gGlobal.sxml.lock)
376 {
377 }
378};
379
380GlobalLock::GlobalLock()
381 : m(new Data())
382{
383}
384
385GlobalLock::~GlobalLock()
386{
387 if (m->pOldLoader)
388 xmlSetExternalEntityLoader(m->pOldLoader);
389 delete m;
390 m = NULL;
391}
392
393void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
394{
395 m->pOldLoader = xmlGetExternalEntityLoader();
396 xmlSetExternalEntityLoader(pLoader);
397}
398
399// static
400xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
401 const char *aID,
402 xmlParserCtxt *aCtxt)
403{
404 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
405}
406
407////////////////////////////////////////////////////////////////////////////////
408//
409// Node class
410//
411////////////////////////////////////////////////////////////////////////////////
412
413struct Node::Data
414{
415 xmlNode *plibNode; // != NULL if this is an element or content node
416 xmlAttr *plibAttr; // != NULL if this is an attribute node
417
418 Node *pParent; // NULL only for the root element
419 const char *pcszName; // element or attribute name, points either into plibNode or plibAttr;
420 // NULL if this is a content node
421
422 struct compare_const_char
423 {
424 bool operator()(const char* s1, const char* s2) const
425 {
426 return strcmp(s1, s2) < 0;
427 }
428 };
429
430 // attributes, if this is an element; can be empty
431 typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap;
432 AttributesMap attribs;
433
434 // child elements, if this is an element; can be empty
435 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
436 InternalNodesList children;
437};
438
439Node::Node(EnumType type)
440 : mType(type),
441 m(new Data)
442{
443 m->plibNode = NULL;
444 m->plibAttr = NULL;
445 m->pParent = NULL;
446}
447
448Node::~Node()
449{
450 delete m;
451}
452
453void Node::buildChildren() // private
454{
455 // go thru this element's attributes
456 xmlAttr *plibAttr = m->plibNode->properties;
457 while (plibAttr)
458 {
459 const char *pcszAttribName = (const char*)plibAttr->name;
460 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
461 pNew->m->plibAttr = plibAttr;
462 pNew->m->pcszName = (const char*)plibAttr->name;
463 pNew->m->pParent = this;
464 // store
465 m->attribs[pcszAttribName] = pNew;
466
467 plibAttr = plibAttr->next;
468 }
469
470 // go thru this element's child elements
471 xmlNodePtr plibNode = m->plibNode->children;
472 while (plibNode)
473 {
474 boost::shared_ptr<Node> pNew;
475
476 if (plibNode->name)
477 pNew = boost::shared_ptr<Node>(new ElementNode);
478 else
479 pNew = boost::shared_ptr<Node>(new ContentNode);
480
481 pNew->m->plibNode = plibNode;
482 pNew->m->pcszName = (const char*)plibNode->name;
483 pNew->m->pParent = this;
484 // store
485 m->children.push_back(pNew);
486
487 // recurse for this child element to get its own children
488 pNew->buildChildren();
489
490 plibNode = plibNode->next;
491 }
492}
493
494const char* Node::getName() const
495{
496 return m->pcszName;
497}
498
499bool Node::nameEquals(const char *pcsz) const
500{
501 if (m->pcszName == pcsz)
502 return true;
503 if (m->pcszName == NULL)
504 return false;
505 if (pcsz == NULL)
506 return false;
507 return !strcmp(m->pcszName, pcsz);
508}
509
510
511/**
512 * Returns the value of a node. If this node is an attribute, returns
513 * the attribute value; if this node is an element, then this returns
514 * the element text content.
515 * @return
516 */
517const char* Node::getValue() const
518{
519 if ( (m->plibAttr)
520 && (m->plibAttr->children)
521 )
522 // libxml hides attribute values in another node created as a
523 // single child of the attribute node, and it's in the content field
524 return (const char*)m->plibAttr->children->content;
525
526 if ( (m->plibNode)
527 && (m->plibNode->children)
528 )
529 return (const char*)m->plibNode->children->content;
530
531 return NULL;
532}
533
534/**
535 * Copies the value of a node into the given integer variable.
536 * Returns TRUE only if a value was found and was actually an
537 * integer of the given type.
538 * @return
539 */
540bool Node::copyValue(int32_t &i) const
541{
542 const char *pcsz;
543 if ( ((pcsz = getValue()))
544 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
545 )
546 return true;
547
548 return false;
549}
550
551/**
552 * Copies the value of a node into the given integer variable.
553 * Returns TRUE only if a value was found and was actually an
554 * integer of the given type.
555 * @return
556 */
557bool Node::copyValue(uint32_t &i) const
558{
559 const char *pcsz;
560 if ( ((pcsz = getValue()))
561 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
562 )
563 return true;
564
565 return false;
566}
567
568/**
569 * Copies the value of a node into the given integer variable.
570 * Returns TRUE only if a value was found and was actually an
571 * integer of the given type.
572 * @return
573 */
574bool Node::copyValue(int64_t &i) const
575{
576 const char *pcsz;
577 if ( ((pcsz = getValue()))
578 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
579 )
580 return true;
581
582 return false;
583}
584
585/**
586 * Copies the value of a node into the given integer variable.
587 * Returns TRUE only if a value was found and was actually an
588 * integer of the given type.
589 * @return
590 */
591bool Node::copyValue(uint64_t &i) const
592{
593 const char *pcsz;
594 if ( ((pcsz = getValue()))
595 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
596 )
597 return true;
598
599 return false;
600}
601
602/**
603 * Returns the line number of the current node in the source XML file.
604 * Useful for error messages.
605 * @return
606 */
607int Node::getLineNumber() const
608{
609 if (m->plibAttr)
610 return m->pParent->m->plibNode->line;
611
612 return m->plibNode->line;
613}
614
615ElementNode::ElementNode()
616 : Node(IsElement)
617{
618}
619
620/**
621 * Builds a list of direct child elements of the current element that
622 * match the given string; if pcszMatch is NULL, all direct child
623 * elements are returned.
624 * @param children out: list of nodes to which children will be appended.
625 * @param pcszMatch in: match string, or NULL to return all children.
626 * @return Number of items appended to the list (0 if none).
627 */
628int ElementNode::getChildElements(ElementNodesList &children,
629 const char *pcszMatch /*= NULL*/)
630 const
631{
632 int i = 0;
633 Data::InternalNodesList::const_iterator
634 it,
635 last = m->children.end();
636 for (it = m->children.begin();
637 it != last;
638 ++it)
639 {
640 // export this child node if ...
641 if ( (!pcszMatch) // the caller wants all nodes or
642 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
643 )
644 {
645 Node *pNode = (*it).get();
646 if (pNode->isElement())
647 children.push_back(static_cast<ElementNode*>(pNode));
648 ++i;
649 }
650 }
651 return i;
652}
653
654/**
655 * Returns the first child element whose name matches pcszMatch.
656 * @param pcszMatch
657 * @return
658 */
659const ElementNode* ElementNode::findChildElement(const char *pcszMatch)
660 const
661{
662 Data::InternalNodesList::const_iterator
663 it,
664 last = m->children.end();
665 for (it = m->children.begin();
666 it != last;
667 ++it)
668 {
669 if ((**it).isElement())
670 {
671 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
672 if (!strcmp(pcszMatch, pelm->getName())) // the element name matches
673 return pelm;
674 }
675 }
676
677 return NULL;
678}
679
680/**
681 * Returns the first child element whose "id" attribute matches pcszId.
682 * @param pcszId identifier to look for.
683 * @return child element or NULL if not found.
684 */
685const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
686{
687 Data::InternalNodesList::const_iterator
688 it,
689 last = m->children.end();
690 for (it = m->children.begin();
691 it != last;
692 ++it)
693 {
694 if ((**it).isElement())
695 {
696 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
697 const AttributeNode *pAttr;
698 if ( ((pAttr = pelm->findAttribute("id")))
699 && (!strcmp(pAttr->getValue(), pcszId))
700 )
701 return pelm;
702 }
703 }
704
705 return NULL;
706}
707
708/**
709 *
710 * @param pcszMatch
711 * @return
712 */
713const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
714{
715 Data::AttributesMap::const_iterator it;
716
717 it = m->attribs.find(pcszMatch);
718 if (it != m->attribs.end())
719 return it->second.get();
720
721 return NULL;
722}
723
724/**
725 * Convenience method which attempts to find the attribute with the given
726 * name and returns its value as a string.
727 *
728 * @param pcszMatch name of attribute to find.
729 * @param str out: attribute value
730 * @return TRUE if attribute was found and str was thus updated.
731 */
732bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
733{
734 const Node* pAttr;
735 if ((pAttr = findAttribute(pcszMatch)))
736 {
737 ppcsz = pAttr->getValue();
738 return true;
739 }
740
741 return false;
742}
743
744/**
745 * Convenience method which attempts to find the attribute with the given
746 * name and returns its value as a string.
747 *
748 * @param pcszMatch name of attribute to find.
749 * @param str out: attribute value
750 * @return TRUE if attribute was found and str was thus updated.
751 */
752bool ElementNode::getAttributeValue(const char *pcszMatch, iprt::MiniString &str) const
753{
754 const Node* pAttr;
755 if ((pAttr = findAttribute(pcszMatch)))
756 {
757 str = pAttr->getValue();
758 return true;
759 }
760
761 return false;
762}
763
764/**
765 * Convenience method which attempts to find the attribute with the given
766 * name and returns its value as a signed integer. This calls
767 * RTStrToInt32Ex internally and will only output the integer if that
768 * function returns no error.
769 *
770 * @param pcszMatch name of attribute to find.
771 * @param i out: attribute value
772 * @return TRUE if attribute was found and str was thus updated.
773 */
774bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
775{
776 const char *pcsz;
777 if ( (getAttributeValue(pcszMatch, pcsz))
778 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
779 )
780 return true;
781
782 return false;
783}
784
785/**
786 * Convenience method which attempts to find the attribute with the given
787 * name and returns its value as an unsigned integer.This calls
788 * RTStrToUInt32Ex internally and will only output the integer if that
789 * function returns no error.
790 *
791 * @param pcszMatch name of attribute to find.
792 * @param i out: attribute value
793 * @return TRUE if attribute was found and str was thus updated.
794 */
795bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
796{
797 const char *pcsz;
798 if ( (getAttributeValue(pcszMatch, pcsz))
799 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
800 )
801 return true;
802
803 return false;
804}
805
806/**
807 * Convenience method which attempts to find the attribute with the given
808 * name and returns its value as a signed long integer. This calls
809 * RTStrToInt64Ex internally and will only output the integer if that
810 * function returns no error.
811 *
812 * @param pcszMatch name of attribute to find.
813 * @param i out: attribute value
814 * @return TRUE if attribute was found and str was thus updated.
815 */
816bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
817{
818 const char *pcsz;
819 if ( (getAttributeValue(pcszMatch, pcsz))
820 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
821 )
822 return true;
823
824 return false;
825}
826
827/**
828 * Convenience method which attempts to find the attribute with the given
829 * name and returns its value as an unsigned long integer.This calls
830 * RTStrToUInt64Ex internally and will only output the integer if that
831 * function returns no error.
832 *
833 * @param pcszMatch name of attribute to find.
834 * @param i out: attribute value
835 * @return TRUE if attribute was found and str was thus updated.
836 */
837bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
838{
839 const char *pcsz;
840 if ( (getAttributeValue(pcszMatch, pcsz))
841 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
842 )
843 return true;
844
845 return false;
846}
847
848/**
849 * Convenience method which attempts to find the attribute with the given
850 * name and returns its value as a boolean. This accepts "true", "false",
851 * "yes", "no", "1" or "0" as valid values.
852 *
853 * @param pcszMatch name of attribute to find.
854 * @param i out: attribute value
855 * @return TRUE if attribute was found and str was thus updated.
856 */
857bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const
858{
859 const char *pcsz;
860 if (getAttributeValue(pcszMatch, pcsz))
861 {
862 if ( (!strcmp(pcsz, "true"))
863 || (!strcmp(pcsz, "yes"))
864 || (!strcmp(pcsz, "1"))
865 )
866 {
867 f = true;
868 return true;
869 }
870 if ( (!strcmp(pcsz, "false"))
871 || (!strcmp(pcsz, "no"))
872 || (!strcmp(pcsz, "0"))
873 )
874 {
875 f = false;
876 return true;
877 }
878 }
879
880 return false;
881}
882
883/**
884 * Creates a new child element node and appends it to the list
885 * of children in "this".
886 *
887 * @param pcszElementName
888 * @return
889 */
890ElementNode* ElementNode::createChild(const char *pcszElementName)
891{
892 // we must be an element, not an attribute
893 if (!m->plibNode)
894 throw ENodeIsNotElement(RT_SRC_POS);
895
896 // libxml side: create new node
897 xmlNode *plibNode;
898 if (!(plibNode = xmlNewNode(NULL, // namespace
899 (const xmlChar*)pcszElementName)))
900 throw std::bad_alloc();
901 xmlAddChild(m->plibNode, plibNode);
902
903 // now wrap this in C++
904 ElementNode *p = new ElementNode;
905 boost::shared_ptr<ElementNode> pNew(p);
906 pNew->m->plibNode = plibNode;
907 pNew->m->pcszName = (const char*)plibNode->name;
908
909 m->children.push_back(pNew);
910
911 return p;
912}
913
914
915/**
916 * Creates a content node and appends it to the list of children
917 * in "this".
918 *
919 * @param pcszElementName
920 * @return
921 */
922ContentNode* ElementNode::addContent(const char *pcszContent)
923{
924 // libxml side: create new node
925 xmlNode *plibNode;
926 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
927 throw std::bad_alloc();
928 xmlAddChild(m->plibNode, plibNode);
929
930 // now wrap this in C++
931 ContentNode *p = new ContentNode;
932 boost::shared_ptr<ContentNode> pNew(p);
933 pNew->m->plibNode = plibNode;
934 pNew->m->pcszName = NULL;
935
936 m->children.push_back(pNew);
937
938 return p;
939}
940
941/**
942 * Sets the given attribute.
943 *
944 * If an attribute with the given name exists, it is overwritten,
945 * otherwise a new attribute is created. Returns the attribute node
946 * that was either created or changed.
947 *
948 * @param pcszName
949 * @param pcszValue
950 * @return
951 */
952AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
953{
954 Data::AttributesMap::const_iterator it;
955
956 it = m->attribs.find(pcszName);
957 if (it == m->attribs.end())
958 {
959 // libxml side: xmlNewProp creates an attribute
960 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
961 const char *pcszAttribName = (const char*)plibAttr->name;
962
963 // C++ side: create an attribute node around it
964 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
965 pNew->m->plibAttr = plibAttr;
966 pNew->m->pcszName = (const char*)plibAttr->name;
967 pNew->m->pParent = this;
968 // store
969 m->attribs[pcszAttribName] = pNew;
970 }
971 else
972 {
973 // @todo
974 throw LogicError("Attribute exists");
975 }
976
977 return NULL;
978
979}
980
981AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
982{
983 char *psz = NULL;
984 RTStrAPrintf(&psz, "%RI32", i);
985 AttributeNode *p = setAttribute(pcszName, psz);
986 RTStrFree(psz);
987 return p;
988}
989
990AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t i)
991{
992 char *psz = NULL;
993 RTStrAPrintf(&psz, "%RU32", i);
994 AttributeNode *p = setAttribute(pcszName, psz);
995 RTStrFree(psz);
996 return p;
997}
998
999AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1000{
1001 char *psz = NULL;
1002 RTStrAPrintf(&psz, "%RI64", i);
1003 AttributeNode *p = setAttribute(pcszName, psz);
1004 RTStrFree(psz);
1005 return p;
1006}
1007
1008AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t i)
1009{
1010 char *psz = NULL;
1011 RTStrAPrintf(&psz, "%RU64", i);
1012 AttributeNode *p = setAttribute(pcszName, psz);
1013 RTStrFree(psz);
1014 return p;
1015}
1016
1017AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t i)
1018{
1019 char *psz = NULL;
1020 RTStrAPrintf(&psz, "0x%RX32", i);
1021 AttributeNode *p = setAttribute(pcszName, psz);
1022 RTStrFree(psz);
1023 return p;
1024}
1025
1026AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1027{
1028 return setAttribute(pcszName, (f) ? "true" : "false");
1029}
1030
1031AttributeNode::AttributeNode()
1032 : Node(IsAttribute)
1033{
1034}
1035
1036ContentNode::ContentNode()
1037 : Node(IsContent)
1038{
1039}
1040
1041/*
1042 * NodesLoop
1043 *
1044 */
1045
1046struct NodesLoop::Data
1047{
1048 ElementNodesList listElements;
1049 ElementNodesList::const_iterator it;
1050};
1051
1052NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1053{
1054 m = new Data;
1055 node.getChildElements(m->listElements, pcszMatch);
1056 m->it = m->listElements.begin();
1057}
1058
1059NodesLoop::~NodesLoop()
1060{
1061 delete m;
1062}
1063
1064
1065/**
1066 * Handy convenience helper for looping over all child elements. Create an
1067 * instance of NodesLoop on the stack and call this method until it returns
1068 * NULL, like this:
1069 * <code>
1070 * xml::ElementNode node; // should point to an element
1071 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1072 * const xml::ElementNode *pChild = NULL;
1073 * while (pChild = loop.forAllNodes())
1074 * ...;
1075 * </code>
1076 * @param node
1077 * @param pcszMatch
1078 * @return
1079 */
1080const ElementNode* NodesLoop::forAllNodes() const
1081{
1082 const ElementNode *pNode = NULL;
1083
1084 if (m->it != m->listElements.end())
1085 {
1086 pNode = *(m->it);
1087 ++(m->it);
1088 }
1089
1090 return pNode;
1091}
1092
1093////////////////////////////////////////////////////////////////////////////////
1094//
1095// Document class
1096//
1097////////////////////////////////////////////////////////////////////////////////
1098
1099struct Document::Data
1100{
1101 xmlDocPtr plibDocument;
1102 ElementNode *pRootElement;
1103
1104 Data()
1105 {
1106 plibDocument = NULL;
1107 pRootElement = NULL;
1108 }
1109
1110 ~Data()
1111 {
1112 reset();
1113 }
1114
1115 void reset()
1116 {
1117 if (plibDocument)
1118 {
1119 xmlFreeDoc(plibDocument);
1120 plibDocument = NULL;
1121 }
1122 if (pRootElement)
1123 {
1124 delete pRootElement;
1125 pRootElement = NULL;
1126 }
1127 }
1128
1129 void copyFrom(const Document::Data *p)
1130 {
1131 if (p->plibDocument)
1132 {
1133 plibDocument = xmlCopyDoc(p->plibDocument,
1134 1); // recursive == copy all
1135 }
1136 }
1137};
1138
1139Document::Document()
1140 : m(new Data)
1141{
1142}
1143
1144Document::Document(const Document &x)
1145 : m(new Data)
1146{
1147 m->copyFrom(x.m);
1148}
1149
1150Document& Document::operator=(const Document &x)
1151{
1152 m->reset();
1153 m->copyFrom(x.m);
1154 return *this;
1155}
1156
1157Document::~Document()
1158{
1159 delete m;
1160}
1161
1162/**
1163 * private method to refresh all internal structures after the internal pDocument
1164 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1165 * called before to make sure all members except the internal pDocument are clean.
1166 */
1167void Document::refreshInternals() // private
1168{
1169 m->pRootElement = new ElementNode();
1170 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
1171 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
1172
1173 m->pRootElement->buildChildren();
1174}
1175
1176/**
1177 * Returns the root element of the document, or NULL if the document is empty.
1178 * Const variant.
1179 * @return
1180 */
1181const ElementNode* Document::getRootElement() const
1182{
1183 return m->pRootElement;
1184}
1185
1186/**
1187 * Returns the root element of the document, or NULL if the document is empty.
1188 * Non-const variant.
1189 * @return
1190 */
1191ElementNode* Document::getRootElement()
1192{
1193 return m->pRootElement;
1194}
1195
1196/**
1197 * Creates a new element node and sets it as the root element. This will
1198 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1199 */
1200ElementNode* Document::createRootElement(const char *pcszRootElementName)
1201{
1202 if (m->pRootElement || m->plibDocument)
1203 throw EDocumentNotEmpty(RT_SRC_POS);
1204
1205 // libxml side: create document, create root node
1206 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1207 xmlNode *plibRootNode;
1208 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1209 (const xmlChar*)pcszRootElementName)))
1210 throw std::bad_alloc();
1211 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1212
1213 // now wrap this in C++
1214 m->pRootElement = new ElementNode();
1215 m->pRootElement->m->plibNode = plibRootNode;
1216 m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
1217
1218 return m->pRootElement;
1219}
1220
1221////////////////////////////////////////////////////////////////////////////////
1222//
1223// XmlParserBase class
1224//
1225////////////////////////////////////////////////////////////////////////////////
1226
1227XmlParserBase::XmlParserBase()
1228{
1229 m_ctxt = xmlNewParserCtxt();
1230 if (m_ctxt == NULL)
1231 throw std::bad_alloc();
1232}
1233
1234XmlParserBase::~XmlParserBase()
1235{
1236 xmlFreeParserCtxt (m_ctxt);
1237 m_ctxt = NULL;
1238}
1239
1240////////////////////////////////////////////////////////////////////////////////
1241//
1242// XmlFileParser class
1243//
1244////////////////////////////////////////////////////////////////////////////////
1245
1246struct XmlFileParser::Data
1247{
1248 xmlParserCtxtPtr ctxt;
1249 iprt::MiniString strXmlFilename;
1250
1251 Data()
1252 {
1253 if (!(ctxt = xmlNewParserCtxt()))
1254 throw std::bad_alloc();
1255 }
1256
1257 ~Data()
1258 {
1259 xmlFreeParserCtxt(ctxt);
1260 ctxt = NULL;
1261 }
1262};
1263
1264XmlFileParser::XmlFileParser()
1265 : XmlParserBase(),
1266 m(new Data())
1267{
1268}
1269
1270XmlFileParser::~XmlFileParser()
1271{
1272 delete m;
1273 m = NULL;
1274}
1275
1276struct IOContext
1277{
1278 File file;
1279 iprt::MiniString error;
1280
1281 IOContext(const char *pcszFilename, File::Mode mode)
1282 : file(mode, pcszFilename)
1283 {
1284 }
1285
1286 void setError(const xml::Error &x)
1287 {
1288 error = x.what();
1289 }
1290
1291 void setError(const std::exception &x)
1292 {
1293 error = x.what();
1294 }
1295};
1296
1297struct ReadContext : IOContext
1298{
1299 ReadContext(const char *pcszFilename)
1300 : IOContext(pcszFilename, File::Mode_Read)
1301 {
1302 }
1303};
1304
1305struct WriteContext : IOContext
1306{
1307 WriteContext(const char *pcszFilename)
1308 : IOContext(pcszFilename, File::Mode_Overwrite)
1309 {
1310 }
1311};
1312
1313/**
1314 * Reads the given file and fills the given Document object with its contents.
1315 * Throws XmlError on parsing errors.
1316 *
1317 * The document that is passed in will be reset before being filled if not empty.
1318 *
1319 * @param pcszFilename in: name fo file to parse.
1320 * @param doc out: document to be reset and filled with data according to file contents.
1321 */
1322void XmlFileParser::read(const iprt::MiniString &strFilename,
1323 Document &doc)
1324{
1325 GlobalLock lock;
1326// global.setExternalEntityLoader(ExternalEntityLoader);
1327
1328 m->strXmlFilename = strFilename;
1329 const char *pcszFilename = strFilename.c_str();
1330
1331 ReadContext context(pcszFilename);
1332 doc.m->reset();
1333 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1334 ReadCallback,
1335 CloseCallback,
1336 &context,
1337 pcszFilename,
1338 NULL, // encoding = auto
1339 XML_PARSE_NOBLANKS)))
1340 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1341
1342 doc.refreshInternals();
1343}
1344
1345// static
1346int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1347{
1348 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1349
1350 /* To prevent throwing exceptions while inside libxml2 code, we catch
1351 * them and forward to our level using a couple of variables. */
1352
1353 try
1354 {
1355 return pContext->file.read(aBuf, aLen);
1356 }
1357 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1358 catch (const xml::Error &err) { pContext->setError(err); }
1359 catch (const std::exception &err) { pContext->setError(err); }
1360 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1361
1362 return -1 /* failure */;
1363}
1364
1365int XmlFileParser::CloseCallback(void *aCtxt)
1366{
1367 /// @todo to be written
1368
1369 return -1;
1370}
1371
1372////////////////////////////////////////////////////////////////////////////////
1373//
1374// XmlFileWriter class
1375//
1376////////////////////////////////////////////////////////////////////////////////
1377
1378struct XmlFileWriter::Data
1379{
1380 Document *pDoc;
1381};
1382
1383XmlFileWriter::XmlFileWriter(Document &doc)
1384{
1385 m = new Data();
1386 m->pDoc = &doc;
1387}
1388
1389XmlFileWriter::~XmlFileWriter()
1390{
1391 delete m;
1392}
1393
1394void XmlFileWriter::write(const char *pcszFilename)
1395{
1396 WriteContext context(pcszFilename);
1397
1398 GlobalLock lock;
1399
1400 /* serialize to the stream */
1401 xmlIndentTreeOutput = 1;
1402 xmlTreeIndentString = " ";
1403 xmlSaveNoEmptyTags = 0;
1404
1405 xmlSaveCtxtPtr saveCtxt;
1406 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1407 CloseCallback,
1408 &context,
1409 NULL,
1410 XML_SAVE_FORMAT)))
1411 throw xml::LogicError(RT_SRC_POS);
1412
1413 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1414 if (rc == -1)
1415 {
1416 /* look if there was a forwared exception from the lower level */
1417// if (m->trappedErr.get() != NULL)
1418// m->trappedErr->rethrow();
1419
1420 /* there must be an exception from the Output implementation,
1421 * otherwise the save operation must always succeed. */
1422 throw xml::LogicError(RT_SRC_POS);
1423 }
1424
1425 xmlSaveClose(saveCtxt);
1426}
1427
1428int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1429{
1430 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1431
1432 /* To prevent throwing exceptions while inside libxml2 code, we catch
1433 * them and forward to our level using a couple of variables. */
1434 try
1435 {
1436 return pContext->file.write(aBuf, aLen);
1437 }
1438 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1439 catch (const xml::Error &err) { pContext->setError(err); }
1440 catch (const std::exception &err) { pContext->setError(err); }
1441 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1442
1443 return -1 /* failure */;
1444}
1445
1446int XmlFileWriter::CloseCallback(void *aCtxt)
1447{
1448 /// @todo to be written
1449
1450 return -1;
1451}
1452
1453
1454} // end namespace xml
1455
1456
Note: See TracBrowser for help on using the repository browser.

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