/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 | | */ |