Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/resiprocate/rutil/XMLCursor.hxx
Line
Count
Source
1
#if !defined(RESIP_XMLCURSOR_HXX)
2
#define RESIP_XMLCURSOR_HXX 
3
4
#include <iosfwd>
5
#include <memory>
6
#include <vector>
7
8
#include "rutil/ParseBuffer.hxx"
9
#include "rutil/HashMap.hxx"
10
#include "rutil/LinkedHashMap.hxx"
11
12
namespace resip
13
{
14
15
/*
16
// XML tree traversal.
17
// XMLCursor starts at the root.
18
// The attributes and value of the cursor are those of the root.
19
// To descend to the first child of the root, call firstChild().
20
// To traverse the children of root from root, call firstChild();nextSibling();nextSibling();...
21
// To descend into the first child of the current element, call firstChild.
22
// To return to the parent of the current element, call parent.
23
// The traversal state among the siblings of the parent is maintained.
24
//
25
//                          root
26
//                        /     \
27
//                       P1     P2
28
//                      /  \   /  \
29
//                     A1  A2 B1  B2
30
//
31
//  atRoot() == true;
32
//  firstChild(); // P1
33
//  firstChild(); // A1
34
//  nextSibling(); // A2
35
//  parent();   // P1
36
//  nextSibling(); // P2
37
//  firstChild(); // B1
38
//  nextSibling(); // B2
39
//  parent();   // P2
40
//  nextSibling(); // false, stay at P2
41
//  parent();   // root
42
//  nextSibling(); // false, stay at root
43
//  parent();   // false, stay at root
44
//  firstChild(); // P1
45
46
// E.g.: Depth first traversal
47
//
48
// void traverse(XMLCursor& c)
49
// {
50
//    if (c.firstChild())
51
//    {
52
//       traverse(c);
53
//       c.parent();
54
//    }
55
// 
56
//    process(c);
57
//    
58
//    if (c.nextSibling())
59
//    {
60
//       traverse(c);
61
//    }
62
// }
63
//
64
// E.g.: Lexical Order traversal
65
//
66
// void
67
// traverse(XMLCursor& c, int i = 0)
68
// {
69
//    for (int ii = 0; ii < i; ++ii)
70
//    {
71
//       cerr << " ";
72
//    }
73
74
//    cerr << c.getTag();
75
//    if (c.atLeaf())
76
//    {
77
//       cerr << "[" << c.getValue() << "]" << endl;
78
//    }
79
//    else
80
//    {
81
//       cerr << endl;
82
//    }
83
84
//    if (c.firstChild())
85
//    {
86
//       traverse(c, i+2);
87
//       c.parent();
88
//    }
89
   
90
//    if (c.nextSibling())
91
//    {
92
//       traverse(c, i+2);
93
//    }
94
// }
95
96
*/
97
98
class XMLCursor
99
{
100
   public:
101
      // !dlb! should be determined by the document
102
      // see http://www.w3.org/TR/1998/REC-xml-19980210#sec-white-space
103
      enum {WhitespaceSignificant = false};
104
105
      XMLCursor(const ParseBuffer& pb);
106
      ~XMLCursor();
107
108
      bool nextSibling();
109
      bool firstChild();
110
      bool parent();
111
      void reset();
112
113
      bool atRoot() const;
114
      bool atLeaf() const;
115
116
      const Data& getTag() const;
117
      // Iterates in document order, so attributes encode back out as they were read.
118
      // Attribute names come from the network (PIDF, dialog-info, etc. bodies).  With
119
      // a plain HashMap a sender could pick names that all hash alike, turning parsing
120
      // one element into quadratic work; the keyed hash prevents that (see KeyedDataHash).
121
      typedef LinkedHashMap<Data, Data, KeyedDataHash> AttributeMap;
122
      /// getAttributes() fails (ParseException) an element with more attributes than this
123
      static constexpr size_t MaxAttributes = 256;
124
      /// Parsing fails (ParseException) a document with elements nested more
125
      /// than this many levels below the root element.
126
      /// Each nested element costs a stack frame while parsing and another while
127
      /// the tree is destroyed, so without a bound very deep nesting would recurse
128
      /// until the stack is exhausted.  Real bodies (PIDF, dialog-info, ...) nest
129
      /// only a few levels.
130
      static constexpr unsigned int MaxNestingDepth = 100;
131
      const AttributeMap& getAttributes() const;
132
      const Data& getValue() const;
133
134
      static EncodeStream& encode(EncodeStream& strm, const AttributeMap& attrs);
135
      class Node;
136
137
      class AttributeValueEqual 
138
      {
139
         Data data_;
140
         public:
141
0
            AttributeValueEqual(const Data& data) : data_(data) {};
142
0
            bool operator()(const std::pair<const Data, Data>& data) { return data.second == data_; }
143
      };
144
145
   private:
146
      static void skipProlog(ParseBuffer& pb);
147
      static void decode(Data&);
148
      static void decodeName(Data&);
149
150
      void parseNextRootChild();
151
152
153
      std::unique_ptr<Node> mRoot;
154
      Node* mCursor;
155
156
      //bool isEmpty;
157
158
      // store for undecoded root tag
159
      Data mTag;
160
161
      // store for copy of input if commented
162
      Data mData;
163
164
      // store date for decoding
165
      mutable Data mValue;
166
      // store attributes for reference
167
      mutable AttributeMap mAttributes;
168
      mutable bool mAttributesSet;
169
170
public:
171
      class Node
172
      {
173
         public:
174
            Node(const ParseBuffer& pb);
175
            ~Node();
176
177
            void addChild(Node*);
178
            // return true if <foo/>
179
            bool extractTag();
180
            // depth: levels below the root element, 1 for a child of the root
181
            void skipToEndTag(unsigned int depth);
182
            static const char* skipComments(ParseBuffer& pb);
183
184
            ParseBuffer mPb;
185
            Node* mParent;
186
            std::vector<Node*> mChildren;
187
            std::vector<Node*>::const_iterator mNext;
188
189
            bool mIsLeaf;
190
            Data mTag;
191
192
         private:
193
            Node(const Node&);
194
            Node& operator=(const Node&);
195
196
            friend EncodeStream& operator<<(EncodeStream& str, const XMLCursor& cursor);
197
            // friend EncodeStream& operator<<(EncodeStream& str, const XMLCursor::Node& cursor); // this line won't compile in windows 
198
      };
199
   private:
200
      friend EncodeStream& operator<<(EncodeStream&, const XMLCursor&);
201
      friend EncodeStream& operator<<(EncodeStream&, const XMLCursor::Node&);
202
203
      // no value semantics
204
      XMLCursor(const XMLCursor&);
205
      XMLCursor& operator=(const XMLCursor&);
206
      friend class Node;
207
};
208
209
EncodeStream&
210
operator<<(EncodeStream& str, const XMLCursor& cursor);
211
212
EncodeStream&
213
operator<<(EncodeStream& str, const XMLCursor::Node& cursor);
214
215
}
216
217
#endif
218
219
/* ====================================================================
220
 * The Vovida Software License, Version 1.0 
221
 * 
222
 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
223
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
224
 * 
225
 * Redistribution and use in source and binary forms, with or without
226
 * modification, are permitted provided that the following conditions
227
 * are met:
228
 * 
229
 * 1. Redistributions of source code must retain the above copyright
230
 *    notice, this list of conditions and the following disclaimer.
231
 * 
232
 * 2. Redistributions in binary form must reproduce the above copyright
233
 *    notice, this list of conditions and the following disclaimer in
234
 *    the documentation and/or other materials provided with the
235
 *    distribution.
236
 * 
237
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
238
 *    and "Vovida Open Communication Application Library (VOCAL)" must
239
 *    not be used to endorse or promote products derived from this
240
 *    software without prior written permission. For written
241
 *    permission, please contact vocal@vovida.org.
242
 *
243
 * 4. Products derived from this software may not be called "VOCAL", nor
244
 *    may "VOCAL" appear in their name, without prior written
245
 *    permission of Vovida Networks, Inc.
246
 * 
247
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
248
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
249
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
250
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
251
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
252
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
253
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
254
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
255
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
256
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
258
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
259
 * DAMAGE.
260
 * 
261
 * ====================================================================
262
 * 
263
 * This software consists of voluntary contributions made by Vovida
264
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
265
 * Inc.  For more information on Vovida Networks, Inc., please see
266
 * <http://www.vovida.org/>.
267
 *
268
 */