VirtualBox

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

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

Main: decouple xml classes from COM dependencies (part 2)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/** @file
2 * VirtualBox XML Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include "Logging.h"
22
23#include <iprt/cdefs.h>
24#include <iprt/err.h>
25#include <iprt/file.h>
26#include <iprt/lock.h>
27#include <iprt/string.h>
28
29#include <libxml/tree.h>
30#include <libxml/parser.h>
31#include <libxml/globals.h>
32#include <libxml/xmlIO.h>
33#include <libxml/xmlsave.h>
34#include <libxml/uri.h>
35
36#include <libxml/xmlschemas.h>
37
38#include <list>
39#include <map>
40#include <boost/shared_ptr.hpp>
41
42#include "VBox/xml.h"
43
44////////////////////////////////////////////////////////////////////////////////
45//
46// globals
47//
48////////////////////////////////////////////////////////////////////////////////
49
50/**
51 * Global module initialization structure. This is to wrap non-reentrant bits
52 * of libxml, among other things.
53 *
54 * The constructor and destructor of this structure are used to perform global
55 * module initiaizaton and cleanup. Thee must be only one global variable of
56 * this structure.
57 */
58static
59class Global
60{
61public:
62
63 Global()
64 {
65 /* Check the parser version. The docs say it will kill the app if
66 * there is a serious version mismatch, but I couldn't find it in the
67 * source code (it only prints the error/warning message to the console) so
68 * let's leave it as is for informational purposes. */
69 LIBXML_TEST_VERSION
70
71 /* Init libxml */
72 xmlInitParser();
73
74 /* Save the default entity resolver before someone has replaced it */
75 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
76 }
77
78 ~Global()
79 {
80 /* Shutdown libxml */
81 xmlCleanupParser();
82 }
83
84 struct
85 {
86 xmlExternalEntityLoader defaultEntityLoader;
87
88 /** Used to provide some thread safety missing in libxml2 (see e.g.
89 * XmlTreeBackend::read()) */
90 RTLockMtx lock;
91 }
92 sxml; /* XXX naming this xml will break with gcc-3.3 */
93}
94gGlobal;
95
96
97
98namespace xml
99{
100
101////////////////////////////////////////////////////////////////////////////////
102//
103// Exceptions
104//
105////////////////////////////////////////////////////////////////////////////////
106
107LogicError::LogicError(RT_SRC_POS_DECL)
108 : Error(NULL)
109{
110 char *msg = NULL;
111 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
112 pszFunction, pszFile, iLine);
113 setWhat(msg);
114 RTStrFree(msg);
115}
116
117XmlError::XmlError(xmlErrorPtr aErr)
118{
119 if (!aErr)
120 throw EInvalidArg(RT_SRC_POS);
121
122 char *msg = Format(aErr);
123 setWhat(msg);
124 RTStrFree(msg);
125}
126
127/**
128 * Composes a single message for the given error. The caller must free the
129 * returned string using RTStrFree() when no more necessary.
130 */
131// static
132char *XmlError::Format(xmlErrorPtr aErr)
133{
134 const char *msg = aErr->message ? aErr->message : "<none>";
135 size_t msgLen = strlen(msg);
136 /* strip spaces, trailing EOLs and dot-like char */
137 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
138 --msgLen;
139
140 char *finalMsg = NULL;
141 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
142 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
143
144 return finalMsg;
145}
146
147EIPRTFailure::EIPRTFailure(int aRC)
148 : RuntimeError(NULL),
149 mRC(aRC)
150{
151 char *newMsg = NULL;
152 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
153 setWhat(newMsg);
154 RTStrFree(newMsg);
155}
156
157////////////////////////////////////////////////////////////////////////////////
158//
159// File Class
160//
161//////////////////////////////////////////////////////////////////////////////
162
163struct File::Data
164{
165 Data()
166 : fileName (NULL), handle (NIL_RTFILE), opened (false) {}
167
168 char *fileName;
169 RTFILE handle;
170 bool opened : 1;
171};
172
173File::File(Mode aMode, const char *aFileName)
174 : m (new Data())
175{
176 m->fileName = RTStrDup (aFileName);
177 if (m->fileName == NULL)
178 throw ENoMemory();
179
180 unsigned flags = 0;
181 switch (aMode)
182 {
183 case Mode_Read:
184 flags = RTFILE_O_READ;
185 break;
186 case Mode_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 (int)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 (int)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 (int)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 delete m;
393 m = NULL;
394}
395
396void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
397{
398 m->pOldLoader = xmlGetExternalEntityLoader();
399 xmlSetExternalEntityLoader(pLoader);
400}
401
402// static
403xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
404 const char *aID,
405 xmlParserCtxt *aCtxt)
406{
407 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
408}
409
410////////////////////////////////////////////////////////////////////////////////
411//
412// Node class
413//
414////////////////////////////////////////////////////////////////////////////////
415
416struct Node::Data
417{
418 xmlNode *plibNode; // != NULL if this is an element or content node
419 xmlAttr *plibAttr; // != NULL if this is an attribute node
420
421 Node *pParent; // NULL only for the root element
422 const char *pcszName; // element or attribute name, points either into plibNode or plibAttr;
423 // NULL if this is a content node
424
425 struct compare_const_char
426 {
427 bool operator()(const char* s1, const char* s2) const
428 {
429 return strcmp(s1, s2) < 0;
430 }
431 };
432
433 // attributes, if this is an element; can be empty
434 typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap;
435 AttributesMap attribs;
436
437 // child elements, if this is an element; can be empty
438 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
439 InternalNodesList children;
440};
441
442Node::Node(EnumType type)
443 : mType(type),
444 m(new Data)
445{
446 m->plibNode = NULL;
447 m->plibAttr = NULL;
448 m->pParent = NULL;
449}
450
451Node::~Node()
452{
453 delete m;
454}
455
456void Node::buildChildren() // private
457{
458 // go thru this element's attributes
459 xmlAttr *plibAttr = m->plibNode->properties;
460 while (plibAttr)
461 {
462 const char *pcszAttribName = (const char*)plibAttr->name;
463 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
464 pNew->m->plibAttr = plibAttr;
465 pNew->m->pcszName = (const char*)plibAttr->name;
466 pNew->m->pParent = this;
467 // store
468 m->attribs[pcszAttribName] = pNew;
469
470 plibAttr = plibAttr->next;
471 }
472
473 // go thru this element's child elements
474 xmlNodePtr plibNode = m->plibNode->children;
475 while (plibNode)
476 {
477 boost::shared_ptr<Node> pNew;
478
479 if (plibNode->name)
480 pNew = boost::shared_ptr<Node>(new ElementNode);
481 else
482 pNew = boost::shared_ptr<Node>(new ContentNode);
483
484 pNew->m->plibNode = plibNode;
485 pNew->m->pcszName = (const char*)plibNode->name;
486 pNew->m->pParent = this;
487 // store
488 m->children.push_back(pNew);
489
490 // recurse for this child element to get its own children
491 pNew->buildChildren();
492
493 plibNode = plibNode->next;
494 }
495}
496
497const char* Node::getName() const
498{
499 return m->pcszName;
500}
501
502/**
503 * Returns the value of a node. If this node is an attribute, returns
504 * the attribute value; if this node is an element, then this returns
505 * the element text content.
506 * @return
507 */
508const char* Node::getValue() const
509{
510 if ( (m->plibAttr)
511 && (m->plibAttr->children)
512 )
513 // libxml hides attribute values in another node created as a
514 // single child of the attribute node, and it's in the content field
515 return (const char*)m->plibAttr->children->content;
516
517 if ( (m->plibNode)
518 && (m->plibNode->children)
519 )
520 return (const char*)m->plibNode->children->content;
521
522 return NULL;
523}
524
525/**
526 * Copies the value of a node into the given integer variable.
527 * Returns TRUE only if a value was found and was actually an
528 * integer of the given type.
529 * @return
530 */
531bool Node::copyValue(int32_t &i) const
532{
533 const char *pcsz;
534 if ( ((pcsz = getValue()))
535 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
536 )
537 return true;
538
539 return false;
540}
541
542/**
543 * Copies the value of a node into the given integer variable.
544 * Returns TRUE only if a value was found and was actually an
545 * integer of the given type.
546 * @return
547 */
548bool Node::copyValue(uint32_t &i) const
549{
550 const char *pcsz;
551 if ( ((pcsz = getValue()))
552 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
553 )
554 return true;
555
556 return false;
557}
558
559/**
560 * Copies the value of a node into the given integer variable.
561 * Returns TRUE only if a value was found and was actually an
562 * integer of the given type.
563 * @return
564 */
565bool Node::copyValue(int64_t &i) const
566{
567 const char *pcsz;
568 if ( ((pcsz = getValue()))
569 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
570 )
571 return true;
572
573 return false;
574}
575
576/**
577 * Copies the value of a node into the given integer variable.
578 * Returns TRUE only if a value was found and was actually an
579 * integer of the given type.
580 * @return
581 */
582bool Node::copyValue(uint64_t &i) const
583{
584 const char *pcsz;
585 if ( ((pcsz = getValue()))
586 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
587 )
588 return true;
589
590 return false;
591}
592
593/**
594 * Returns the line number of the current node in the source XML file.
595 * Useful for error messages.
596 * @return
597 */
598int Node::getLineNumber() const
599{
600 if (m->plibAttr)
601 return m->pParent->m->plibNode->line;
602
603 return m->plibNode->line;
604}
605
606ElementNode::ElementNode()
607 : Node(IsElement)
608{
609}
610
611/**
612 * Builds a list of direct child elements of the current element that
613 * match the given string; if pcszMatch is NULL, all direct child
614 * elements are returned.
615 * @param children out: list of nodes to which children will be appended.
616 * @param pcszMatch in: match string, or NULL to return all children.
617 * @return Number of items appended to the list (0 if none).
618 */
619int ElementNode::getChildElements(ElementNodesList &children,
620 const char *pcszMatch /*= NULL*/)
621 const
622{
623 int i = 0;
624 Data::InternalNodesList::const_iterator
625 it,
626 last = m->children.end();
627 for (it = m->children.begin();
628 it != last;
629 ++it)
630 {
631 // export this child node if ...
632 if ( (!pcszMatch) // the caller wants all nodes or
633 || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
634 )
635 {
636 Node *pNode = (*it).get();
637 if (pNode->isElement())
638 children.push_back(static_cast<ElementNode*>(pNode));
639 ++i;
640 }
641 }
642 return i;
643}
644
645/**
646 * Returns the first child element whose name matches pcszMatch.
647 * @param pcszMatch
648 * @return
649 */
650const ElementNode* ElementNode::findChildElement(const char *pcszMatch)
651 const
652{
653 Data::InternalNodesList::const_iterator
654 it,
655 last = m->children.end();
656 for (it = m->children.begin();
657 it != last;
658 ++it)
659 {
660 if ((**it).isElement())
661 {
662 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
663 if (!strcmp(pcszMatch, pelm->getName())) // the element name matches
664 return pelm;
665 }
666 }
667
668 return NULL;
669}
670
671/**
672 * Returns the first child element whose "id" attribute matches pcszId.
673 * @param pcszId identifier to look for.
674 * @return child element or NULL if not found.
675 */
676const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
677{
678 Data::InternalNodesList::const_iterator
679 it,
680 last = m->children.end();
681 for (it = m->children.begin();
682 it != last;
683 ++it)
684 {
685 if ((**it).isElement())
686 {
687 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
688 const AttributeNode *pAttr;
689 if ( ((pAttr = pelm->findAttribute("id")))
690 && (!strcmp(pAttr->getValue(), pcszId))
691 )
692 return pelm;
693 }
694 }
695
696 return NULL;
697}
698
699/**
700 *
701 * @param pcszMatch
702 * @return
703 */
704const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
705{
706 Data::AttributesMap::const_iterator it;
707
708 it = m->attribs.find(pcszMatch);
709 if (it != m->attribs.end())
710 return it->second.get();
711
712 return NULL;
713}
714
715/**
716 * Convenience method which attempts to find the attribute with the given
717 * name and returns its value as a string.
718 *
719 * @param pcszMatch name of attribute to find.
720 * @param str out: attribute value
721 * @return TRUE if attribute was found and str was thus updated.
722 */
723bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
724{
725 const Node* pAttr;
726 if ((pAttr = findAttribute(pcszMatch)))
727 {
728 ppcsz = pAttr->getValue();
729 return true;
730 }
731
732 return false;
733}
734
735/**
736 * Convenience method which attempts to find the attribute with the given
737 * name and returns its value as a signed long integer. This calls
738 * RTStrToInt64Ex internally and will only output the integer if that
739 * function returns no error.
740 *
741 * @param pcszMatch name of attribute to find.
742 * @param i out: attribute value
743 * @return TRUE if attribute was found and str was thus updated.
744 */
745bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
746{
747 const char *pcsz;
748 if ( (getAttributeValue(pcszMatch, pcsz))
749 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
750 )
751 return true;
752
753 return false;
754}
755
756/**
757 * Convenience method which attempts to find the attribute with the given
758 * name and returns its value as an unsigned long integer.This calls
759 * RTStrToUInt64Ex internally and will only output the integer if that
760 * function returns no error.
761 *
762 * @param pcszMatch name of attribute to find.
763 * @param i out: attribute value
764 * @return TRUE if attribute was found and str was thus updated.
765 */
766bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
767{
768 const char *pcsz;
769 if ( (getAttributeValue(pcszMatch, pcsz))
770 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
771 )
772 return true;
773
774 return false;
775}
776
777/**
778 * Creates a new child element node and appends it to the list
779 * of children in "this".
780 *
781 * @param pcszElementName
782 * @return
783 */
784ElementNode* ElementNode::createChild(const char *pcszElementName)
785{
786 // we must be an element, not an attribute
787 if (!m->plibNode)
788 throw ENodeIsNotElement(RT_SRC_POS);
789
790 // libxml side: create new node
791 xmlNode *plibNode;
792 if (!(plibNode = xmlNewNode(NULL, // namespace
793 (const xmlChar*)pcszElementName)))
794 throw ENoMemory();
795 xmlAddChild(m->plibNode, plibNode);
796
797 // now wrap this in C++
798 ElementNode *p = new ElementNode;
799 boost::shared_ptr<ElementNode> pNew(p);
800 pNew->m->plibNode = plibNode;
801 pNew->m->pcszName = (const char*)plibNode->name;
802
803 m->children.push_back(pNew);
804
805 return p;
806}
807
808
809/**
810 * Creates a content node and appends it to the list of children
811 * in "this".
812 *
813 * @param pcszElementName
814 * @return
815 */
816ContentNode* ElementNode::addContent(const char *pcszContent)
817{
818 // libxml side: create new node
819 xmlNode *plibNode;
820 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
821 throw ENoMemory();
822 xmlAddChild(m->plibNode, plibNode);
823
824 // now wrap this in C++
825 ContentNode *p = new ContentNode;
826 boost::shared_ptr<ContentNode> pNew(p);
827 pNew->m->plibNode = plibNode;
828 pNew->m->pcszName = NULL;
829
830 m->children.push_back(pNew);
831
832 return p;
833}
834
835/**
836 * Sets the given attribute.
837 *
838 * If an attribute with the given name exists, it is overwritten,
839 * otherwise a new attribute is created. Returns the attribute node
840 * that was either created or changed.
841 *
842 * @param pcszName
843 * @param pcszValue
844 * @return
845 */
846AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
847{
848 Data::AttributesMap::const_iterator it;
849
850 it = m->attribs.find(pcszName);
851 if (it == m->attribs.end())
852 {
853 // libxml side: xmlNewProp creates an attribute
854 xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
855 const char *pcszAttribName = (const char*)plibAttr->name;
856
857 // C++ side: create an attribute node around it
858 boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
859 pNew->m->plibAttr = plibAttr;
860 pNew->m->pcszName = (const char*)plibAttr->name;
861 pNew->m->pParent = this;
862 // store
863 m->attribs[pcszAttribName] = pNew;
864 }
865 else
866 {
867 // @todo
868 throw LogicError("Attribute exists");
869 }
870
871 return NULL;
872
873}
874
875
876AttributeNode::AttributeNode()
877 : Node(IsAttribute)
878{
879}
880
881ContentNode::ContentNode()
882 : Node(IsContent)
883{
884}
885
886/*
887 * NodesLoop
888 *
889 */
890
891struct NodesLoop::Data
892{
893 ElementNodesList listElements;
894 ElementNodesList::const_iterator it;
895};
896
897NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
898{
899 m = new Data;
900 node.getChildElements(m->listElements, pcszMatch);
901 m->it = m->listElements.begin();
902}
903
904NodesLoop::~NodesLoop()
905{
906 delete m;
907}
908
909
910/**
911 * Handy convenience helper for looping over all child elements. Create an
912 * instance of NodesLoop on the stack and call this method until it returns
913 * NULL, like this:
914 * <code>
915 * xml::Node node; // should point to an element
916 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
917 * const xml::Node *pChild = NULL;
918 * while (pChild = loop.forAllNodes())
919 * ...;
920 * </code>
921 * @param node
922 * @param pcszMatch
923 * @return
924 */
925const ElementNode* NodesLoop::forAllNodes() const
926{
927 const ElementNode *pNode = NULL;
928
929 if (m->it != m->listElements.end())
930 {
931 pNode = *(m->it);
932 ++(m->it);
933 }
934
935 return pNode;
936}
937
938////////////////////////////////////////////////////////////////////////////////
939//
940// Document class
941//
942////////////////////////////////////////////////////////////////////////////////
943
944struct Document::Data
945{
946 xmlDocPtr plibDocument;
947 ElementNode *pRootElement;
948
949 Data()
950 {
951 plibDocument = NULL;
952 pRootElement = NULL;
953 }
954
955 ~Data()
956 {
957 reset();
958 }
959
960 void reset()
961 {
962 if (plibDocument)
963 {
964 xmlFreeDoc(plibDocument);
965 plibDocument = NULL;
966 }
967 if (pRootElement)
968 {
969 delete pRootElement;
970 pRootElement = NULL;
971 }
972 }
973
974 void copyFrom(const Document::Data *p)
975 {
976 if (p->plibDocument)
977 {
978 plibDocument = xmlCopyDoc(p->plibDocument,
979 1); // recursive == copy all
980 }
981 }
982};
983
984Document::Document()
985 : m(new Data)
986{
987}
988
989Document::Document(const Document &x)
990 : m(new Data)
991{
992 m->copyFrom(x.m);
993};
994
995Document& Document::operator=(const Document &x)
996{
997 m->reset();
998 m->copyFrom(x.m);
999 return *this;
1000};
1001
1002Document::~Document()
1003{
1004 delete m;
1005}
1006
1007/**
1008 * private method to refresh all internal structures after the internal pDocument
1009 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1010 * called before to make sure all members except the internal pDocument are clean.
1011 */
1012void Document::refreshInternals() // private
1013{
1014 m->pRootElement = new ElementNode();
1015 m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
1016 m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
1017
1018 m->pRootElement->buildChildren();
1019}
1020
1021/**
1022 * Returns the root element of the document, or NULL if the document is empty.
1023 * @return
1024 */
1025const ElementNode* Document::getRootElement() const
1026{
1027 return m->pRootElement;
1028}
1029
1030/**
1031 * Creates a new element node and sets it as the root element. This will
1032 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1033 */
1034ElementNode* Document::createRootElement(const char *pcszRootElementName)
1035{
1036 if (m->pRootElement || m->plibDocument)
1037 throw EDocumentNotEmpty(RT_SRC_POS);
1038
1039 // libxml side: create document, create root node
1040 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1041 xmlNode *plibRootNode;
1042 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1043 (const xmlChar*)pcszRootElementName)))
1044 throw ENoMemory();
1045 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1046
1047 // now wrap this in C++
1048 m->pRootElement = new ElementNode();
1049 m->pRootElement->m->plibNode = plibRootNode;
1050 m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
1051
1052 return m->pRootElement;
1053}
1054
1055////////////////////////////////////////////////////////////////////////////////
1056//
1057// XmlParserBase class
1058//
1059////////////////////////////////////////////////////////////////////////////////
1060
1061XmlParserBase::XmlParserBase()
1062{
1063 m_ctxt = xmlNewParserCtxt();
1064 if (m_ctxt == NULL)
1065 throw ENoMemory();
1066}
1067
1068XmlParserBase::~XmlParserBase()
1069{
1070 xmlFreeParserCtxt (m_ctxt);
1071 m_ctxt = NULL;
1072}
1073
1074////////////////////////////////////////////////////////////////////////////////
1075//
1076// XmlFileParser class
1077//
1078////////////////////////////////////////////////////////////////////////////////
1079
1080struct XmlFileParser::Data
1081{
1082 xmlParserCtxtPtr ctxt;
1083 ministring strXmlFilename;
1084
1085 Data()
1086 {
1087 if (!(ctxt = xmlNewParserCtxt()))
1088 throw xml::ENoMemory();
1089 }
1090
1091 ~Data()
1092 {
1093 xmlFreeParserCtxt(ctxt);
1094 ctxt = NULL;
1095 }
1096};
1097
1098XmlFileParser::XmlFileParser()
1099 : XmlParserBase(),
1100 m(new Data())
1101{
1102}
1103
1104XmlFileParser::~XmlFileParser()
1105{
1106 delete m;
1107 m = NULL;
1108}
1109
1110struct IOContext
1111{
1112 File file;
1113 ministring error;
1114
1115 IOContext(const char *pcszFilename, File::Mode mode)
1116 : file(mode, pcszFilename)
1117 {
1118 }
1119
1120 void setError(const xml::Error &x)
1121 {
1122 error = x.what();
1123 }
1124
1125 void setError(const std::exception &x)
1126 {
1127 error = x.what();
1128 }
1129};
1130
1131struct ReadContext : IOContext
1132{
1133 ReadContext(const char *pcszFilename)
1134 : IOContext(pcszFilename, File::Mode_Read)
1135 {
1136 }
1137};
1138
1139struct WriteContext : IOContext
1140{
1141 WriteContext(const char *pcszFilename)
1142 : IOContext(pcszFilename, File::Mode_Overwrite)
1143 {
1144 }
1145};
1146
1147/**
1148 * Reads the given file and fills the given Document object with its contents.
1149 * Throws XmlError on parsing errors.
1150 *
1151 * The document that is passed in will be reset before being filled if not empty.
1152 *
1153 * @param pcszFilename in: name fo file to parse.
1154 * @param doc out: document to be reset and filled with data according to file contents.
1155 */
1156void XmlFileParser::read(const char *pcszFilename,
1157 Document &doc)
1158{
1159 GlobalLock lock;
1160// global.setExternalEntityLoader(ExternalEntityLoader);
1161
1162 m->strXmlFilename = pcszFilename;
1163
1164 ReadContext context(pcszFilename);
1165 doc.m->reset();
1166 if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
1167 ReadCallback,
1168 CloseCallback,
1169 &context,
1170 pcszFilename,
1171 NULL, // encoding = auto
1172 XML_PARSE_NOBLANKS)))
1173 throw XmlError(xmlCtxtGetLastError(m->ctxt));
1174
1175 doc.refreshInternals();
1176}
1177
1178// static
1179int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1180{
1181 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1182
1183 /* To prevent throwing exceptions while inside libxml2 code, we catch
1184 * them and forward to our level using a couple of variables. */
1185
1186 try
1187 {
1188 return pContext->file.read(aBuf, aLen);
1189 }
1190 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1191 catch (const xml::Error &err) { pContext->setError(err); }
1192 catch (const std::exception &err) { pContext->setError(err); }
1193 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1194
1195 return -1 /* failure */;
1196}
1197
1198int XmlFileParser::CloseCallback(void *aCtxt)
1199{
1200 /// @todo to be written
1201
1202 return -1;
1203}
1204
1205////////////////////////////////////////////////////////////////////////////////
1206//
1207// XmlFileWriter class
1208//
1209////////////////////////////////////////////////////////////////////////////////
1210
1211struct XmlFileWriter::Data
1212{
1213 Document *pDoc;
1214};
1215
1216XmlFileWriter::XmlFileWriter(Document &doc)
1217{
1218 m = new Data();
1219 m->pDoc = &doc;
1220}
1221
1222XmlFileWriter::~XmlFileWriter()
1223{
1224 delete m;
1225}
1226
1227void XmlFileWriter::write(const char *pcszFilename)
1228{
1229 WriteContext context(pcszFilename);
1230
1231 GlobalLock lock;
1232
1233 /* serialize to the stream */
1234 xmlIndentTreeOutput = 1;
1235 xmlTreeIndentString = " ";
1236 xmlSaveNoEmptyTags = 0;
1237
1238 xmlSaveCtxtPtr saveCtxt;
1239 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1240 CloseCallback,
1241 &context,
1242 NULL,
1243 XML_SAVE_FORMAT)))
1244 throw xml::LogicError(RT_SRC_POS);
1245
1246 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1247 if (rc == -1)
1248 {
1249 /* look if there was a forwared exception from the lower level */
1250// if (m->trappedErr.get() != NULL)
1251// m->trappedErr->rethrow();
1252
1253 /* there must be an exception from the Output implementation,
1254 * otherwise the save operation must always succeed. */
1255 throw xml::LogicError(RT_SRC_POS);
1256 }
1257
1258 xmlSaveClose(saveCtxt);
1259}
1260
1261int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1262{
1263 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1264
1265 /* To prevent throwing exceptions while inside libxml2 code, we catch
1266 * them and forward to our level using a couple of variables. */
1267 try
1268 {
1269 return pContext->file.write(aBuf, aLen);
1270 }
1271 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1272 catch (const xml::Error &err) { pContext->setError(err); }
1273 catch (const std::exception &err) { pContext->setError(err); }
1274 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1275
1276 return -1 /* failure */;
1277}
1278
1279int XmlFileWriter::CloseCallback(void *aCtxt)
1280{
1281 /// @todo to be written
1282
1283 return -1;
1284}
1285
1286
1287} // end namespace xml
1288
1289
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