VirtualBox

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

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

Main: remove ENoMemory exception class, replace with std::bad_alloc() to avoid string allocations after memory allocation failed already

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