VirtualBox

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

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

IPRT/XML: do not treat all non-elements as elements (fix wrong treatment of comments)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 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->type == XML_ELEMENT_NODE)
477 pNew = boost::shared_ptr<Node>(new ElementNode);
478 else if (plibNode->type == XML_TEXT_NODE)
479 pNew = boost::shared_ptr<Node>(new ContentNode);
480 if (pNew)
481 {
482 pNew->m->plibNode = plibNode;
483 pNew->m->pcszName = (const char*)plibNode->name;
484 pNew->m->pParent = this;
485 // store
486 m->children.push_back(pNew);
487
488 // recurse for this child element to get its own children
489 pNew->buildChildren();
490 }
491
492 plibNode = plibNode->next;
493 }
494}
495
496const char* Node::getName() const
497{
498 return m->pcszName;
499}
500
501bool Node::nameEquals(const char *pcsz) const
502{
503 if (m->pcszName == pcsz)
504 return true;
505 if (m->pcszName == NULL)
506 return false;
507 if (pcsz == NULL)
508 return false;
509 return !strcmp(m->pcszName, pcsz);
510}
511
512
513/**
514 * Returns the value of a node. If this node is an attribute, returns
515 * the attribute value; if this node is an element, then this returns
516 * the element text content.
517 * @return
518 */
519const char* Node::getValue() const
520{
521 if ( (m->plibAttr)
522 && (m->plibAttr->children)
523 )
524 // libxml hides attribute values in another node created as a
525 // single child of the attribute node, and it's in the content field
526 return (const char*)m->plibAttr->children->content;
527
528 if ( (m->plibNode)
529 && (m->plibNode->children)
530 )
531 return (const char*)m->plibNode->children->content;
532
533 return NULL;
534}
535
536/**
537 * Copies the value of a node into the given integer variable.
538 * Returns TRUE only if a value was found and was actually an
539 * integer of the given type.
540 * @return
541 */
542bool Node::copyValue(int32_t &i) const
543{
544 const char *pcsz;
545 if ( ((pcsz = getValue()))
546 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
547 )
548 return true;
549
550 return false;
551}
552
553/**
554 * Copies the value of a node into the given integer variable.
555 * Returns TRUE only if a value was found and was actually an
556 * integer of the given type.
557 * @return
558 */
559bool Node::copyValue(uint32_t &i) const
560{
561 const char *pcsz;
562 if ( ((pcsz = getValue()))
563 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
564 )
565 return true;
566
567 return false;
568}
569
570/**
571 * Copies the value of a node into the given integer variable.
572 * Returns TRUE only if a value was found and was actually an
573 * integer of the given type.
574 * @return
575 */
576bool Node::copyValue(int64_t &i) const
577{
578 const char *pcsz;
579 if ( ((pcsz = getValue()))
580 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
581 )
582 return true;
583
584 return false;
585}
586
587/**
588 * Copies the value of a node into the given integer variable.
589 * Returns TRUE only if a value was found and was actually an
590 * integer of the given type.
591 * @return
592 */
593bool Node::copyValue(uint64_t &i) const
594{
595 const char *pcsz;
596 if ( ((pcsz = getValue()))
597 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
598 )
599 return true;
600
601 return false;
602}
603
604/**
605 * Returns the line number of the current node in the source XML file.
606 * Useful for error messages.
607 * @return
608 */
609int Node::getLineNumber() const
610{
611 if (m->plibAttr)
612 return m->pParent->m->plibNode->line;
613
614 return m->plibNode->line;
615}
616
617ElementNode::ElementNode()
618 : Node(IsElement)
619{
620}
621
622/**
623 * Builds a list of direct child elements of the current element that
624 * match the given string; if pcszMatch is NULL, all direct child
625 * elements are returned.
626 * @param children out: list of nodes to which children will be appended.
627 * @param pcszMatch in: match string, or NULL to return all children.
628 * @return Number of items appended to the list (0 if none).
629 */
630int ElementNode::getChildElements(ElementNodesList &children,
631 const char *pcszMatch /*= NULL*/)
632 const
633{
634 int i = 0;
635 Data::InternalNodesList::const_iterator
636 it,
637 last = m->children.end();
638 for (it = m->children.begin();
639 it != last;
640 ++it)
641 {
642 // export this child node if ...
643 if ( (!pcszMatch) // the caller wants all nodes or
644 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
645 )
646 {
647 Node *pNode = (*it).get();
648 if (pNode->isElement())
649 children.push_back(static_cast<ElementNode*>(pNode));
650 ++i;
651 }
652 }
653 return i;
654}
655
656/**
657 * Returns the first child element whose name matches pcszMatch.
658 * @param pcszMatch
659 * @return
660 */
661const ElementNode* ElementNode::findChildElement(const char *pcszMatch)
662 const
663{
664 Data::InternalNodesList::const_iterator
665 it,
666 last = m->children.end();
667 for (it = m->children.begin();
668 it != last;
669 ++it)
670 {
671 if ((**it).isElement())
672 {
673 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
674 if (!strcmp(pcszMatch, pelm->getName())) // the element name matches
675 return pelm;
676 }
677 }
678
679 return NULL;
680}
681
682/**
683 * Returns the first child element whose "id" attribute matches pcszId.
684 * @param pcszId identifier to look for.
685 * @return child element or NULL if not found.
686 */
687const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
688{
689 Data::InternalNodesList::const_iterator
690 it,
691 last = m->children.end();
692 for (it = m->children.begin();
693 it != last;
694 ++it)
695 {
696 if ((**it).isElement())
697 {
698 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
699 const AttributeNode *pAttr;
700 if ( ((pAttr = pelm->findAttribute("id")))
701 && (!strcmp(pAttr->getValue(), pcszId))
702 )
703 return pelm;
704 }
705 }
706
707 return NULL;
708}
709
710/**
711 *
712 * @param pcszMatch
713 * @return
714 */
715const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
716{
717 Data::AttributesMap::const_iterator it;
718
719 it = m->attribs.find(pcszMatch);
720 if (it != m->attribs.end())
721 return it->second.get();
722
723 return NULL;
724}
725
726/**
727 * Convenience method which attempts to find the attribute with the given
728 * name and returns its value as a string.
729 *
730 * @param pcszMatch name of attribute to find.
731 * @param str out: attribute value
732 * @return TRUE if attribute was found and str was thus updated.
733 */
734bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
735{
736 const Node* pAttr;
737 if ((pAttr = findAttribute(pcszMatch)))
738 {
739 ppcsz = pAttr->getValue();
740 return true;
741 }
742
743 return false;
744}
745
746/**
747 * Convenience method which attempts to find the attribute with the given
748 * name and returns its value as a string.
749 *
750 * @param pcszMatch name of attribute to find.
751 * @param str out: attribute value
752 * @return TRUE if attribute was found and str was thus updated.
753 */
754bool ElementNode::getAttributeValue(const char *pcszMatch, iprt::MiniString &str) const
755{
756 const Node* pAttr;
757 if ((pAttr = findAttribute(pcszMatch)))
758 {
759 str = pAttr->getValue();
760 return true;
761 }
762
763 return false;
764}
765
766/**
767 * Convenience method which attempts to find the attribute with the given
768 * name and returns its value as a signed integer. This calls
769 * RTStrToInt32Ex internally and will only output the integer if that
770 * function returns no error.
771 *
772 * @param pcszMatch name of attribute to find.
773 * @param i out: attribute value
774 * @return TRUE if attribute was found and str was thus updated.
775 */
776bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
777{
778 const char *pcsz;
779 if ( (getAttributeValue(pcszMatch, pcsz))
780 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
781 )
782 return true;
783
784 return false;
785}
786
787/**
788 * Convenience method which attempts to find the attribute with the given
789 * name and returns its value as an unsigned integer.This calls
790 * RTStrToUInt32Ex internally and will only output the integer if that
791 * function returns no error.
792 *
793 * @param pcszMatch name of attribute to find.
794 * @param i out: attribute value
795 * @return TRUE if attribute was found and str was thus updated.
796 */
797bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
798{
799 const char *pcsz;
800 if ( (getAttributeValue(pcszMatch, pcsz))
801 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
802 )
803 return true;
804
805 return false;
806}
807
808/**
809 * Convenience method which attempts to find the attribute with the given
810 * name and returns its value as a signed long integer. This calls
811 * RTStrToInt64Ex internally and will only output the integer if that
812 * function returns no error.
813 *
814 * @param pcszMatch name of attribute to find.
815 * @param i out: attribute value
816 * @return TRUE if attribute was found and str was thus updated.
817 */
818bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
819{
820 const char *pcsz;
821 if ( (getAttributeValue(pcszMatch, pcsz))
822 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
823 )
824 return true;
825
826 return false;
827}
828
829/**
830 * Convenience method which attempts to find the attribute with the given
831 * name and returns its value as an unsigned long integer.This calls
832 * RTStrToUInt64Ex internally and will only output the integer if that
833 * function returns no error.
834 *
835 * @param pcszMatch name of attribute to find.
836 * @param i out: attribute value
837 * @return TRUE if attribute was found and str was thus updated.
838 */
839bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
840{
841 const char *pcsz;
842 if ( (getAttributeValue(pcszMatch, pcsz))
843 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
844 )
845 return true;
846
847 return false;
848}
849
850/**
851 * Convenience method which attempts to find the attribute with the given
852 * name and returns its value as a boolean. This accepts "true", "false",
853 * "yes", "no", "1" or "0" as valid values.
854 *
855 * @param pcszMatch name of attribute to find.
856 * @param i out: attribute value
857 * @return TRUE if attribute was found and str was thus updated.
858 */
859bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const
860{
861 const char *pcsz;
862 if (getAttributeValue(pcszMatch, pcsz))
863 {
864 if ( (!strcmp(pcsz, "true"))
865 || (!strcmp(pcsz, "yes"))
866 || (!strcmp(pcsz, "1"))
867 )
868 {
869 f = true;
870 return true;
871 }
872 if ( (!strcmp(pcsz, "false"))
873 || (!strcmp(pcsz, "no"))
874 || (!strcmp(pcsz, "0"))
875 )
876 {
877 f = false;
878 return true;
879 }
880 }
881
882 return false;
883}
884
885/**
886 * Creates a new child element node and appends it to the list
887 * of children in "this".
888 *
889 * @param pcszElementName
890 * @return
891 */
892ElementNode* ElementNode::createChild(const char *pcszElementName)
893{
894 // we must be an element, not an attribute
895 if (!m->plibNode)
896 throw ENodeIsNotElement(RT_SRC_POS);
897
898 // libxml side: create new node
899 xmlNode *plibNode;
900 if (!(plibNode = xmlNewNode(NULL, // namespace
901 (const xmlChar*)pcszElementName)))
902 throw std::bad_alloc();
903 xmlAddChild(m->plibNode, plibNode);
904
905 // now wrap this in C++
906 ElementNode *p = new ElementNode;
907 boost::shared_ptr<ElementNode> pNew(p);
908 pNew->m->plibNode = plibNode;
909 pNew->m->pcszName = (const char*)plibNode->name;
910
911 m->children.push_back(pNew);
912
913 return p;
914}
915
916
917/**
918 * Creates a content node and appends it to the list of children
919 * in "this".
920 *
921 * @param pcszElementName
922 * @return
923 */
924ContentNode* ElementNode::addContent(const char *pcszContent)
925{
926 // libxml side: create new node
927 xmlNode *plibNode;
928 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
929 throw std::bad_alloc();
930 xmlAddChild(m->plibNode, plibNode);
931
932 // now wrap this in C++
933 ContentNode *p = new ContentNode;
934 boost::shared_ptr<ContentNode> pNew(p);
935 pNew->m->plibNode = plibNode;
936 pNew->m->pcszName = NULL;
937
938 m->children.push_back(pNew);
939
940 return p;
941}
942
943/**
944 * Sets the given attribute.
945 *
946 * If an attribute with the given name exists, it is overwritten,
947 * otherwise a new attribute is created. Returns the attribute node
948 * that was either created or changed.
949 *
950 * @param pcszName
951 * @param pcszValue
952 * @return
953 */
954AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
955{
956 Data::AttributesMap::const_iterator it;
957
958 it = m->attribs.find(pcszName);
959 if (it == m->attribs.end())
960 {
961 // libxml side: xmlNewProp creates an attribute
962 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
963 const char *pcszAttribName = (const char*)plibAttr->name;
964
965 // C++ side: create an attribute node around it
966 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
967 pNew->m->plibAttr = plibAttr;
968 pNew->m->pcszName = (const char*)plibAttr->name;
969 pNew->m->pParent = this;
970 // store
971 m->attribs[pcszAttribName] = pNew;
972 }
973 else
974 {
975 // @todo
976 throw LogicError("Attribute exists");
977 }
978
979 return NULL;
980
981}
982
983AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
984{
985 char *psz = NULL;
986 RTStrAPrintf(&psz, "%RI32", i);
987 AttributeNode *p = setAttribute(pcszName, psz);
988 RTStrFree(psz);
989 return p;
990}
991
992AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t i)
993{
994 char *psz = NULL;
995 RTStrAPrintf(&psz, "%RU32", i);
996 AttributeNode *p = setAttribute(pcszName, psz);
997 RTStrFree(psz);
998 return p;
999}
1000
1001AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1002{
1003 char *psz = NULL;
1004 RTStrAPrintf(&psz, "%RI64", i);
1005 AttributeNode *p = setAttribute(pcszName, psz);
1006 RTStrFree(psz);
1007 return p;
1008}
1009
1010AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t i)
1011{
1012 char *psz = NULL;
1013 RTStrAPrintf(&psz, "%RU64", i);
1014 AttributeNode *p = setAttribute(pcszName, psz);
1015 RTStrFree(psz);
1016 return p;
1017}
1018
1019AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t i)
1020{
1021 char *psz = NULL;
1022 RTStrAPrintf(&psz, "0x%RX32", i);
1023 AttributeNode *p = setAttribute(pcszName, psz);
1024 RTStrFree(psz);
1025 return p;
1026}
1027
1028AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1029{
1030 return setAttribute(pcszName, (f) ? "true" : "false");
1031}
1032
1033AttributeNode::AttributeNode()
1034 : Node(IsAttribute)
1035{
1036}
1037
1038ContentNode::ContentNode()
1039 : Node(IsContent)
1040{
1041}
1042
1043/*
1044 * NodesLoop
1045 *
1046 */
1047
1048struct NodesLoop::Data
1049{
1050 ElementNodesList listElements;
1051 ElementNodesList::const_iterator it;
1052};
1053
1054NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1055{
1056 m = new Data;
1057 node.getChildElements(m->listElements, pcszMatch);
1058 m->it = m->listElements.begin();
1059}
1060
1061NodesLoop::~NodesLoop()
1062{
1063 delete m;
1064}
1065
1066
1067/**
1068 * Handy convenience helper for looping over all child elements. Create an
1069 * instance of NodesLoop on the stack and call this method until it returns
1070 * NULL, like this:
1071 * <code>
1072 * xml::ElementNode node; // should point to an element
1073 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1074 * const xml::ElementNode *pChild = NULL;
1075 * while (pChild = loop.forAllNodes())
1076 * ...;
1077 * </code>
1078 * @param node
1079 * @param pcszMatch
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 pcszFilename 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
1458
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