VirtualBox

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

Last change on this file since 106495 was 106495, checked in by vboxsync, 3 months ago

iprt/cpp/xml.h: Shut up the C5267 warning with VC 14.3. jiraref:VBP-1171

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