VirtualBox

source: vbox/trunk/include/iprt/cpp/xml.h@ 97583

Last change on this file since 97583 was 96507, checked in by vboxsync, 2 years ago

IPRT/nocrt: The xml.cpp stuff won't ever work without exceptions. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.8 KB
Line 
1/** @file
2 * IPRT - XML Helper APIs.
3 */
4
5/*
6 * Copyright (C) 2007-2022 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * The contents of this file may alternatively be used under the terms
25 * of the Common Development and Distribution License Version 1.0
26 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
27 * in the VirtualBox distribution, in which case the provisions of the
28 * CDDL are applicable instead of those of the GPL.
29 *
30 * You may elect to license modified versions of this file under the
31 * terms and conditions of either the GPL or the CDDL or both.
32 *
33 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
34 */
35
36#ifndef IPRT_INCLUDED_cpp_xml_h
37#define IPRT_INCLUDED_cpp_xml_h
38#ifndef RT_WITHOUT_PRAGMA_ONCE
39# pragma once
40#endif
41
42#ifndef IN_RING3
43# error "There are no XML APIs available in Ring-0 Context!"
44#endif
45#ifdef IPRT_NO_CRT
46# error "Not available in no-CRT mode because it depends on exceptions, std::list, std::map and stdio.h."
47#endif
48
49#include <iprt/list.h>
50#include <iprt/cpp/exception.h>
51#include <iprt/cpp/utils.h>
52
53#include <list>
54#include <memory>
55
56
57/** @defgroup grp_rt_cpp_xml C++ XML support
58 * @ingroup grp_rt_cpp
59 * @{
60 */
61
62/* Forwards */
63typedef struct _xmlParserInput xmlParserInput;
64typedef xmlParserInput *xmlParserInputPtr;
65typedef struct _xmlParserCtxt xmlParserCtxt;
66typedef xmlParserCtxt *xmlParserCtxtPtr;
67typedef struct _xmlError xmlError;
68typedef xmlError *xmlErrorPtr;
69
70typedef struct _xmlAttr xmlAttr;
71typedef struct _xmlNode xmlNode;
72
73#define RT_XML_CONTENT_SMALL _8K
74#define RT_XML_CONTENT_LARGE _128K
75#define RT_XML_ATTR_TINY 64
76#define RT_XML_ATTR_SMALL _1K
77#define RT_XML_ATTR_MEDIUM _8K
78#define RT_XML_ATTR_LARGE _64K
79
80/** @} */
81
82namespace xml
83{
84
85/**
86 * @addtogroup grp_rt_cpp_xml
87 * @{
88 */
89
90// Exceptions
91//////////////////////////////////////////////////////////////////////////////
92
93class RT_DECL_CLASS LogicError : public RTCError
94{
95public:
96
97 LogicError(const char *aMsg = NULL)
98 : RTCError(aMsg)
99 {}
100
101 LogicError(RT_SRC_POS_DECL);
102};
103
104class RT_DECL_CLASS RuntimeError : public RTCError
105{
106public:
107
108 RuntimeError(const char *aMsg = NULL)
109 : RTCError(aMsg)
110 {}
111};
112
113class RT_DECL_CLASS XmlError : public RuntimeError
114{
115public:
116 XmlError(xmlErrorPtr aErr);
117
118 static char* Format(xmlErrorPtr aErr);
119};
120
121// Logical errors
122//////////////////////////////////////////////////////////////////////////////
123
124class RT_DECL_CLASS ENotImplemented : public LogicError
125{
126public:
127 ENotImplemented(const char *aMsg = NULL) : LogicError(aMsg) {}
128 ENotImplemented(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
129};
130
131class RT_DECL_CLASS EInvalidArg : public LogicError
132{
133public:
134 EInvalidArg(const char *aMsg = NULL) : LogicError(aMsg) {}
135 EInvalidArg(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
136};
137
138class RT_DECL_CLASS EDocumentNotEmpty : public LogicError
139{
140public:
141 EDocumentNotEmpty(const char *aMsg = NULL) : LogicError(aMsg) {}
142 EDocumentNotEmpty(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
143};
144
145class RT_DECL_CLASS ENodeIsNotElement : public LogicError
146{
147public:
148 ENodeIsNotElement(const char *aMsg = NULL) : LogicError(aMsg) {}
149 ENodeIsNotElement(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
150};
151
152// Runtime errors
153//////////////////////////////////////////////////////////////////////////////
154
155class RT_DECL_CLASS EIPRTFailure : public RuntimeError
156{
157public:
158
159 EIPRTFailure(int aRC, const char *pszContextFmt, ...);
160
161 int rc() const
162 {
163 return mRC;
164 }
165
166private:
167 int mRC;
168};
169
170/**
171 * The Stream class is a base class for I/O streams.
172 */
173class RT_DECL_CLASS Stream
174{
175public:
176
177 virtual ~Stream() {}
178
179 virtual const char *uri() const = 0;
180
181 /**
182 * Returns the current read/write position in the stream. The returned
183 * position is a zero-based byte offset from the beginning of the file.
184 *
185 * Throws ENotImplemented if this operation is not implemented for the
186 * given stream.
187 */
188 virtual uint64_t pos() const = 0;
189
190 /**
191 * Sets the current read/write position in the stream.
192 *
193 * @param aPos Zero-based byte offset from the beginning of the stream.
194 *
195 * Throws ENotImplemented if this operation is not implemented for the
196 * given stream.
197 */
198 virtual void setPos (uint64_t aPos) = 0;
199};
200
201/**
202 * The Input class represents an input stream.
203 *
204 * This input stream is used to read the settings tree from.
205 * This is an abstract class that must be subclassed in order to fill it with
206 * useful functionality.
207 */
208class RT_DECL_CLASS Input : virtual public Stream
209{
210public:
211
212 /**
213 * Reads from the stream to the supplied buffer.
214 *
215 * @param aBuf Buffer to store read data to.
216 * @param aLen Buffer length.
217 *
218 * @return Number of bytes read.
219 */
220 virtual int read (char *aBuf, int aLen) = 0;
221};
222
223/**
224 *
225 */
226class RT_DECL_CLASS Output : virtual public Stream
227{
228public:
229
230 /**
231 * Writes to the stream from the supplied buffer.
232 *
233 * @param aBuf Buffer to write data from.
234 * @param aLen Buffer length.
235 *
236 * @return Number of bytes written.
237 */
238 virtual int write (const char *aBuf, int aLen) = 0;
239
240 /**
241 * Truncates the stream from the current position and upto the end.
242 * The new file size will become exactly #pos() bytes.
243 *
244 * Throws ENotImplemented if this operation is not implemented for the
245 * given stream.
246 */
247 virtual void truncate() = 0;
248};
249
250
251//////////////////////////////////////////////////////////////////////////////
252
253/**
254 * The File class is a stream implementation that reads from and writes to
255 * regular files.
256 *
257 * The File class uses IPRT File API for file operations. Note that IPRT File
258 * API is not thread-safe. This means that if you pass the same RTFILE handle to
259 * different File instances that may be simultaneously used on different
260 * threads, you should care about serialization; otherwise you will get garbage
261 * when reading from or writing to such File instances.
262 */
263class RT_DECL_CLASS File : public Input, public Output
264{
265public:
266
267 /**
268 * Possible file access modes.
269 */
270 enum Mode { Mode_Read, Mode_WriteCreate, Mode_Overwrite, Mode_ReadWrite };
271
272 /**
273 * Opens a file with the given name in the given mode. If @a aMode is Read
274 * or ReadWrite, the file must exist. If @a aMode is Write, the file must
275 * not exist. Otherwise, an EIPRTFailure exception will be thrown.
276 *
277 * @param aMode File mode.
278 * @param aFileName File name.
279 * @param aFlushIt Whether to flush a writable file before closing it.
280 */
281 File(Mode aMode, const char *aFileName, bool aFlushIt = false);
282
283 /**
284 * Uses the given file handle to perform file operations. This file
285 * handle must be already open in necessary mode (read, or write, or mixed).
286 *
287 * The read/write position of the given handle will be reset to the
288 * beginning of the file on success.
289 *
290 * Note that the given file handle will not be automatically closed upon
291 * this object destruction.
292 *
293 * @note It you pass the same RTFILE handle to more than one File instance,
294 * please make sure you have provided serialization in case if these
295 * instasnces are to be simultaneously used by different threads.
296 * Otherwise you may get garbage when reading or writing.
297 *
298 * @param aHandle Open file handle.
299 * @param aFileName File name (for reference).
300 * @param aFlushIt Whether to flush a writable file before closing it.
301 */
302 File(RTFILE aHandle, const char *aFileName = NULL, bool aFlushIt = false);
303
304 /**
305 * Destroys the File object. If the object was created from a file name
306 * the corresponding file will be automatically closed. If the object was
307 * created from a file handle, it will remain open.
308 */
309 virtual ~File();
310
311 const char *uri() const;
312
313 uint64_t pos() const;
314 void setPos(uint64_t aPos);
315
316 /**
317 * See Input::read(). If this method is called in wrong file mode,
318 * LogicError will be thrown.
319 */
320 int read(char *aBuf, int aLen);
321
322 /**
323 * See Output::write(). If this method is called in wrong file mode,
324 * LogicError will be thrown.
325 */
326 int write(const char *aBuf, int aLen);
327
328 /**
329 * See Output::truncate(). If this method is called in wrong file mode,
330 * LogicError will be thrown.
331 */
332 void truncate();
333
334private:
335
336 /* Obscure class data */
337 struct Data;
338 Data *m;
339
340 /* auto_ptr data doesn't have proper copy semantics */
341 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(File);
342};
343
344/**
345 * The MemoryBuf class represents a stream implementation that reads from the
346 * memory buffer.
347 */
348class RT_DECL_CLASS MemoryBuf : public Input
349{
350public:
351
352 MemoryBuf (const char *aBuf, size_t aLen, const char *aURI = NULL);
353
354 virtual ~MemoryBuf();
355
356 const char *uri() const;
357
358 int read(char *aBuf, int aLen);
359 uint64_t pos() const;
360 void setPos(uint64_t aPos);
361
362private:
363 /* Obscure class data */
364 struct Data;
365 Data *m;
366
367 /* auto_ptr data doesn't have proper copy semantics */
368 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(MemoryBuf);
369};
370
371
372/*
373 * GlobalLock
374 *
375 *
376 */
377
378
379typedef DECLCALLBACKTYPE_EX(xmlParserInput *, RT_NOTHING, FNEXTERNALENTITYLOADER,(const char *aURI, const char *aID,
380 xmlParserCtxt *aCtxt));
381typedef FNEXTERNALENTITYLOADER *PFNEXTERNALENTITYLOADER; /**< xmlExternalEntityLoader w/ noexcept. */
382
383class RT_DECL_CLASS GlobalLock
384{
385public:
386 GlobalLock();
387 ~GlobalLock();
388
389 void setExternalEntityLoader(PFNEXTERNALENTITYLOADER pFunc);
390
391 static xmlParserInput* callDefaultLoader(const char *aURI,
392 const char *aID,
393 xmlParserCtxt *aCtxt);
394
395private:
396 /* Obscure class data. */
397 struct Data;
398 struct Data *m;
399};
400
401class ElementNode;
402typedef std::list<const ElementNode*> ElementNodesList;
403
404class AttributeNode;
405
406class ContentNode;
407
408/**
409 * Node base class.
410 *
411 * Cannot be used directly, but ElementNode, ContentNode and AttributeNode
412 * derive from this. This does implement useful public methods though.
413 *
414 *
415 */
416class RT_DECL_CLASS Node
417{
418public:
419 virtual ~Node();
420
421 const char *getName() const;
422 const char *getPrefix() const;
423 const char *getNamespaceURI() const;
424 bool nameEqualsNS(const char *pcszNamespace, const char *pcsz) const;
425 bool nameEquals(const char *pcsz) const
426 {
427 return nameEqualsNS(NULL, pcsz);
428 }
429 bool nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace = NULL) const;
430
431 const char *getValue() const;
432 const char *getValueN(size_t cchValueLimit) const;
433 bool copyValue(int32_t &i) const;
434 bool copyValue(uint32_t &i) const;
435 bool copyValue(int64_t &i) const;
436 bool copyValue(uint64_t &i) const;
437
438 /** @name Introspection.
439 * @{ */
440 /** Is this an ElementNode instance.
441 * @returns true / false */
442 bool isElement() const
443 {
444 return m_Type == IsElement;
445 }
446
447 /** Is this an ContentNode instance.
448 * @returns true / false */
449 bool isContent() const
450 {
451 return m_Type == IsContent;
452 }
453
454 /** Is this an AttributeNode instance.
455 * @returns true / false */
456 bool isAttribute() const
457 {
458 return m_Type == IsAttribute;
459 }
460
461 int getLineNumber() const;
462 /** @} */
463
464 /** @name General tree enumeration.
465 *
466 * Use the introspection methods isElement() and isContent() before doing static
467 * casting. Parents are always or ElementNode type, but siblings and children
468 * can be of both ContentNode and ElementNode types.
469 *
470 * @remarks Attribute node are in the attributes list, while both content and
471 * element nodes are in the list of children. See ElementNode.
472 *
473 * @remarks Careful mixing tree walking with node removal!
474 * @{
475 */
476 /** Get the parent node
477 * @returns Pointer to the parent node, or NULL if root. */
478 const Node *getParent() const
479 {
480 return m_pParent;
481 }
482
483 /** Get the previous sibling.
484 * @returns Pointer to the previous sibling node, NULL if first child.
485 */
486 const Node *getPrevSibiling() const
487 {
488 if (!m_pParentListAnchor)
489 return NULL;
490 return RTListGetPrevCpp(m_pParentListAnchor, this, const Node, m_listEntry);
491 }
492
493 /** Get the next sibling.
494 * @returns Pointer to the next sibling node, NULL if last child. */
495 const Node *getNextSibiling() const
496 {
497 if (!m_pParentListAnchor)
498 return NULL;
499 return RTListGetNextCpp(m_pParentListAnchor, this, const Node, m_listEntry);
500 }
501 /** @} */
502
503protected:
504 /** Node types. */
505 typedef enum { IsElement, IsAttribute, IsContent } EnumType;
506
507 /** The type of node this is an instance of. */
508 EnumType m_Type;
509 /** The parent node (always an element), NULL if root. */
510 Node *m_pParent;
511
512 xmlNode *m_pLibNode; ///< != NULL if this is an element or content node
513 xmlAttr *m_pLibAttr; ///< != NULL if this is an attribute node
514 const char *m_pcszNamespacePrefix; ///< not always set
515 const char *m_pcszNamespaceHref; ///< full http:// spec
516 const char *m_pcszName; ///< element or attribute name, points either into pLibNode or pLibAttr;
517 ///< NULL if this is a content node
518
519 /** Child list entry of this node. (List head m_pParent->m_children or
520 * m_pParent->m_attribute depending on the type.) */
521 RTLISTNODE m_listEntry;
522 /** Pointer to the parent list anchor.
523 * This allows us to use m_listEntry both for children and attributes. */
524 PRTLISTANCHOR m_pParentListAnchor;
525
526 // hide the default constructor so people use only our factory methods
527 Node(EnumType type,
528 Node *pParent,
529 PRTLISTANCHOR pListAnchor,
530 xmlNode *pLibNode,
531 xmlAttr *pLibAttr);
532 Node(const Node &x); // no copying
533
534 friend class AttributeNode;
535 friend class ElementNode; /* C list hack. */
536};
537
538/**
539 * Node subclass that represents an attribute of an element.
540 *
541 * For attributes, Node::getName() returns the attribute name, and Node::getValue()
542 * returns the attribute value, if any.
543 *
544 * Since the Node constructor is private, one can create new attribute nodes
545 * only through the following factory methods:
546 *
547 * -- ElementNode::setAttribute()
548 */
549class RT_DECL_CLASS AttributeNode : public Node
550{
551public:
552
553protected:
554 // hide the default constructor so people use only our factory methods
555 AttributeNode(const ElementNode *pElmRoot,
556 Node *pParent,
557 PRTLISTANCHOR pListAnchor,
558 xmlAttr *pLibAttr);
559 AttributeNode(const AttributeNode &x); // no copying
560
561 friend class Node;
562 friend class ElementNode;
563};
564
565/**
566 * Node subclass that represents an element.
567 *
568 * For elements, Node::getName() returns the element name, and Node::getValue()
569 * returns the text contents, if any.
570 *
571 * Since the Node constructor is private, one can create element nodes
572 * only through the following factory methods:
573 *
574 * -- Document::createRootElement()
575 * -- ElementNode::createChild()
576 */
577class RT_DECL_CLASS ElementNode : public Node
578{
579public:
580 int getChildElements(ElementNodesList &children, const char *pcszMatch = NULL) const;
581
582 const ElementNode *findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const;
583 const ElementNode *findChildElement(const char *pcszMatch) const
584 {
585 return findChildElementNS(NULL, pcszMatch);
586 }
587 const ElementNode *findChildElementFromId(const char *pcszId) const;
588
589 /** Finds the first decendant matching the name at the end of @a pcszPath and
590 * optionally namespace.
591 *
592 * @returns Pointer to the child string value, NULL if not found or no value.
593 * @param pcszPath Path to the child element. Slashes can be used to
594 * make a simple path to any decendant.
595 * @param pcszNamespace The namespace to match, NULL (default) match any
596 * namespace. When using a path, this matches all
597 * elements along the way.
598 * @see findChildElement, findChildElementP
599 */
600 const ElementNode *findChildElementP(const char *pcszPath, const char *pcszNamespace = NULL) const;
601
602 /** Finds the first child with matching the give name and optionally namspace,
603 * returning its value.
604 *
605 * @returns Pointer to the child string value, NULL if not found or no value.
606 * @param pcszPath Path to the child element. Slashes can be used to
607 * make a simple path to any decendant.
608 * @param pcszNamespace The namespace to match, NULL (default) match any
609 * namespace. When using a path, this matches all
610 * elements along the way.
611 * @see findChildElement, findChildElementP
612 */
613 const char *findChildElementValueP(const char *pcszPath, const char *pcszNamespace = NULL) const
614 {
615 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
616 if (pElem)
617 return pElem->getValue();
618 return NULL;
619 }
620
621 /** Finds the first child with matching the give name and optionally namspace,
622 * returning its value. Checks the length against the limit.
623 *
624 * @returns Pointer to the child string value, NULL if not found or no value.
625 * @param pcszPath Path to the child element. Slashes can be used to
626 * make a simple path to any decendant.
627 * @param cchValueLimit If the length of the returned value exceeds this
628 * limit a EIPRTFailure exception will be thrown.
629 * @param pcszNamespace The namespace to match, NULL (default) match any
630 * namespace. When using a path, this matches all
631 * elements along the way.
632 * @see findChildElement, findChildElementP
633 */
634 const char *findChildElementValuePN(const char *pcszPath, size_t cchValueLimit, const char *pcszNamespace = NULL) const
635 {
636 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
637 if (pElem)
638 return pElem->getValueN(cchValueLimit);
639 return NULL;
640 }
641
642 /** Combines findChildElementNS and findAttributeValue.
643 *
644 * @returns Pointer to attribute string value, NULL if either the element or
645 * the attribute was not found.
646 * @param pcszChild The child element name.
647 * @param pcszAttribute The attribute name.
648 * @param pcszChildNamespace The namespace to match @a pcszChild with, NULL
649 * (default) match any namespace.
650 * @param pcszAttributeNamespace The namespace prefix to apply to the
651 * attribute, NULL (default) match any namespace.
652 * @see findChildElementNS and findAttributeValue
653 * @note The findChildElementAttributeValueP() method would do the same thing
654 * given the same inputs, but it would be slightly slower, thus the
655 * separate method.
656 */
657 const char *findChildElementAttributeValue(const char *pcszChild, const char *pcszAttribute,
658 const char *pcszChildNamespace = NULL,
659 const char *pcszAttributeNamespace = NULL) const
660 {
661 const ElementNode *pElem = findChildElementNS(pcszChildNamespace, pcszChild);
662 if (pElem)
663 return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
664 return NULL;
665 }
666
667 /** Combines findChildElementP and findAttributeValue.
668 *
669 * @returns Pointer to attribute string value, NULL if either the element or
670 * the attribute was not found.
671 * @param pcszPath Path to the child element. Slashes can be used
672 * to make a simple path to any decendant.
673 * @param pcszAttribute The attribute name.
674 * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
675 * (default) match any namespace. When using a
676 * path, this matches all elements along the way.
677 * @param pcszAttributeNamespace The namespace prefix to apply to the
678 * attribute, NULL (default) match any namespace.
679 * @see findChildElementP and findAttributeValue
680 */
681 const char *findChildElementAttributeValueP(const char *pcszPath, const char *pcszAttribute,
682 const char *pcszPathNamespace = NULL,
683 const char *pcszAttributeNamespace = NULL) const
684 {
685 const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
686 if (pElem)
687 return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
688 return NULL;
689 }
690
691 /** Combines findChildElementP and findAttributeValueN.
692 *
693 * @returns Pointer to attribute string value, NULL if either the element or
694 * the attribute was not found.
695 * @param pcszPath The attribute name. Slashes can be used to make a
696 * simple path to any decendant.
697 * @param pcszAttribute The attribute name.
698 * @param cchValueLimit If the length of the returned value exceeds this
699 * limit a EIPRTFailure exception will be thrown.
700 * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
701 * (default) match any namespace. When using a
702 * path, this matches all elements along the way.
703 * @param pcszAttributeNamespace The namespace prefix to apply to the
704 * attribute, NULL (default) match any namespace.
705 * @see findChildElementP and findAttributeValue
706 */
707 const char *findChildElementAttributeValuePN(const char *pcszPath, const char *pcszAttribute,
708 size_t cchValueLimit,
709 const char *pcszPathNamespace = NULL,
710 const char *pcszAttributeNamespace = NULL) const
711 {
712 const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
713 if (pElem)
714 return pElem->findAttributeValueN(pcszAttribute, cchValueLimit, pcszAttributeNamespace);
715 return NULL;
716 }
717
718
719 /** @name Tree enumeration.
720 * @{ */
721
722 /** Get the next tree element in a full tree enumeration.
723 *
724 * By starting with the root node, this can be used to enumerate the entire tree
725 * (or sub-tree if @a pElmRoot is used).
726 *
727 * @returns Pointer to the next element in the tree, NULL if we're done.
728 * @param pElmRoot The root of the tree we're enumerating. NULL if
729 * it's the entire tree.
730 */
731 ElementNode const *getNextTreeElement(ElementNode const *pElmRoot = NULL) const;
732 RT_CPP_GETTER_UNCONST_RET(ElementNode *, ElementNode, getNextTreeElement, (const ElementNode *pElmRoot = NULL), (pElmRoot))
733
734 /** Get the first child node.
735 * @returns Pointer to the first child node, NULL if no children. */
736 const Node *getFirstChild() const
737 {
738 return RTListGetFirstCpp(&m_children, const Node, m_listEntry);
739 }
740 RT_CPP_GETTER_UNCONST_RET(Node *, ElementNode, getFirstChild,(),())
741
742 /** Get the last child node.
743 * @returns Pointer to the last child node, NULL if no children. */
744 const Node *getLastChild() const
745 {
746 return RTListGetLastCpp(&m_children, const Node, m_listEntry);
747 }
748
749 /** Get the first child node.
750 * @returns Pointer to the first child node, NULL if no children. */
751 const ElementNode *getFirstChildElement() const;
752
753 /** Get the last child node.
754 * @returns Pointer to the last child node, NULL if no children. */
755 const ElementNode *getLastChildElement() const;
756
757 /** Get the previous sibling element.
758 * @returns Pointer to the previous sibling element, NULL if first child
759 * element.
760 * @see getNextSibilingElement, getPrevSibling
761 */
762 const ElementNode *getPrevSibilingElement() const;
763
764 /** Get the next sibling element.
765 * @returns Pointer to the next sibling element, NULL if last child element.
766 * @see getPrevSibilingElement, getNextSibling
767 */
768 const ElementNode *getNextSibilingElement() const;
769
770 /** Find the previous element matching the given name and namespace (optionally).
771 * @returns Pointer to the previous sibling element, NULL if first child
772 * element.
773 * @param pcszName The element name to match.
774 * @param pcszNamespace The namespace name, default is NULL which means
775 * anything goes.
776 * @note Changed the order of the arguments.
777 */
778 const ElementNode *findPrevSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
779
780 /** Find the next element matching the given name and namespace (optionally).
781 * @returns Pointer to the previous sibling element, NULL if first child
782 * element.
783 * @param pcszName The element name to match.
784 * @param pcszNamespace The namespace name, default is NULL which means
785 * anything goes.
786 * @note Changed the order of the arguments.
787 */
788 const ElementNode *findNextSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
789 /** @} */
790
791 /** @name Attribute enumeration
792 * @{ */
793
794 /** Get the first attribute node.
795 * @returns Pointer to the first child node, NULL if no attributes. */
796 const AttributeNode *getFirstAttribute() const
797 {
798 return RTListGetFirstCpp(&m_attributes, const AttributeNode, m_listEntry);
799 }
800
801 /** Get the last attribute node.
802 * @returns Pointer to the last child node, NULL if no attributes. */
803 const AttributeNode *getLastAttribute() const
804 {
805 return RTListGetLastCpp(&m_attributes, const AttributeNode, m_listEntry);
806 }
807
808 /** @} */
809
810 const AttributeNode *findAttribute(const char *pcszMatch, const char *pcszNamespace = NULL) const;
811 /** Find the first attribute with the given name, returning its value string.
812 * @returns Pointer to the attribute string value.
813 * @param pcszName The attribute name.
814 * @param pcszNamespace The namespace name, default is NULL which means
815 * anything goes.
816 * @see getAttributeValue
817 */
818 const char *findAttributeValue(const char *pcszName, const char *pcszNamespace = NULL) const
819 {
820 const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
821 if (pAttr)
822 return pAttr->getValue();
823 return NULL;
824 }
825 /** Find the first attribute with the given name, returning its value string.
826 * @returns Pointer to the attribute string value.
827 * @param pcszName The attribute name.
828 * @param cchValueLimit If the length of the returned value exceeds this
829 * limit a EIPRTFailure exception will be thrown.
830 * @param pcszNamespace The namespace name, default is NULL which means
831 * anything goes.
832 * @see getAttributeValue
833 */
834 const char *findAttributeValueN(const char *pcszName, size_t cchValueLimit, const char *pcszNamespace = NULL) const
835 {
836 const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
837 if (pAttr)
838 return pAttr->getValueN(cchValueLimit);
839 return NULL;
840 }
841
842 bool getAttributeValue(const char *pcszMatch, const char *&pcsz, const char *pcszNamespace = NULL) const
843 { return getAttributeValue(pcszMatch, &pcsz, pcszNamespace); }
844 bool getAttributeValue(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
845 { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
846 bool getAttributeValuePath(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
847 { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
848 bool getAttributeValue(const char *pcszMatch, int32_t &i, const char *pcszNamespace = NULL) const
849 { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
850 bool getAttributeValue(const char *pcszMatch, uint32_t &i, const char *pcszNamespace = NULL) const
851 { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
852 bool getAttributeValue(const char *pcszMatch, int64_t &i, const char *pcszNamespace = NULL) const
853 { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
854 bool getAttributeValue(const char *pcszMatch, uint64_t &u, const char *pcszNamespace = NULL) const
855 { return getAttributeValue(pcszMatch, &u, pcszNamespace); }
856 bool getAttributeValue(const char *pcszMatch, bool &f, const char *pcszNamespace = NULL) const
857 { return getAttributeValue(pcszMatch, &f, pcszNamespace); }
858 bool getAttributeValueN(const char *pcszMatch, const char *&pcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const
859 { return getAttributeValueN(pcszMatch, &pcsz, cchValueLimit, pcszNamespace); }
860 bool getAttributeValueN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
861 { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
862 bool getAttributeValuePathN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
863 { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
864
865 /** @name Variants that for clarity does not use references for output params.
866 * @{ */
867 bool getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace = NULL) const;
868 bool getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
869 bool getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
870 bool getAttributeValue(const char *pcszMatch, int32_t *pi, const char *pcszNamespace = NULL) const;
871 bool getAttributeValue(const char *pcszMatch, uint32_t *pu, const char *pcszNamespace = NULL) const;
872 bool getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace = NULL) const;
873 bool getAttributeValue(const char *pcszMatch, uint64_t *pu, const char *pcszNamespace = NULL) const;
874 bool getAttributeValue(const char *pcszMatch, bool *pf, const char *pcszNamespace = NULL) const;
875 bool getAttributeValueN(const char *pcszMatch, const char **ppcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
876 bool getAttributeValueN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
877 bool getAttributeValuePathN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
878 /** @} */
879
880 /** @name Convenience methods for convering the element value.
881 * @{ */
882 bool getElementValue(int32_t *piValue) const;
883 bool getElementValue(uint32_t *puValue) const;
884 bool getElementValue(int64_t *piValue) const;
885 bool getElementValue(uint64_t *puValue) const;
886 bool getElementValue(bool *pfValue) const;
887 /** @} */
888
889 /** @name Convenience findChildElementValueP and getElementValue.
890 * @{ */
891 bool getChildElementValueP(const char *pcszPath, int32_t *piValue, const char *pcszNamespace = NULL) const
892 {
893 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
894 return pElem && pElem->getElementValue(piValue);
895 }
896 bool getChildElementValueP(const char *pcszPath, uint32_t *puValue, const char *pcszNamespace = NULL) const
897 {
898 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
899 return pElem && pElem->getElementValue(puValue);
900 }
901 bool getChildElementValueP(const char *pcszPath, int64_t *piValue, const char *pcszNamespace = NULL) const
902 {
903 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
904 return pElem && pElem->getElementValue(piValue);
905 }
906 bool getChildElementValueP(const char *pcszPath, uint64_t *puValue, const char *pcszNamespace = NULL) const
907 {
908 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
909 return pElem && pElem->getElementValue(puValue);
910 }
911 bool getChildElementValueP(const char *pcszPath, bool *pfValue, const char *pcszNamespace = NULL) const
912 {
913 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
914 return pElem && pElem->getElementValue(pfValue);
915 }
916
917 /** @} */
918
919 /** @name Convenience findChildElementValueP and getElementValue with a
920 * default value being return if the child element isn't present.
921 *
922 * @remarks These will return false on conversion errors.
923 * @{ */
924 bool getChildElementValueDefP(const char *pcszPath, int32_t iDefault, int32_t *piValue, const char *pcszNamespace = NULL) const
925 {
926 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
927 if (pElem)
928 return pElem->getElementValue(piValue);
929 *piValue = iDefault;
930 return true;
931 }
932 bool getChildElementValueDefP(const char *pcszPath, uint32_t uDefault, uint32_t *puValue, const char *pcszNamespace = NULL) const
933 {
934 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
935 if (pElem)
936 return pElem->getElementValue(puValue);
937 *puValue = uDefault;
938 return true;
939 }
940 bool getChildElementValueDefP(const char *pcszPath, int64_t iDefault, int64_t *piValue, const char *pcszNamespace = NULL) const
941 {
942 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
943 if (pElem)
944 return pElem->getElementValue(piValue);
945 *piValue = iDefault;
946 return true;
947 }
948 bool getChildElementValueDefP(const char *pcszPath, uint64_t uDefault, uint64_t *puValue, const char *pcszNamespace = NULL) const
949 {
950 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
951 if (pElem)
952 return pElem->getElementValue(puValue);
953 *puValue = uDefault;
954 return true;
955 }
956 bool getChildElementValueDefP(const char *pcszPath, bool fDefault, bool *pfValue, const char *pcszNamespace = NULL) const
957 {
958 const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
959 if (pElem)
960 return pElem->getElementValue(pfValue);
961 *pfValue = fDefault;
962 return true;
963 }
964 /** @} */
965
966 ElementNode *createChild(const char *pcszElementName);
967
968 ContentNode *addContent(const char *pcszContent);
969 ContentNode *addContent(const RTCString &strContent)
970 {
971 return addContent(strContent.c_str());
972 }
973
974 ContentNode *setContent(const char *pcszContent);
975 ContentNode *setContent(const RTCString &strContent)
976 {
977 return setContent(strContent.c_str());
978 }
979
980 AttributeNode *setAttribute(const char *pcszName, const char *pcszValue);
981 AttributeNode *setAttribute(const char *pcszName, const RTCString &strValue)
982 {
983 return setAttribute(pcszName, strValue.c_str());
984 }
985 AttributeNode *setAttributePath(const char *pcszName, const RTCString &strValue);
986 AttributeNode *setAttribute(const char *pcszName, int32_t i);
987 AttributeNode *setAttribute(const char *pcszName, uint32_t i);
988 AttributeNode *setAttribute(const char *pcszName, int64_t i);
989 AttributeNode *setAttribute(const char *pcszName, uint64_t i);
990 AttributeNode *setAttributeHex(const char *pcszName, uint32_t i);
991 AttributeNode *setAttribute(const char *pcszName, bool f);
992
993 virtual ~ElementNode();
994
995protected:
996 // hide the default constructor so people use only our factory methods
997 ElementNode(const ElementNode *pElmRoot, Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
998 ElementNode(const ElementNode &x); // no copying
999
1000 /** We keep a pointer to the root element for attribute namespace handling. */
1001 const ElementNode *m_pElmRoot;
1002
1003 /** List of child elements and content nodes. */
1004 RTLISTANCHOR m_children;
1005 /** List of attributes nodes. */
1006 RTLISTANCHOR m_attributes;
1007
1008 static void buildChildren(ElementNode *pElmRoot);
1009
1010 friend class Node;
1011 friend class Document;
1012 friend class XmlFileParser;
1013};
1014
1015/**
1016 * Node subclass that represents content (non-element text).
1017 *
1018 * Since the Node constructor is private, one can create new content nodes
1019 * only through the following factory methods:
1020 *
1021 * -- ElementNode::addContent()
1022 */
1023class RT_DECL_CLASS ContentNode : public Node
1024{
1025public:
1026
1027protected:
1028 // hide the default constructor so people use only our factory methods
1029 ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
1030 ContentNode(const ContentNode &x); // no copying
1031
1032 friend class Node;
1033 friend class ElementNode;
1034};
1035
1036
1037/**
1038 * Handy helper class with which one can loop through all or some children
1039 * of a particular element. See NodesLoop::forAllNodes() for details.
1040 */
1041class RT_DECL_CLASS NodesLoop
1042{
1043public:
1044 NodesLoop(const ElementNode &node, const char *pcszMatch = NULL);
1045 ~NodesLoop();
1046 const ElementNode* forAllNodes() const;
1047
1048private:
1049 /* Obscure class data */
1050 struct Data;
1051 Data *m;
1052};
1053
1054/**
1055 * The XML document class. An instance of this needs to be created by a user
1056 * of the XML classes and then passed to
1057 *
1058 * -- XmlMemParser or XmlFileParser to read an XML document; those classes then
1059 * fill the caller's Document with ElementNode, ContentNode and AttributeNode
1060 * instances. The typical sequence then is:
1061 * @code
1062 Document doc;
1063 XmlFileParser parser;
1064 parser.read("file.xml", doc);
1065 Element *pElmRoot = doc.getRootElement();
1066 @endcode
1067 *
1068 * -- XmlMemWriter or XmlFileWriter to write out an XML document after it has
1069 * been created and filled. Example:
1070 *
1071 * @code
1072 Document doc;
1073 Element *pElmRoot = doc.createRootElement();
1074 // add children
1075 xml::XmlFileWriter writer(doc);
1076 writer.write("file.xml", true);
1077 @endcode
1078 */
1079class RT_DECL_CLASS Document
1080{
1081public:
1082 Document();
1083 ~Document();
1084
1085 Document(const Document &x);
1086 Document& operator=(const Document &x);
1087
1088 const ElementNode* getRootElement() const;
1089 ElementNode* getRootElement();
1090
1091 ElementNode* createRootElement(const char *pcszRootElementName,
1092 const char *pcszComment = NULL);
1093
1094private:
1095 friend class XmlMemParser;
1096 friend class XmlFileParser;
1097 friend class XmlMemWriter;
1098 friend class XmlStringWriter;
1099 friend class XmlFileWriter;
1100
1101 void refreshInternals();
1102
1103 /* Obscure class data */
1104 struct Data;
1105 Data *m;
1106};
1107
1108/*
1109 * XmlParserBase
1110 *
1111 */
1112
1113class RT_DECL_CLASS XmlParserBase
1114{
1115protected:
1116 XmlParserBase();
1117 ~XmlParserBase();
1118
1119 xmlParserCtxtPtr m_ctxt;
1120};
1121
1122/*
1123 * XmlMemParser
1124 *
1125 */
1126
1127class RT_DECL_CLASS XmlMemParser : public XmlParserBase
1128{
1129public:
1130 XmlMemParser();
1131 ~XmlMemParser();
1132
1133 void read(const void* pvBuf, size_t cbSize, const RTCString &strFilename, Document &doc);
1134};
1135
1136/*
1137 * XmlFileParser
1138 *
1139 */
1140
1141class RT_DECL_CLASS XmlFileParser : public XmlParserBase
1142{
1143public:
1144 XmlFileParser();
1145 ~XmlFileParser();
1146
1147 void read(const RTCString &strFilename, Document &doc);
1148
1149private:
1150 /* Obscure class data */
1151 struct Data;
1152 struct Data *m;
1153
1154 static int ReadCallback(void *aCtxt, char *aBuf, int aLen) RT_NOTHROW_PROTO;
1155 static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
1156};
1157
1158/**
1159 * XmlMemWriter
1160 */
1161class RT_DECL_CLASS XmlMemWriter
1162{
1163public:
1164 XmlMemWriter();
1165 ~XmlMemWriter();
1166
1167 void write(const Document &doc, void** ppvBuf, size_t *pcbSize);
1168
1169private:
1170 void* m_pBuf;
1171};
1172
1173
1174/**
1175 * XmlStringWriter - writes the XML to an RTCString instance.
1176 */
1177class RT_DECL_CLASS XmlStringWriter
1178{
1179public:
1180 XmlStringWriter();
1181
1182 int write(const Document &rDoc, RTCString *pStrDst);
1183
1184private:
1185 static int WriteCallbackForSize(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
1186 static int WriteCallbackForReal(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
1187 static int CloseCallback(void *pvUser) RT_NOTHROW_PROTO;
1188
1189 /** Pointer to the destination string while we're in the write() call. */
1190 RTCString *m_pStrDst;
1191 /** Set by WriteCallback if we cannot grow the destination string. */
1192 bool m_fOutOfMemory;
1193};
1194
1195
1196/**
1197 * XmlFileWriter
1198 */
1199class RT_DECL_CLASS XmlFileWriter
1200{
1201public:
1202 XmlFileWriter(Document &doc);
1203 ~XmlFileWriter();
1204
1205 /**
1206 * Writes the XML document to the specified file.
1207 *
1208 * @param pcszFilename The name of the output file.
1209 * @param fSafe If @c true, some extra safety precautions will be
1210 * taken when writing the file:
1211 * -# The file is written with a '-tmp' suffix.
1212 * -# It is flushed to disk after writing.
1213 * -# Any original file is renamed to '-prev'.
1214 * -# The '-tmp' file is then renamed to the
1215 * specified name.
1216 * -# The directory changes are flushed to disk.
1217 * The suffixes are available via s_pszTmpSuff and
1218 * s_pszPrevSuff.
1219 */
1220 void write(const char *pcszFilename, bool fSafe);
1221
1222 static int WriteCallback(void *aCtxt, const char *aBuf, int aLen) RT_NOTHROW_PROTO;
1223 static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
1224
1225 /** The suffix used by XmlFileWriter::write() for the temporary file. */
1226 static const char * const s_pszTmpSuff;
1227 /** The suffix used by XmlFileWriter::write() for the previous (backup) file. */
1228 static const char * const s_pszPrevSuff;
1229
1230private:
1231 void writeInternal(const char *pcszFilename, bool fSafe);
1232
1233 /* Obscure class data */
1234 struct Data;
1235 Data *m;
1236};
1237
1238#if defined(_MSC_VER)
1239#pragma warning (default:4251)
1240#endif
1241
1242/** @} */
1243
1244} // end namespace xml
1245
1246#endif /* !IPRT_INCLUDED_cpp_xml_h */
1247
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