VirtualBox

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

Last change on this file was 98264, checked in by vboxsync, 17 months ago

iprt/cpp/xml.h,Main: Added EIPRTFailure::getStatus() as an alternative to rc() to avoid having scm getting upset about ambigous 'rc' term. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use