VirtualBox

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

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

OVF: use AnnotationSection for VM description, don't fail on export if XML file exists

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