VirtualBox

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

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

OVF: partial implementation of write support in XML classes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.5 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
414 xmlAttr *plibAttr; // != NULL if this is an attribute
415
416 Node *pParent; // NULL only for the root element
417 const char *pcszName; // points either into plibNode or plibAttr
418
419 struct compare_const_char
420 {
421 bool operator()(const char* s1, const char* s2) const
422 {
423 return strcmp(s1, s2) < 0;
424 }
425 };
426
427 // attributes, if this is an element; can be empty
428 typedef std::map<const char*, boost::shared_ptr<Node>, compare_const_char > AttributesMap;
429 AttributesMap attribs;
430
431 // child elements, if this is an element; can be empty
432 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
433 InternalNodesList children;
434};
435
436Node::Node()
437 : m(new Data)
438{
439 m->plibNode = NULL;
440 m->plibAttr = NULL;
441 m->pParent = NULL;
442}
443
444Node::~Node()
445{
446 delete m;
447}
448
449void Node::buildChildren() // private
450{
451 // go thru this element's attributes
452 xmlAttr *plibAttr = m->plibNode->properties;
453 while (plibAttr)
454 {
455 const char *pcszAttribName = (const char*)plibAttr->name;
456 boost::shared_ptr<Node> pNew(new Node);
457 pNew->m->plibAttr = plibAttr;
458 pNew->m->pcszName = (const char*)plibAttr->name;
459 pNew->m->pParent = this;
460 // store
461 m->attribs[pcszAttribName] = pNew;
462
463 plibAttr = plibAttr->next;
464 }
465
466 // go thru this element's child elements
467 xmlNodePtr plibNode = m->plibNode->children;
468 while (plibNode)
469 {
470 // create a new Node for this child element
471 boost::shared_ptr<Node> pNew(new Node);
472 pNew->m->plibNode = plibNode;
473 pNew->m->pcszName = (const char*)plibNode->name;
474 pNew->m->pParent = this;
475 // store
476 m->children.push_back(pNew);
477
478 // recurse for this child element to get its own children
479 pNew->buildChildren();
480
481 plibNode = plibNode->next;
482 }
483}
484
485const char* Node::getName() const
486{
487 return m->pcszName;
488}
489
490/**
491 * Returns the value of a node. If this node is an attribute, returns
492 * the attribute value; if this node is an element, then this returns
493 * the element text content.
494 * @return
495 */
496const char* Node::getValue() const
497{
498 if ( (m->plibAttr)
499 && (m->plibAttr->children)
500 )
501 // libxml hides attribute values in another node created as a
502 // single child of the attribute node, and it's in the content field
503 return (const char*)m->plibAttr->children->content;
504
505 if ( (m->plibNode)
506 && (m->plibNode->children)
507 )
508 return (const char*)m->plibNode->children->content;
509
510 return NULL;
511}
512
513/**
514 * Copies the value of a node into the given integer variable.
515 * Returns TRUE only if a value was found and was actually an
516 * integer of the given type.
517 * @return
518 */
519bool Node::copyValue(int32_t &i) const
520{
521 const char *pcsz;
522 if ( ((pcsz = getValue()))
523 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
524 )
525 return true;
526
527 return false;
528}
529
530/**
531 * Copies the value of a node into the given integer variable.
532 * Returns TRUE only if a value was found and was actually an
533 * integer of the given type.
534 * @return
535 */
536bool Node::copyValue(uint32_t &i) const
537{
538 const char *pcsz;
539 if ( ((pcsz = getValue()))
540 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
541 )
542 return true;
543
544 return false;
545}
546
547/**
548 * Copies the value of a node into the given integer variable.
549 * Returns TRUE only if a value was found and was actually an
550 * integer of the given type.
551 * @return
552 */
553bool Node::copyValue(int64_t &i) const
554{
555 const char *pcsz;
556 if ( ((pcsz = getValue()))
557 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
558 )
559 return true;
560
561 return false;
562}
563
564/**
565 * Copies the value of a node into the given integer variable.
566 * Returns TRUE only if a value was found and was actually an
567 * integer of the given type.
568 * @return
569 */
570bool Node::copyValue(uint64_t &i) const
571{
572 const char *pcsz;
573 if ( ((pcsz = getValue()))
574 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
575 )
576 return true;
577
578 return false;
579}
580
581/**
582 * Returns the line number of the current node in the source XML file.
583 * Useful for error messages.
584 * @return
585 */
586int Node::getLineNumber() const
587{
588 if (m->plibAttr)
589 return m->pParent->m->plibNode->line;
590
591 return m->plibNode->line;
592}
593
594/**
595 * Builds a list of direct child elements of the current element that
596 * match the given string; if pcszMatch is NULL, all direct child
597 * elements are returned.
598 * @param children out: list of nodes to which children will be appended.
599 * @param pcszMatch in: match string, or NULL to return all children.
600 * @return Number of items appended to the list (0 if none).
601 */
602int Node::getChildElements(NodesList &children,
603 const char *pcszMatch /*= NULL*/)
604 const
605{
606 int i = 0;
607 Data::InternalNodesList::const_iterator
608 it,
609 last = m->children.end();
610 for (it = m->children.begin();
611 it != last;
612 ++it)
613 {
614 // export this child node if ...
615 if ( (!pcszMatch) // the caller wants all nodes or
616 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
617 )
618 {
619 children.push_back((*it).get());
620 ++i;
621 }
622 }
623 return i;
624}
625
626/**
627 * Returns the first child element whose name matches pcszMatch.
628 * @param pcszMatch
629 * @return
630 */
631const Node* Node::findChildElement(const char *pcszMatch)
632 const
633{
634 Data::InternalNodesList::const_iterator
635 it,
636 last = m->children.end();
637 for (it = m->children.begin();
638 it != last;
639 ++it)
640 {
641 if (!strcmp(pcszMatch, (**it).getName())) // the element name matches
642 return (*it).get();
643 }
644
645 return NULL;
646}
647
648/**
649 * Returns the first child element whose "id" attribute matches pcszId.
650 * @param pcszId identifier to look for.
651 * @return child element or NULL if not found.
652 */
653const Node* Node::findChildElementFromId(const char *pcszId) const
654{
655 Data::InternalNodesList::const_iterator
656 it,
657 last = m->children.end();
658 for (it = m->children.begin();
659 it != last;
660 ++it)
661 {
662 const Node *pElem = (*it).get();
663 const Node *pAttr;
664 if ( ((pAttr = pElem->findAttribute("id")))
665 && (!strcmp(pAttr->getValue(), pcszId))
666 )
667 return pElem;
668 }
669
670 return NULL;
671}
672
673/**
674 *
675 * @param pcszMatch
676 * @return
677 */
678const Node* Node::findAttribute(const char *pcszMatch) const
679{
680 Data::AttributesMap::const_iterator it;
681
682 it = m->attribs.find(pcszMatch);
683 if (it != m->attribs.end())
684 return it->second.get();
685
686 return NULL;
687}
688
689/**
690 * Convenience method which attempts to find the attribute with the given
691 * name and returns its value as a string.
692 *
693 * @param pcszMatch name of attribute to find.
694 * @param str out: attribute value
695 * @return TRUE if attribute was found and str was thus updated.
696 */
697bool Node::getAttributeValue(const char *pcszMatch, com::Utf8Str &str) const
698{
699 const Node* pAttr;
700 if ((pAttr = findAttribute(pcszMatch)))
701 {
702 str = pAttr->getValue();
703 return true;
704 }
705
706 return false;
707}
708
709/**
710 * Convenience method which attempts to find the attribute with the given
711 * name and returns its value as a signed long integer. This calls
712 * RTStrToInt64Ex internally and will only output the integer if that
713 * function returns no error.
714 *
715 * @param pcszMatch name of attribute to find.
716 * @param i out: attribute value
717 * @return TRUE if attribute was found and str was thus updated.
718 */
719bool Node::getAttributeValue(const char *pcszMatch, int64_t &i) const
720{
721 com::Utf8Str str;
722 if ( (getAttributeValue(pcszMatch, str))
723 && (VINF_SUCCESS == RTStrToInt64Ex(str.c_str(), NULL, 10, &i))
724 )
725 return true;
726
727 return false;
728}
729
730/**
731 * Convenience method which attempts to find the attribute with the given
732 * name and returns its value as an unsigned long integer.This calls
733 * RTStrToUInt64Ex internally and will only output the integer if that
734 * function returns no error.
735 *
736 * @param pcszMatch name of attribute to find.
737 * @param i out: attribute value
738 * @return TRUE if attribute was found and str was thus updated.
739 */
740bool Node::getAttributeValue(const char *pcszMatch, uint64_t &i) const
741{
742 com::Utf8Str str;
743 if ( (getAttributeValue(pcszMatch, str))
744 && (VINF_SUCCESS == RTStrToUInt64Ex(str.c_str(), NULL, 10, &i))
745 )
746 return true;
747
748 return false;
749}
750
751/*
752 * NodesLoop
753 *
754 */
755
756struct NodesLoop::Data
757{
758 NodesList listElements;
759 NodesList::const_iterator it;
760};
761
762NodesLoop::NodesLoop(const Node &node, const char *pcszMatch /* = NULL */)
763{
764 m = new Data;
765 node.getChildElements(m->listElements, pcszMatch);
766 m->it = m->listElements.begin();
767}
768
769NodesLoop::~NodesLoop()
770{
771 delete m;
772}
773
774
775/**
776 * Handy convenience helper for looping over all child elements. Create an
777 * instance of NodesLoop on the stack and call this method until it returns
778 * NULL, like this:
779 * <code>
780 * xml::Node node; // should point to an element
781 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
782 * const xml::Node *pChild = NULL;
783 * while (pChild = loop.forAllNodes())
784 * ...;
785 * </code>
786 * @param node
787 * @param pcszMatch
788 * @return
789 */
790const Node* NodesLoop::forAllNodes() const
791{
792 const Node *pNode = NULL;
793
794 if (m->it != m->listElements.end())
795 {
796 pNode = *(m->it);
797 ++(m->it);
798 }
799
800 return pNode;
801}
802
803////////////////////////////////////////////////////////////////////////////////
804//
805// Document class
806//
807////////////////////////////////////////////////////////////////////////////////
808
809struct Document::Data
810{
811 xmlDocPtr plibDocument;
812 Node *pRootElement;
813
814 Data()
815 {
816 plibDocument = NULL;
817 pRootElement = NULL;
818 }
819
820 ~Data()
821 {
822 reset();
823 }
824
825 void reset()
826 {
827 if (plibDocument)
828 {
829 xmlFreeDoc(plibDocument);
830 plibDocument = NULL;
831 }
832 if (pRootElement)
833 {
834 delete pRootElement;
835 pRootElement = NULL;
836 }
837 }
838
839 void copyFrom(const Document::Data *p)
840 {
841 if (p->plibDocument)
842 {
843 plibDocument = xmlCopyDoc(p->plibDocument,
844 1); // recursive == copy all
845 }
846 }
847};
848
849Document::Document()
850 : m(new Data)
851{
852}
853
854Document::Document(const Document &x)
855 : m(new Data)
856{
857 m->copyFrom(x.m);
858};
859
860Document& Document::operator=(const Document &x)
861{
862 m->reset();
863 m->copyFrom(x.m);
864 return *this;
865};
866
867Document::~Document()
868{
869 delete m;
870}
871
872/**
873 * private method to refresh all internal structures after the internal pDocument
874 * has changed. Called from XmlFileParser::read(). m->reset() must have been
875 * called before to make sure all members except the internal pDocument are clean.
876 */
877void Document::refreshInternals() // private
878{
879 m->pRootElement = new Node();
880 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
881 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
882
883 m->pRootElement->buildChildren();
884}
885
886/**
887 * Returns the root element of the document, or NULL if the document is empty.
888 * @return
889 */
890const Node* Document::getRootElement() const
891{
892 return m->pRootElement;
893}
894
895/**
896 * Creates a new element node and sets it as the root element. This will
897 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
898 */
899Node* Document::createRootElement(const char *pcszRootElementName)
900{
901 if (m->pRootElement)
902 throw EDocumentNotEmpty(RT_SRC_POS);
903
904 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
905 if (!(m->pRootElement = new Node()))
906 throw ENoMemory();
907 Node::Data *pNodeData = m->pRootElement->m;
908 if (!(pNodeData->plibNode = xmlNewNode(NULL, // namespace
909 (const xmlChar*)pcszRootElementName)))
910 throw ENoMemory();
911 pNodeData->pcszName = (const char*)pNodeData->plibNode->name;
912
913 return m->pRootElement;
914}
915
916////////////////////////////////////////////////////////////////////////////////
917//
918// XmlParserBase class
919//
920////////////////////////////////////////////////////////////////////////////////
921
922XmlParserBase::XmlParserBase()
923{
924 m_ctxt = xmlNewParserCtxt();
925 if (m_ctxt == NULL)
926 throw ENoMemory();
927}
928
929XmlParserBase::~XmlParserBase()
930{
931 xmlFreeParserCtxt (m_ctxt);
932 m_ctxt = NULL;
933}
934
935////////////////////////////////////////////////////////////////////////////////
936//
937// XmlFileParser class
938//
939////////////////////////////////////////////////////////////////////////////////
940
941struct XmlFileParser::Data
942{
943 xmlParserCtxtPtr ctxt;
944 com::Utf8Str strXmlFilename;
945
946 Data()
947 {
948 if (!(ctxt = xmlNewParserCtxt()))
949 throw xml::ENoMemory();
950 }
951
952 ~Data()
953 {
954 xmlFreeParserCtxt(ctxt);
955 ctxt = NULL;
956 }
957};
958
959XmlFileParser::XmlFileParser()
960 : XmlParserBase(),
961 m(new Data())
962{
963}
964
965XmlFileParser::~XmlFileParser()
966{
967}
968
969struct ReadWriteContext
970{
971 File file;
972 com::Utf8Str error;
973
974 ReadWriteContext(const char *pcszFilename)
975 : file(File::Mode_Read, pcszFilename) // @todo must be write for writer
976 {
977 }
978
979 void setError(const xml::Error &x)
980 {
981 error = x.what();
982 }
983
984 void setError(const std::exception &x)
985 {
986 error = x.what();
987 }
988};
989
990/**
991 * Reads the given file and fills the given Document object with its contents.
992 * Throws XmlError on parsing errors.
993 *
994 * The document that is passed in will be reset before being filled if not empty.
995 *
996 * @param pcszFilename in: name fo file to parse.
997 * @param doc out: document to be reset and filled with data according to file contents.
998 */
999void XmlFileParser::read(const char *pcszFilename,
1000 Document &doc)
1001{
1002 GlobalLock lock();
1003// global.setExternalEntityLoader(ExternalEntityLoader);
1004
1005 m->strXmlFilename = pcszFilename;
1006
1007 ReadWriteContext context(pcszFilename);
1008 doc.m->reset();
1009 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1010 ReadCallback,
1011 CloseCallback,
1012 &context,
1013 pcszFilename,
1014 NULL, // encoding = auto
1015 XML_PARSE_NOBLANKS)))
1016 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1017
1018 doc.refreshInternals();
1019}
1020
1021// static
1022int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1023{
1024 ReadWriteContext *pContext = static_cast<ReadWriteContext*>(aCtxt);
1025
1026 /* To prevent throwing exceptions while inside libxml2 code, we catch
1027 * them and forward to our level using a couple of variables. */
1028
1029 try
1030 {
1031 return pContext->file.read(aBuf, aLen);
1032 }
1033 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1034 catch (const xml::Error &err) { pContext->setError(err); }
1035 catch (const std::exception &err) { pContext->setError(err); }
1036 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1037
1038 return -1 /* failure */;
1039}
1040
1041int XmlFileParser::CloseCallback(void *aCtxt)
1042{
1043 /// @todo to be written
1044
1045 return -1;
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049//
1050// XmlFileWriter class
1051//
1052////////////////////////////////////////////////////////////////////////////////
1053
1054struct XmlFileWriter::Data
1055{
1056 Document *pDoc;
1057};
1058
1059XmlFileWriter::XmlFileWriter(Document &doc)
1060{
1061 m = new Data();
1062 m->pDoc = &doc;
1063}
1064
1065XmlFileWriter::~XmlFileWriter()
1066{
1067 delete m;
1068}
1069
1070int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1071{
1072 ReadWriteContext *pContext = static_cast<ReadWriteContext*>(aCtxt);
1073
1074 /* To prevent throwing exceptions while inside libxml2 code, we catch
1075 * them and forward to our level using a couple of variables. */
1076 try
1077 {
1078 return pContext->file.write(aBuf, aLen);
1079 }
1080 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1081 catch (const xml::Error &err) { pContext->setError(err); }
1082 catch (const std::exception &err) { pContext->setError(err); }
1083 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1084
1085 return -1 /* failure */;
1086}
1087
1088int XmlFileWriter::CloseCallback(void *aCtxt)
1089{
1090 /// @todo to be written
1091
1092 return -1;
1093}
1094
1095void XmlFileWriter::write(const char *pcszFilename)
1096{
1097 ReadWriteContext context(pcszFilename);
1098
1099 GlobalLock lock();
1100
1101 /* serialize to the stream */
1102 xmlIndentTreeOutput = 1;
1103 xmlTreeIndentString = " ";
1104 xmlSaveNoEmptyTags = 0;
1105
1106 xmlSaveCtxtPtr saveCtxt;
1107 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1108 CloseCallback,
1109 &context,
1110 NULL,
1111 XML_SAVE_FORMAT)))
1112 throw xml::LogicError(RT_SRC_POS);
1113
1114 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1115 if (rc == -1)
1116 {
1117 /* look if there was a forwared exception from the lower level */
1118// if (m->trappedErr.get() != NULL)
1119// m->trappedErr->rethrow();
1120
1121 /* there must be an exception from the Output implementation,
1122 * otherwise the save operation must always succeed. */
1123 throw xml::LogicError(RT_SRC_POS);
1124 }
1125
1126 xmlSaveClose(saveCtxt);
1127}
1128
1129
1130} // end namespace xml
1131
1132
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