VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/xml.cpp@ 21080

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

Main: move libxml2 to IPRT unconditionally (remove VBOX_WITH_LIBXML2_IN_VBOXRT); move xml classes to IPRT; introduce IPRT ministring class as base for both Utf8Str and xml.cpp, with better performance; introduce some Utf8Str helpers to avoid string buffer hacks in Main code; remove std::auto_ptr<> from some headers

  • 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 <iprt/cdefs.h>
22#include <iprt/err.h>
23#include <iprt/file.h>
24#include <iprt/lock.h>
25#include <iprt/xml_cpp.h>
26
27#include <libxml/tree.h>
28#include <libxml/parser.h>
29#include <libxml/globals.h>
30#include <libxml/xmlIO.h>
31#include <libxml/xmlsave.h>
32#include <libxml/uri.h>
33
34#include <libxml/xmlschemas.h>
35
36#include <map>
37#include <boost/shared_ptr.hpp>
38
39////////////////////////////////////////////////////////////////////////////////
40//
41// globals
42//
43////////////////////////////////////////////////////////////////////////////////
44
45/**
46 * Global module initialization structure. This is to wrap non-reentrant bits
47 * of libxml, among other things.
48 *
49 * The constructor and destructor of this structure are used to perform global
50 * module initiaizaton and cleanup. Thee must be only one global variable of
51 * this structure.
52 */
53static
54class Global
55{
56public:
57
58 Global()
59 {
60 /* Check the parser version. The docs say it will kill the app if
61 * there is a serious version mismatch, but I couldn't find it in the
62 * source code (it only prints the error/warning message to the console) so
63 * let's leave it as is for informational purposes. */
64 LIBXML_TEST_VERSION
65
66 /* Init libxml */
67 xmlInitParser();
68
69 /* Save the default entity resolver before someone has replaced it */
70 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
71 }
72
73 ~Global()
74 {
75 /* Shutdown libxml */
76 xmlCleanupParser();
77 }
78
79 struct
80 {
81 xmlExternalEntityLoader defaultEntityLoader;
82
83 /** Used to provide some thread safety missing in libxml2 (see e.g.
84 * XmlTreeBackend::read()) */
85 RTLockMtx lock;
86 }
87 sxml; /* XXX naming this xml will break with gcc-3.3 */
88}
89gGlobal;
90
91
92
93namespace xml
94{
95
96////////////////////////////////////////////////////////////////////////////////
97//
98// Exceptions
99//
100////////////////////////////////////////////////////////////////////////////////
101
102LogicError::LogicError(RT_SRC_POS_DECL)
103 : Error(NULL)
104{
105 char *msg = NULL;
106 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
107 pszFunction, pszFile, iLine);
108 setWhat(msg);
109 RTStrFree(msg);
110}
111
112XmlError::XmlError(xmlErrorPtr aErr)
113{
114 if (!aErr)
115 throw EInvalidArg(RT_SRC_POS);
116
117 char *msg = Format(aErr);
118 setWhat(msg);
119 RTStrFree(msg);
120}
121
122/**
123 * Composes a single message for the given error. The caller must free the
124 * returned string using RTStrFree() when no more necessary.
125 */
126// static
127char *XmlError::Format(xmlErrorPtr aErr)
128{
129 const char *msg = aErr->message ? aErr->message : "<none>";
130 size_t msgLen = strlen(msg);
131 /* strip spaces, trailing EOLs and dot-like char */
132 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
133 --msgLen;
134
135 char *finalMsg = NULL;
136 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
137 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
138
139 return finalMsg;
140}
141
142EIPRTFailure::EIPRTFailure(int aRC)
143 : RuntimeError(NULL),
144 mRC(aRC)
145{
146 char *newMsg = NULL;
147 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
148 setWhat(newMsg);
149 RTStrFree(newMsg);
150}
151
152////////////////////////////////////////////////////////////////////////////////
153//
154// File Class
155//
156//////////////////////////////////////////////////////////////////////////////
157
158struct File::Data
159{
160 Data()
161 : fileName (NULL), handle (NIL_RTFILE), opened (false) {}
162
163 char *fileName;
164 RTFILE handle;
165 bool opened : 1;
166};
167
168File::File(Mode aMode, const char *aFileName)
169 : m (new Data())
170{
171 m->fileName = RTStrDup (aFileName);
172 if (m->fileName == NULL)
173 throw ENoMemory();
174
175 unsigned flags = 0;
176 switch (aMode)
177 {
178 case Mode_Read:
179 flags = RTFILE_O_READ;
180 break;
181 case Mode_WriteCreate: // fail if file exists
182 flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
183 break;
184 case Mode_Overwrite: // overwrite if file exists
185 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE;
186 break;
187 case Mode_ReadWrite:
188 flags = RTFILE_O_READ | RTFILE_O_WRITE;
189 }
190
191 int vrc = RTFileOpen (&m->handle, aFileName, flags);
192 if (RT_FAILURE (vrc))
193 throw EIPRTFailure (vrc);
194
195 m->opened = true;
196}
197
198File::File (RTFILE aHandle, const char *aFileName /* = NULL */)
199 : m (new Data())
200{
201 if (aHandle == NIL_RTFILE)
202 throw EInvalidArg (RT_SRC_POS);
203
204 m->handle = aHandle;
205
206 if (aFileName)
207 {
208 m->fileName = RTStrDup (aFileName);
209 if (m->fileName == NULL)
210 throw ENoMemory();
211 }
212
213 setPos (0);
214}
215
216File::~File()
217{
218 if (m->opened)
219 RTFileClose (m->handle);
220
221 RTStrFree (m->fileName);
222}
223
224const char *File::uri() const
225{
226 return m->fileName;
227}
228
229uint64_t File::pos() const
230{
231 uint64_t p = 0;
232 int vrc = RTFileSeek (m->handle, 0, RTFILE_SEEK_CURRENT, &p);
233 if (RT_SUCCESS (vrc))
234 return p;
235
236 throw EIPRTFailure (vrc);
237}
238
239void File::setPos (uint64_t aPos)
240{
241 uint64_t p = 0;
242 unsigned method = RTFILE_SEEK_BEGIN;
243 int vrc = VINF_SUCCESS;
244
245 /* check if we overflow int64_t and move to INT64_MAX first */
246 if (((int64_t) aPos) < 0)
247 {
248 vrc = RTFileSeek (m->handle, INT64_MAX, method, &p);
249 aPos -= (uint64_t) INT64_MAX;
250 method = RTFILE_SEEK_CURRENT;
251 }
252 /* seek the rest */
253 if (RT_SUCCESS (vrc))
254 vrc = RTFileSeek (m->handle, (int64_t) aPos, method, &p);
255 if (RT_SUCCESS (vrc))
256 return;
257
258 throw EIPRTFailure (vrc);
259}
260
261int File::read (char *aBuf, int aLen)
262{
263 size_t len = aLen;
264 int vrc = RTFileRead (m->handle, aBuf, len, &len);
265 if (RT_SUCCESS (vrc))
266 return (int)len;
267
268 throw EIPRTFailure (vrc);
269}
270
271int File::write (const char *aBuf, int aLen)
272{
273 size_t len = aLen;
274 int vrc = RTFileWrite (m->handle, aBuf, len, &len);
275 if (RT_SUCCESS (vrc))
276 return (int)len;
277
278 throw EIPRTFailure (vrc);
279
280 return -1 /* failure */;
281}
282
283void File::truncate()
284{
285 int vrc = RTFileSetSize (m->handle, pos());
286 if (RT_SUCCESS (vrc))
287 return;
288
289 throw EIPRTFailure (vrc);
290}
291
292////////////////////////////////////////////////////////////////////////////////
293//
294// MemoryBuf Class
295//
296//////////////////////////////////////////////////////////////////////////////
297
298struct MemoryBuf::Data
299{
300 Data()
301 : buf (NULL), len (0), uri (NULL), pos (0) {}
302
303 const char *buf;
304 size_t len;
305 char *uri;
306
307 size_t pos;
308};
309
310MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
311 : m (new Data())
312{
313 if (aBuf == NULL)
314 throw EInvalidArg (RT_SRC_POS);
315
316 m->buf = aBuf;
317 m->len = aLen;
318 m->uri = RTStrDup (aURI);
319}
320
321MemoryBuf::~MemoryBuf()
322{
323 RTStrFree (m->uri);
324}
325
326const char *MemoryBuf::uri() const
327{
328 return m->uri;
329}
330
331uint64_t MemoryBuf::pos() const
332{
333 return m->pos;
334}
335
336void MemoryBuf::setPos (uint64_t aPos)
337{
338 size_t pos = (size_t) aPos;
339 if ((uint64_t) pos != aPos)
340 throw EInvalidArg();
341
342 if (pos > m->len)
343 throw EInvalidArg();
344
345 m->pos = pos;
346}
347
348int MemoryBuf::read (char *aBuf, int aLen)
349{
350 if (m->pos >= m->len)
351 return 0 /* nothing to read */;
352
353 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
354 memcpy (aBuf, m->buf + m->pos, len);
355 m->pos += len;
356
357 return (int)len;
358}
359
360////////////////////////////////////////////////////////////////////////////////
361//
362// GlobalLock class
363//
364////////////////////////////////////////////////////////////////////////////////
365
366struct GlobalLock::Data
367{
368 PFNEXTERNALENTITYLOADER pOldLoader;
369 RTLock lock;
370
371 Data()
372 : pOldLoader(NULL),
373 lock(gGlobal.sxml.lock)
374 {
375 }
376};
377
378GlobalLock::GlobalLock()
379 : m(new Data())
380{
381}
382
383GlobalLock::~GlobalLock()
384{
385 if (m->pOldLoader)
386 xmlSetExternalEntityLoader(m->pOldLoader);
387 delete m;
388 m = NULL;
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, const char *&ppcsz) const
719{
720 const Node* pAttr;
721 if ((pAttr = findAttribute(pcszMatch)))
722 {
723 ppcsz = 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 const char *pcsz;
743 if ( (getAttributeValue(pcszMatch, pcsz))
744 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, 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 const char *pcsz;
764 if ( (getAttributeValue(pcszMatch, pcsz))
765 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, 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 ministring 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 delete m;
1102 m = NULL;
1103}
1104
1105struct IOContext
1106{
1107 File file;
1108 ministring error;
1109
1110 IOContext(const char *pcszFilename, File::Mode mode)
1111 : file(mode, pcszFilename)
1112 {
1113 }
1114
1115 void setError(const xml::Error &x)
1116 {
1117 error = x.what();
1118 }
1119
1120 void setError(const std::exception &x)
1121 {
1122 error = x.what();
1123 }
1124};
1125
1126struct ReadContext : IOContext
1127{
1128 ReadContext(const char *pcszFilename)
1129 : IOContext(pcszFilename, File::Mode_Read)
1130 {
1131 }
1132};
1133
1134struct WriteContext : IOContext
1135{
1136 WriteContext(const char *pcszFilename)
1137 : IOContext(pcszFilename, File::Mode_Overwrite)
1138 {
1139 }
1140};
1141
1142/**
1143 * Reads the given file and fills the given Document object with its contents.
1144 * Throws XmlError on parsing errors.
1145 *
1146 * The document that is passed in will be reset before being filled if not empty.
1147 *
1148 * @param pcszFilename in: name fo file to parse.
1149 * @param doc out: document to be reset and filled with data according to file contents.
1150 */
1151void XmlFileParser::read(const char *pcszFilename,
1152 Document &doc)
1153{
1154 GlobalLock lock;
1155// global.setExternalEntityLoader(ExternalEntityLoader);
1156
1157 m->strXmlFilename = pcszFilename;
1158
1159 ReadContext context(pcszFilename);
1160 doc.m->reset();
1161 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1162 ReadCallback,
1163 CloseCallback,
1164 &context,
1165 pcszFilename,
1166 NULL, // encoding = auto
1167 XML_PARSE_NOBLANKS)))
1168 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1169
1170 doc.refreshInternals();
1171}
1172
1173// static
1174int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1175{
1176 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1177
1178 /* To prevent throwing exceptions while inside libxml2 code, we catch
1179 * them and forward to our level using a couple of variables. */
1180
1181 try
1182 {
1183 return pContext->file.read(aBuf, aLen);
1184 }
1185 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1186 catch (const xml::Error &err) { pContext->setError(err); }
1187 catch (const std::exception &err) { pContext->setError(err); }
1188 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1189
1190 return -1 /* failure */;
1191}
1192
1193int XmlFileParser::CloseCallback(void *aCtxt)
1194{
1195 /// @todo to be written
1196
1197 return -1;
1198}
1199
1200////////////////////////////////////////////////////////////////////////////////
1201//
1202// XmlFileWriter class
1203//
1204////////////////////////////////////////////////////////////////////////////////
1205
1206struct XmlFileWriter::Data
1207{
1208 Document *pDoc;
1209};
1210
1211XmlFileWriter::XmlFileWriter(Document &doc)
1212{
1213 m = new Data();
1214 m->pDoc = &doc;
1215}
1216
1217XmlFileWriter::~XmlFileWriter()
1218{
1219 delete m;
1220}
1221
1222void XmlFileWriter::write(const char *pcszFilename)
1223{
1224 WriteContext context(pcszFilename);
1225
1226 GlobalLock lock;
1227
1228 /* serialize to the stream */
1229 xmlIndentTreeOutput = 1;
1230 xmlTreeIndentString = " ";
1231 xmlSaveNoEmptyTags = 0;
1232
1233 xmlSaveCtxtPtr saveCtxt;
1234 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1235 CloseCallback,
1236 &context,
1237 NULL,
1238 XML_SAVE_FORMAT)))
1239 throw xml::LogicError(RT_SRC_POS);
1240
1241 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1242 if (rc == -1)
1243 {
1244 /* look if there was a forwared exception from the lower level */
1245// if (m->trappedErr.get() != NULL)
1246// m->trappedErr->rethrow();
1247
1248 /* there must be an exception from the Output implementation,
1249 * otherwise the save operation must always succeed. */
1250 throw xml::LogicError(RT_SRC_POS);
1251 }
1252
1253 xmlSaveClose(saveCtxt);
1254}
1255
1256int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1257{
1258 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1259
1260 /* To prevent throwing exceptions while inside libxml2 code, we catch
1261 * them and forward to our level using a couple of variables. */
1262 try
1263 {
1264 return pContext->file.write(aBuf, aLen);
1265 }
1266 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1267 catch (const xml::Error &err) { pContext->setError(err); }
1268 catch (const std::exception &err) { pContext->setError(err); }
1269 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1270
1271 return -1 /* failure */;
1272}
1273
1274int XmlFileWriter::CloseCallback(void *aCtxt)
1275{
1276 /// @todo to be written
1277
1278 return -1;
1279}
1280
1281
1282} // end namespace xml
1283
1284
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette