VirtualBox

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

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

Main-XML: return missing

  • 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 return p;
812}
813
814/**
815 * Sets the given attribute. Assumes that "this" is an element node,
816 * otherwise ENodeIsNotElement is thrown.
817 *
818 * If an attribute with the given name exists, it is overwritten,
819 * otherwise a new attribute is created. Returns the attribute node
820 * that was either created or changed.
821 *
822 * @param pcszName
823 * @param pcszValue
824 * @return
825 */
826Node* Node::setAttribute(const char *pcszName, const char *pcszValue)
827{
828 // we must be an element, not an attribute
829 if (!m->plibNode)
830 throw ENodeIsNotElement(RT_SRC_POS);
831
832 Data::AttributesMap::const_iterator it;
833
834 it = m->attribs.find(pcszName);
835 if (it == m->attribs.end())
836 {
837 // libxml side: xmlNewProp creates an attribute
838 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
839 const char *pcszAttribName = (const char*)plibAttr->name;
840
841 // C++ side: create an attribute node around it
842 boost::shared_ptr<Node> pNew(new Node);
843 pNew->m->plibAttr = plibAttr;
844 pNew->m->pcszName = (const char*)plibAttr->name;
845 pNew->m->pParent = this;
846 // store
847 m->attribs[pcszAttribName] = pNew;
848 }
849 else
850 {
851 // @todo
852 throw LogicError("Attribute exists");
853 }
854
855 return NULL;
856
857}
858
859
860/*
861 * NodesLoop
862 *
863 */
864
865struct NodesLoop::Data
866{
867 NodesList listElements;
868 NodesList::const_iterator it;
869};
870
871NodesLoop::NodesLoop(const Node &node, const char *pcszMatch /* = NULL */)
872{
873 m = new Data;
874 node.getChildElements(m->listElements, pcszMatch);
875 m->it = m->listElements.begin();
876}
877
878NodesLoop::~NodesLoop()
879{
880 delete m;
881}
882
883
884/**
885 * Handy convenience helper for looping over all child elements. Create an
886 * instance of NodesLoop on the stack and call this method until it returns
887 * NULL, like this:
888 * <code>
889 * xml::Node node; // should point to an element
890 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
891 * const xml::Node *pChild = NULL;
892 * while (pChild = loop.forAllNodes())
893 * ...;
894 * </code>
895 * @param node
896 * @param pcszMatch
897 * @return
898 */
899const Node* NodesLoop::forAllNodes() const
900{
901 const Node *pNode = NULL;
902
903 if (m->it != m->listElements.end())
904 {
905 pNode = *(m->it);
906 ++(m->it);
907 }
908
909 return pNode;
910}
911
912////////////////////////////////////////////////////////////////////////////////
913//
914// Document class
915//
916////////////////////////////////////////////////////////////////////////////////
917
918struct Document::Data
919{
920 xmlDocPtr plibDocument;
921 Node *pRootElement;
922
923 Data()
924 {
925 plibDocument = NULL;
926 pRootElement = NULL;
927 }
928
929 ~Data()
930 {
931 reset();
932 }
933
934 void reset()
935 {
936 if (plibDocument)
937 {
938 xmlFreeDoc(plibDocument);
939 plibDocument = NULL;
940 }
941 if (pRootElement)
942 {
943 delete pRootElement;
944 pRootElement = NULL;
945 }
946 }
947
948 void copyFrom(const Document::Data *p)
949 {
950 if (p->plibDocument)
951 {
952 plibDocument = xmlCopyDoc(p->plibDocument,
953 1); // recursive == copy all
954 }
955 }
956};
957
958Document::Document()
959 : m(new Data)
960{
961}
962
963Document::Document(const Document &x)
964 : m(new Data)
965{
966 m->copyFrom(x.m);
967};
968
969Document& Document::operator=(const Document &x)
970{
971 m->reset();
972 m->copyFrom(x.m);
973 return *this;
974};
975
976Document::~Document()
977{
978 delete m;
979}
980
981/**
982 * private method to refresh all internal structures after the internal pDocument
983 * has changed. Called from XmlFileParser::read(). m->reset() must have been
984 * called before to make sure all members except the internal pDocument are clean.
985 */
986void Document::refreshInternals() // private
987{
988 m->pRootElement = new Node();
989 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
990 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
991
992 m->pRootElement->buildChildren();
993}
994
995/**
996 * Returns the root element of the document, or NULL if the document is empty.
997 * @return
998 */
999const Node* Document::getRootElement() const
1000{
1001 return m->pRootElement;
1002}
1003
1004/**
1005 * Creates a new element node and sets it as the root element. This will
1006 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1007 */
1008Node* Document::createRootElement(const char *pcszRootElementName)
1009{
1010 if (m->pRootElement || m->plibDocument)
1011 throw EDocumentNotEmpty(RT_SRC_POS);
1012
1013 // libxml side: create document, create root node
1014 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1015 xmlNode *plibRootNode;
1016 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1017 (const xmlChar*)pcszRootElementName)))
1018 throw ENoMemory();
1019 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1020
1021 // now wrap this in C++
1022 m->pRootElement = new Node();
1023 m->pRootElement->m->plibNode = plibRootNode;
1024 m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
1025
1026 return m->pRootElement;
1027}
1028
1029////////////////////////////////////////////////////////////////////////////////
1030//
1031// XmlParserBase class
1032//
1033////////////////////////////////////////////////////////////////////////////////
1034
1035XmlParserBase::XmlParserBase()
1036{
1037 m_ctxt = xmlNewParserCtxt();
1038 if (m_ctxt == NULL)
1039 throw ENoMemory();
1040}
1041
1042XmlParserBase::~XmlParserBase()
1043{
1044 xmlFreeParserCtxt (m_ctxt);
1045 m_ctxt = NULL;
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049//
1050// XmlFileParser class
1051//
1052////////////////////////////////////////////////////////////////////////////////
1053
1054struct XmlFileParser::Data
1055{
1056 xmlParserCtxtPtr ctxt;
1057 com::Utf8Str strXmlFilename;
1058
1059 Data()
1060 {
1061 if (!(ctxt = xmlNewParserCtxt()))
1062 throw xml::ENoMemory();
1063 }
1064
1065 ~Data()
1066 {
1067 xmlFreeParserCtxt(ctxt);
1068 ctxt = NULL;
1069 }
1070};
1071
1072XmlFileParser::XmlFileParser()
1073 : XmlParserBase(),
1074 m(new Data())
1075{
1076}
1077
1078XmlFileParser::~XmlFileParser()
1079{
1080}
1081
1082struct IOContext
1083{
1084 File file;
1085 com::Utf8Str error;
1086
1087 IOContext(const char *pcszFilename, File::Mode mode)
1088 : file(mode, pcszFilename)
1089 {
1090 }
1091
1092 void setError(const xml::Error &x)
1093 {
1094 error = x.what();
1095 }
1096
1097 void setError(const std::exception &x)
1098 {
1099 error = x.what();
1100 }
1101};
1102
1103struct ReadContext : IOContext
1104{
1105 ReadContext(const char *pcszFilename)
1106 : IOContext(pcszFilename, File::Mode_Read)
1107 {
1108 }
1109};
1110
1111struct WriteContext : IOContext
1112{
1113 WriteContext(const char *pcszFilename)
1114 : IOContext(pcszFilename, File::Mode_Write)
1115 {
1116 }
1117};
1118
1119/**
1120 * Reads the given file and fills the given Document object with its contents.
1121 * Throws XmlError on parsing errors.
1122 *
1123 * The document that is passed in will be reset before being filled if not empty.
1124 *
1125 * @param pcszFilename in: name fo file to parse.
1126 * @param doc out: document to be reset and filled with data according to file contents.
1127 */
1128void XmlFileParser::read(const char *pcszFilename,
1129 Document &doc)
1130{
1131 GlobalLock lock();
1132// global.setExternalEntityLoader(ExternalEntityLoader);
1133
1134 m->strXmlFilename = pcszFilename;
1135
1136 ReadContext context(pcszFilename);
1137 doc.m->reset();
1138 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1139 ReadCallback,
1140 CloseCallback,
1141 &context,
1142 pcszFilename,
1143 NULL, // encoding = auto
1144 XML_PARSE_NOBLANKS)))
1145 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1146
1147 doc.refreshInternals();
1148}
1149
1150// static
1151int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1152{
1153 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1154
1155 /* To prevent throwing exceptions while inside libxml2 code, we catch
1156 * them and forward to our level using a couple of variables. */
1157
1158 try
1159 {
1160 return pContext->file.read(aBuf, aLen);
1161 }
1162 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1163 catch (const xml::Error &err) { pContext->setError(err); }
1164 catch (const std::exception &err) { pContext->setError(err); }
1165 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1166
1167 return -1 /* failure */;
1168}
1169
1170int XmlFileParser::CloseCallback(void *aCtxt)
1171{
1172 /// @todo to be written
1173
1174 return -1;
1175}
1176
1177////////////////////////////////////////////////////////////////////////////////
1178//
1179// XmlFileWriter class
1180//
1181////////////////////////////////////////////////////////////////////////////////
1182
1183struct XmlFileWriter::Data
1184{
1185 Document *pDoc;
1186};
1187
1188XmlFileWriter::XmlFileWriter(Document &doc)
1189{
1190 m = new Data();
1191 m->pDoc = &doc;
1192}
1193
1194XmlFileWriter::~XmlFileWriter()
1195{
1196 delete m;
1197}
1198
1199void XmlFileWriter::write(const char *pcszFilename)
1200{
1201 WriteContext context(pcszFilename);
1202
1203 GlobalLock lock();
1204
1205 /* serialize to the stream */
1206 xmlIndentTreeOutput = 1;
1207 xmlTreeIndentString = " ";
1208 xmlSaveNoEmptyTags = 0;
1209
1210 xmlSaveCtxtPtr saveCtxt;
1211 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1212 CloseCallback,
1213 &context,
1214 NULL,
1215 XML_SAVE_FORMAT)))
1216 throw xml::LogicError(RT_SRC_POS);
1217
1218 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1219 if (rc == -1)
1220 {
1221 /* look if there was a forwared exception from the lower level */
1222// if (m->trappedErr.get() != NULL)
1223// m->trappedErr->rethrow();
1224
1225 /* there must be an exception from the Output implementation,
1226 * otherwise the save operation must always succeed. */
1227 throw xml::LogicError(RT_SRC_POS);
1228 }
1229
1230 xmlSaveClose(saveCtxt);
1231}
1232
1233int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1234{
1235 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1236
1237 /* To prevent throwing exceptions while inside libxml2 code, we catch
1238 * them and forward to our level using a couple of variables. */
1239 try
1240 {
1241 return pContext->file.write(aBuf, aLen);
1242 }
1243 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1244 catch (const xml::Error &err) { pContext->setError(err); }
1245 catch (const std::exception &err) { pContext->setError(err); }
1246 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1247
1248 return -1 /* failure */;
1249}
1250
1251int XmlFileWriter::CloseCallback(void *aCtxt)
1252{
1253 /// @todo to be written
1254
1255 return -1;
1256}
1257
1258
1259} // end namespace xml
1260
1261
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