VirtualBox

source: vbox/trunk/src/VBox/Main/xml/xml.cpp@ 17417

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

OVF: more XML write and export.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.6 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include "Logging.h"
22
23#include <iprt/cdefs.h>
24#include <iprt/err.h>
25#include <iprt/file.h>
26#include <iprt/lock.h>
27#include <iprt/string.h>
28
29#include <libxml/tree.h>
30#include <libxml/parser.h>
31#include <libxml/globals.h>
32#include <libxml/xmlIO.h>
33#include <libxml/xmlsave.h>
34#include <libxml/uri.h>
35
36#include <libxml/xmlschemas.h>
37
38#include <list>
39#include <map>
40#include <boost/shared_ptr.hpp>
41
42#include "VBox/xml.h"
43
44////////////////////////////////////////////////////////////////////////////////
45//
46// globals
47//
48////////////////////////////////////////////////////////////////////////////////
49
50/**
51 * Global module initialization structure. This is to wrap non-reentrant bits
52 * of libxml, among other things.
53 *
54 * The constructor and destructor of this structure are used to perform global
55 * module initiaizaton and cleanup. Thee must be only one global variable of
56 * this structure.
57 */
58static
59class Global
60{
61public:
62
63 Global()
64 {
65 /* Check the parser version. The docs say it will kill the app if
66 * there is a serious version mismatch, but I couldn't find it in the
67 * source code (it only prints the error/warning message to the console) so
68 * let's leave it as is for informational purposes. */
69 LIBXML_TEST_VERSION
70
71 /* Init libxml */
72 xmlInitParser();
73
74 /* Save the default entity resolver before someone has replaced it */
75 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
76 }
77
78 ~Global()
79 {
80 /* Shutdown libxml */
81 xmlCleanupParser();
82 }
83
84 struct
85 {
86 xmlExternalEntityLoader defaultEntityLoader;
87
88 /** Used to provide some thread safety missing in libxml2 (see e.g.
89 * XmlTreeBackend::read()) */
90 RTLockMtx lock;
91 }
92 sxml; /* XXX naming this xml will break with gcc-3.3 */
93}
94gGlobal;
95
96
97
98namespace xml
99{
100
101////////////////////////////////////////////////////////////////////////////////
102//
103// Exceptions
104//
105////////////////////////////////////////////////////////////////////////////////
106
107LogicError::LogicError(RT_SRC_POS_DECL)
108 : Error(NULL)
109{
110 char *msg = NULL;
111 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
112 pszFunction, pszFile, iLine);
113 setWhat(msg);
114 RTStrFree(msg);
115}
116
117XmlError::XmlError(xmlErrorPtr aErr)
118{
119 if (!aErr)
120 throw EInvalidArg(RT_SRC_POS);
121
122 char *msg = Format(aErr);
123 setWhat(msg);
124 RTStrFree(msg);
125}
126
127/**
128 * Composes a single message for the given error. The caller must free the
129 * returned string using RTStrFree() when no more necessary.
130 */
131// static
132char *XmlError::Format(xmlErrorPtr aErr)
133{
134 const char *msg = aErr->message ? aErr->message : "<none>";
135 size_t msgLen = strlen(msg);
136 /* strip spaces, trailing EOLs and dot-like char */
137 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
138 --msgLen;
139
140 char *finalMsg = NULL;
141 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
142 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
143
144 return finalMsg;
145}
146
147EIPRTFailure::EIPRTFailure(int aRC)
148 : RuntimeError(NULL),
149 mRC(aRC)
150{
151 char *newMsg = NULL;
152 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
153 setWhat(newMsg);
154 RTStrFree(newMsg);
155}
156
157////////////////////////////////////////////////////////////////////////////////
158//
159// File Class
160//
161//////////////////////////////////////////////////////////////////////////////
162
163struct File::Data
164{
165 Data()
166 : fileName (NULL), handle (NIL_RTFILE), opened (false) {}
167
168 char *fileName;
169 RTFILE handle;
170 bool opened : 1;
171};
172
173File::File(Mode aMode, const char *aFileName)
174 : m (new Data())
175{
176 m->fileName = RTStrDup (aFileName);
177 if (m->fileName == NULL)
178 throw ENoMemory();
179
180 unsigned flags = 0;
181 switch (aMode)
182 {
183 case Mode_Read:
184 flags = RTFILE_O_READ;
185 break;
186 case Mode_Write:
187 flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
188 break;
189 case Mode_ReadWrite:
190 flags = RTFILE_O_READ | RTFILE_O_WRITE;
191 }
192
193 int vrc = RTFileOpen (&m->handle, aFileName, flags);
194 if (RT_FAILURE (vrc))
195 throw EIPRTFailure (vrc);
196
197 m->opened = true;
198}
199
200File::File (RTFILE aHandle, const char *aFileName /* = NULL */)
201 : m (new Data())
202{
203 if (aHandle == NIL_RTFILE)
204 throw EInvalidArg (RT_SRC_POS);
205
206 m->handle = aHandle;
207
208 if (aFileName)
209 {
210 m->fileName = RTStrDup (aFileName);
211 if (m->fileName == NULL)
212 throw ENoMemory();
213 }
214
215 setPos (0);
216}
217
218File::~File()
219{
220 if (m->opened)
221 RTFileClose (m->handle);
222
223 RTStrFree (m->fileName);
224}
225
226const char *File::uri() const
227{
228 return m->fileName;
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 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 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 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}
390
391void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
392{
393 m->pOldLoader = xmlGetExternalEntityLoader();
394 xmlSetExternalEntityLoader(pLoader);
395}
396
397// static
398xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
399 const char *aID,
400 xmlParserCtxt *aCtxt)
401{
402 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
403}
404
405////////////////////////////////////////////////////////////////////////////////
406//
407// Node class
408//
409////////////////////////////////////////////////////////////////////////////////
410
411struct Node::Data
412{
413 xmlNode *plibNode; // != NULL if this is an element or content node
414 xmlAttr *plibAttr; // != NULL if this is an attribute node
415
416 Node *pParent; // NULL only for the root element
417 const char *pcszName; // element or attribute name, points either into plibNode or plibAttr;
418 // NULL if this is a content node
419
420 struct compare_const_char
421 {
422 bool operator()(const char* s1, const char* s2) const
423 {
424 return strcmp(s1, s2) < 0;
425 }
426 };
427
428 // attributes, if this is an element; can be empty
429 typedef std::map<const char*, boost::shared_ptr<Node>, compare_const_char > AttributesMap;
430 AttributesMap attribs;
431
432 // child elements, if this is an element; can be empty
433 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
434 InternalNodesList children;
435};
436
437Node::Node()
438 : m(new Data)
439{
440 m->plibNode = NULL;
441 m->plibAttr = NULL;
442 m->pParent = NULL;
443}
444
445Node::~Node()
446{
447 delete m;
448}
449
450void Node::buildChildren() // private
451{
452 // go thru this element's attributes
453 xmlAttr *plibAttr = m->plibNode->properties;
454 while (plibAttr)
455 {
456 const char *pcszAttribName = (const char*)plibAttr->name;
457 boost::shared_ptr<Node> pNew(new Node);
458 pNew->m->plibAttr = plibAttr;
459 pNew->m->pcszName = (const char*)plibAttr->name;
460 pNew->m->pParent = this;
461 // store
462 m->attribs[pcszAttribName] = pNew;
463
464 plibAttr = plibAttr->next;
465 }
466
467 // go thru this element's child elements
468 xmlNodePtr plibNode = m->plibNode->children;
469 while (plibNode)
470 {
471 // create a new Node for this child element
472 boost::shared_ptr<Node> pNew(new Node);
473 pNew->m->plibNode = plibNode;
474 pNew->m->pcszName = (const char*)plibNode->name;
475 pNew->m->pParent = this;
476 // store
477 m->children.push_back(pNew);
478
479 // recurse for this child element to get its own children
480 pNew->buildChildren();
481
482 plibNode = plibNode->next;
483 }
484}
485
486const char* Node::getName() const
487{
488 return m->pcszName;
489}
490
491/**
492 * Returns the value of a node. If this node is an attribute, returns
493 * the attribute value; if this node is an element, then this returns
494 * the element text content.
495 * @return
496 */
497const char* Node::getValue() const
498{
499 if ( (m->plibAttr)
500 && (m->plibAttr->children)
501 )
502 // libxml hides attribute values in another node created as a
503 // single child of the attribute node, and it's in the content field
504 return (const char*)m->plibAttr->children->content;
505
506 if ( (m->plibNode)
507 && (m->plibNode->children)
508 )
509 return (const char*)m->plibNode->children->content;
510
511 return NULL;
512}
513
514/**
515 * Copies the value of a node into the given integer variable.
516 * Returns TRUE only if a value was found and was actually an
517 * integer of the given type.
518 * @return
519 */
520bool Node::copyValue(int32_t &i) const
521{
522 const char *pcsz;
523 if ( ((pcsz = getValue()))
524 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
525 )
526 return true;
527
528 return false;
529}
530
531/**
532 * Copies the value of a node into the given integer variable.
533 * Returns TRUE only if a value was found and was actually an
534 * integer of the given type.
535 * @return
536 */
537bool Node::copyValue(uint32_t &i) const
538{
539 const char *pcsz;
540 if ( ((pcsz = getValue()))
541 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
542 )
543 return true;
544
545 return false;
546}
547
548/**
549 * Copies the value of a node into the given integer variable.
550 * Returns TRUE only if a value was found and was actually an
551 * integer of the given type.
552 * @return
553 */
554bool Node::copyValue(int64_t &i) const
555{
556 const char *pcsz;
557 if ( ((pcsz = getValue()))
558 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
559 )
560 return true;
561
562 return false;
563}
564
565/**
566 * Copies the value of a node into the given integer variable.
567 * Returns TRUE only if a value was found and was actually an
568 * integer of the given type.
569 * @return
570 */
571bool Node::copyValue(uint64_t &i) const
572{
573 const char *pcsz;
574 if ( ((pcsz = getValue()))
575 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
576 )
577 return true;
578
579 return false;
580}
581
582/**
583 * Returns the line number of the current node in the source XML file.
584 * Useful for error messages.
585 * @return
586 */
587int Node::getLineNumber() const
588{
589 if (m->plibAttr)
590 return m->pParent->m->plibNode->line;
591
592 return m->plibNode->line;
593}
594
595/**
596 * Builds a list of direct child elements of the current element that
597 * match the given string; if pcszMatch is NULL, all direct child
598 * elements are returned.
599 * @param children out: list of nodes to which children will be appended.
600 * @param pcszMatch in: match string, or NULL to return all children.
601 * @return Number of items appended to the list (0 if none).
602 */
603int Node::getChildElements(NodesList &children,
604 const char *pcszMatch /*= NULL*/)
605 const
606{
607 int i = 0;
608 Data::InternalNodesList::const_iterator
609 it,
610 last = m->children.end();
611 for (it = m->children.begin();
612 it != last;
613 ++it)
614 {
615 // export this child node if ...
616 if ( (!pcszMatch) // the caller wants all nodes or
617 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
618 )
619 {
620 children.push_back((*it).get());
621 ++i;
622 }
623 }
624 return i;
625}
626
627/**
628 * Returns the first child element whose name matches pcszMatch.
629 * @param pcszMatch
630 * @return
631 */
632const Node* Node::findChildElement(const char *pcszMatch)
633 const
634{
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 if (!strcmp(pcszMatch, (**it).getName())) // the element name matches
643 return (*it).get();
644 }
645
646 return NULL;
647}
648
649/**
650 * Returns the first child element whose "id" attribute matches pcszId.
651 * @param pcszId identifier to look for.
652 * @return child element or NULL if not found.
653 */
654const Node* Node::findChildElementFromId(const char *pcszId) const
655{
656 Data::InternalNodesList::const_iterator
657 it,
658 last = m->children.end();
659 for (it = m->children.begin();
660 it != last;
661 ++it)
662 {
663 const Node *pElem = (*it).get();
664 const Node *pAttr;
665 if ( ((pAttr = pElem->findAttribute("id")))
666 && (!strcmp(pAttr->getValue(), pcszId))
667 )
668 return pElem;
669 }
670
671 return NULL;
672}
673
674/**
675 *
676 * @param pcszMatch
677 * @return
678 */
679const Node* Node::findAttribute(const char *pcszMatch) const
680{
681 Data::AttributesMap::const_iterator it;
682
683 it = m->attribs.find(pcszMatch);
684 if (it != m->attribs.end())
685 return it->second.get();
686
687 return NULL;
688}
689
690/**
691 * Convenience method which attempts to find the attribute with the given
692 * name and returns its value as a string.
693 *
694 * @param pcszMatch name of attribute to find.
695 * @param str out: attribute value
696 * @return TRUE if attribute was found and str was thus updated.
697 */
698bool Node::getAttributeValue(const char *pcszMatch, com::Utf8Str &str) const
699{
700 const Node* pAttr;
701 if ((pAttr = findAttribute(pcszMatch)))
702 {
703 str = pAttr->getValue();
704 return true;
705 }
706
707 return false;
708}
709
710/**
711 * Convenience method which attempts to find the attribute with the given
712 * name and returns its value as a signed long integer. This calls
713 * RTStrToInt64Ex internally and will only output the integer if that
714 * function returns no error.
715 *
716 * @param pcszMatch name of attribute to find.
717 * @param i out: attribute value
718 * @return TRUE if attribute was found and str was thus updated.
719 */
720bool Node::getAttributeValue(const char *pcszMatch, int64_t &i) const
721{
722 com::Utf8Str str;
723 if ( (getAttributeValue(pcszMatch, str))
724 && (VINF_SUCCESS == RTStrToInt64Ex(str.c_str(), NULL, 10, &i))
725 )
726 return true;
727
728 return false;
729}
730
731/**
732 * Convenience method which attempts to find the attribute with the given
733 * name and returns its value as an unsigned long integer.This calls
734 * RTStrToUInt64Ex internally and will only output the integer if that
735 * function returns no error.
736 *
737 * @param pcszMatch name of attribute to find.
738 * @param i out: attribute value
739 * @return TRUE if attribute was found and str was thus updated.
740 */
741bool Node::getAttributeValue(const char *pcszMatch, uint64_t &i) const
742{
743 com::Utf8Str str;
744 if ( (getAttributeValue(pcszMatch, str))
745 && (VINF_SUCCESS == RTStrToUInt64Ex(str.c_str(), NULL, 10, &i))
746 )
747 return true;
748
749 return false;
750}
751
752/**
753 * Creates a new child element node and appends it to the list
754 * of children in "this".
755 *
756 * @param pcszElementName
757 * @return
758 */
759Node* Node::createChild(const char *pcszElementName)
760{
761 // we must be an element, not an attribute
762 if (!m->plibNode)
763 throw ENodeIsNotElement(RT_SRC_POS);
764
765 // libxml side: create new node
766 xmlNode *plibNode;
767 if (!(plibNode = xmlNewNode(NULL, // namespace
768 (const xmlChar*)pcszElementName)))
769 throw ENoMemory();
770 xmlAddChild(m->plibNode, plibNode);
771
772 // now wrap this in C++
773 Node *p = new Node;
774 boost::shared_ptr<Node> pNew(p);
775 pNew->m->plibNode = plibNode;
776 pNew->m->pcszName = (const char*)plibNode->name;
777
778 m->children.push_back(pNew);
779
780 return p;
781}
782
783
784/**
785 * Creates a content node and appends it to the list of children
786 * in "this".
787 *
788 * @param pcszElementName
789 * @return
790 */
791Node* Node::addContent(const char *pcszContent)
792{
793 // we must be an element, not an attribute
794 if (!m->plibNode)
795 throw ENodeIsNotElement(RT_SRC_POS);
796
797 // libxml side: create new node
798 xmlNode *plibNode;
799 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
800 throw ENoMemory();
801 xmlAddChild(m->plibNode, plibNode);
802
803 // now wrap this in C++
804 Node *p = new Node;
805 boost::shared_ptr<Node> pNew(p);
806 pNew->m->plibNode = plibNode;
807 pNew->m->pcszName = NULL;
808
809 m->children.push_back(pNew);
810
811}
812
813/**
814 * Sets the given attribute. Assumes that "this" is an element node,
815 * otherwise ENodeIsNotElement is thrown.
816 *
817 * If an attribute with the given name exists, it is overwritten,
818 * otherwise a new attribute is created. Returns the attribute node
819 * that was either created or changed.
820 *
821 * @param pcszName
822 * @param pcszValue
823 * @return
824 */
825Node* Node::setAttribute(const char *pcszName, const char *pcszValue)
826{
827 // we must be an element, not an attribute
828 if (!m->plibNode)
829 throw ENodeIsNotElement(RT_SRC_POS);
830
831 Data::AttributesMap::const_iterator it;
832
833 it = m->attribs.find(pcszName);
834 if (it == m->attribs.end())
835 {
836 // libxml side: xmlNewProp creates an attribute
837 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
838 const char *pcszAttribName = (const char*)plibAttr->name;
839
840 // C++ side: create an attribute node around it
841 boost::shared_ptr<Node> pNew(new Node);
842 pNew->m->plibAttr = plibAttr;
843 pNew->m->pcszName = (const char*)plibAttr->name;
844 pNew->m->pParent = this;
845 // store
846 m->attribs[pcszAttribName] = pNew;
847 }
848 else
849 {
850 // @todo
851 throw LogicError("Attribute exists");
852 }
853
854 return NULL;
855
856}
857
858
859/*
860 * NodesLoop
861 *
862 */
863
864struct NodesLoop::Data
865{
866 NodesList listElements;
867 NodesList::const_iterator it;
868};
869
870NodesLoop::NodesLoop(const Node &node, const char *pcszMatch /* = NULL */)
871{
872 m = new Data;
873 node.getChildElements(m->listElements, pcszMatch);
874 m->it = m->listElements.begin();
875}
876
877NodesLoop::~NodesLoop()
878{
879 delete m;
880}
881
882
883/**
884 * Handy convenience helper for looping over all child elements. Create an
885 * instance of NodesLoop on the stack and call this method until it returns
886 * NULL, like this:
887 * <code>
888 * xml::Node node; // should point to an element
889 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
890 * const xml::Node *pChild = NULL;
891 * while (pChild = loop.forAllNodes())
892 * ...;
893 * </code>
894 * @param node
895 * @param pcszMatch
896 * @return
897 */
898const Node* NodesLoop::forAllNodes() const
899{
900 const Node *pNode = NULL;
901
902 if (m->it != m->listElements.end())
903 {
904 pNode = *(m->it);
905 ++(m->it);
906 }
907
908 return pNode;
909}
910
911////////////////////////////////////////////////////////////////////////////////
912//
913// Document class
914//
915////////////////////////////////////////////////////////////////////////////////
916
917struct Document::Data
918{
919 xmlDocPtr plibDocument;
920 Node *pRootElement;
921
922 Data()
923 {
924 plibDocument = NULL;
925 pRootElement = NULL;
926 }
927
928 ~Data()
929 {
930 reset();
931 }
932
933 void reset()
934 {
935 if (plibDocument)
936 {
937 xmlFreeDoc(plibDocument);
938 plibDocument = NULL;
939 }
940 if (pRootElement)
941 {
942 delete pRootElement;
943 pRootElement = NULL;
944 }
945 }
946
947 void copyFrom(const Document::Data *p)
948 {
949 if (p->plibDocument)
950 {
951 plibDocument = xmlCopyDoc(p->plibDocument,
952 1); // recursive == copy all
953 }
954 }
955};
956
957Document::Document()
958 : m(new Data)
959{
960}
961
962Document::Document(const Document &x)
963 : m(new Data)
964{
965 m->copyFrom(x.m);
966};
967
968Document& Document::operator=(const Document &x)
969{
970 m->reset();
971 m->copyFrom(x.m);
972 return *this;
973};
974
975Document::~Document()
976{
977 delete m;
978}
979
980/**
981 * private method to refresh all internal structures after the internal pDocument
982 * has changed. Called from XmlFileParser::read(). m->reset() must have been
983 * called before to make sure all members except the internal pDocument are clean.
984 */
985void Document::refreshInternals() // private
986{
987 m->pRootElement = new Node();
988 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
989 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
990
991 m->pRootElement->buildChildren();
992}
993
994/**
995 * Returns the root element of the document, or NULL if the document is empty.
996 * @return
997 */
998const Node* Document::getRootElement() const
999{
1000 return m->pRootElement;
1001}
1002
1003/**
1004 * Creates a new element node and sets it as the root element. This will
1005 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1006 */
1007Node* Document::createRootElement(const char *pcszRootElementName)
1008{
1009 if (m->pRootElement || m->plibDocument)
1010 throw EDocumentNotEmpty(RT_SRC_POS);
1011
1012 // libxml side: create document, create root node
1013 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1014 xmlNode *plibRootNode;
1015 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1016 (const xmlChar*)pcszRootElementName)))
1017 throw ENoMemory();
1018 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1019
1020 // now wrap this in C++
1021 m->pRootElement = new Node();
1022 m->pRootElement->m->plibNode = plibRootNode;
1023 m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
1024
1025 return m->pRootElement;
1026}
1027
1028////////////////////////////////////////////////////////////////////////////////
1029//
1030// XmlParserBase class
1031//
1032////////////////////////////////////////////////////////////////////////////////
1033
1034XmlParserBase::XmlParserBase()
1035{
1036 m_ctxt = xmlNewParserCtxt();
1037 if (m_ctxt == NULL)
1038 throw ENoMemory();
1039}
1040
1041XmlParserBase::~XmlParserBase()
1042{
1043 xmlFreeParserCtxt (m_ctxt);
1044 m_ctxt = NULL;
1045}
1046
1047////////////////////////////////////////////////////////////////////////////////
1048//
1049// XmlFileParser class
1050//
1051////////////////////////////////////////////////////////////////////////////////
1052
1053struct XmlFileParser::Data
1054{
1055 xmlParserCtxtPtr ctxt;
1056 com::Utf8Str strXmlFilename;
1057
1058 Data()
1059 {
1060 if (!(ctxt = xmlNewParserCtxt()))
1061 throw xml::ENoMemory();
1062 }
1063
1064 ~Data()
1065 {
1066 xmlFreeParserCtxt(ctxt);
1067 ctxt = NULL;
1068 }
1069};
1070
1071XmlFileParser::XmlFileParser()
1072 : XmlParserBase(),
1073 m(new Data())
1074{
1075}
1076
1077XmlFileParser::~XmlFileParser()
1078{
1079}
1080
1081struct IOContext
1082{
1083 File file;
1084 com::Utf8Str error;
1085
1086 IOContext(const char *pcszFilename, File::Mode mode)
1087 : file(mode, pcszFilename)
1088 {
1089 }
1090
1091 void setError(const xml::Error &x)
1092 {
1093 error = x.what();
1094 }
1095
1096 void setError(const std::exception &x)
1097 {
1098 error = x.what();
1099 }
1100};
1101
1102struct ReadContext : IOContext
1103{
1104 ReadContext(const char *pcszFilename)
1105 : IOContext(pcszFilename, File::Mode_Read)
1106 {
1107 }
1108};
1109
1110struct WriteContext : IOContext
1111{
1112 WriteContext(const char *pcszFilename)
1113 : IOContext(pcszFilename, File::Mode_Write)
1114 {
1115 }
1116};
1117
1118/**
1119 * Reads the given file and fills the given Document object with its contents.
1120 * Throws XmlError on parsing errors.
1121 *
1122 * The document that is passed in will be reset before being filled if not empty.
1123 *
1124 * @param pcszFilename in: name fo file to parse.
1125 * @param doc out: document to be reset and filled with data according to file contents.
1126 */
1127void XmlFileParser::read(const char *pcszFilename,
1128 Document &doc)
1129{
1130 GlobalLock lock();
1131// global.setExternalEntityLoader(ExternalEntityLoader);
1132
1133 m->strXmlFilename = pcszFilename;
1134
1135 ReadContext context(pcszFilename);
1136 doc.m->reset();
1137 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1138 ReadCallback,
1139 CloseCallback,
1140 &context,
1141 pcszFilename,
1142 NULL, // encoding = auto
1143 XML_PARSE_NOBLANKS)))
1144 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1145
1146 doc.refreshInternals();
1147}
1148
1149// static
1150int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1151{
1152 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1153
1154 /* To prevent throwing exceptions while inside libxml2 code, we catch
1155 * them and forward to our level using a couple of variables. */
1156
1157 try
1158 {
1159 return pContext->file.read(aBuf, aLen);
1160 }
1161 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1162 catch (const xml::Error &err) { pContext->setError(err); }
1163 catch (const std::exception &err) { pContext->setError(err); }
1164 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1165
1166 return -1 /* failure */;
1167}
1168
1169int XmlFileParser::CloseCallback(void *aCtxt)
1170{
1171 /// @todo to be written
1172
1173 return -1;
1174}
1175
1176////////////////////////////////////////////////////////////////////////////////
1177//
1178// XmlFileWriter class
1179//
1180////////////////////////////////////////////////////////////////////////////////
1181
1182struct XmlFileWriter::Data
1183{
1184 Document *pDoc;
1185};
1186
1187XmlFileWriter::XmlFileWriter(Document &doc)
1188{
1189 m = new Data();
1190 m->pDoc = &doc;
1191}
1192
1193XmlFileWriter::~XmlFileWriter()
1194{
1195 delete m;
1196}
1197
1198void XmlFileWriter::write(const char *pcszFilename)
1199{
1200 WriteContext context(pcszFilename);
1201
1202 GlobalLock lock();
1203
1204 /* serialize to the stream */
1205 xmlIndentTreeOutput = 1;
1206 xmlTreeIndentString = " ";
1207 xmlSaveNoEmptyTags = 0;
1208
1209 xmlSaveCtxtPtr saveCtxt;
1210 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1211 CloseCallback,
1212 &context,
1213 NULL,
1214 XML_SAVE_FORMAT)))
1215 throw xml::LogicError(RT_SRC_POS);
1216
1217 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1218 if (rc == -1)
1219 {
1220 /* look if there was a forwared exception from the lower level */
1221// if (m->trappedErr.get() != NULL)
1222// m->trappedErr->rethrow();
1223
1224 /* there must be an exception from the Output implementation,
1225 * otherwise the save operation must always succeed. */
1226 throw xml::LogicError(RT_SRC_POS);
1227 }
1228
1229 xmlSaveClose(saveCtxt);
1230}
1231
1232int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1233{
1234 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1235
1236 /* To prevent throwing exceptions while inside libxml2 code, we catch
1237 * them and forward to our level using a couple of variables. */
1238 try
1239 {
1240 return pContext->file.write(aBuf, aLen);
1241 }
1242 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1243 catch (const xml::Error &err) { pContext->setError(err); }
1244 catch (const std::exception &err) { pContext->setError(err); }
1245 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1246
1247 return -1 /* failure */;
1248}
1249
1250int XmlFileWriter::CloseCallback(void *aCtxt)
1251{
1252 /// @todo to be written
1253
1254 return -1;
1255}
1256
1257
1258} // end namespace xml
1259
1260
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