VirtualBox

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

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

OVF: replace no-op lock code with working lock

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