VirtualBox

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

Last change on this file since 21409 was 21409, checked in by vboxsync, 16 years ago

IPRT: rename ministring, add iprt:: namespace

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

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