VirtualBox

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

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

Runtime: white space cleanup.

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