Chris@16
|
1 // ----------------------------------------------------------------------------
|
Chris@16
|
2 // Copyright (C) 2006, 2009 Marcin Kalicinski
|
Chris@16
|
3 //
|
Chris@16
|
4 // Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
5 // (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
6 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
7 //
|
Chris@16
|
8 // For more information, see www.boost.org
|
Chris@16
|
9 // ----------------------------------------------------------------------------
|
Chris@16
|
10 #ifndef BOOST_PROPERTY_TREE_RAPIDXML_HPP_INCLUDED
|
Chris@16
|
11 #define BOOST_PROPERTY_TREE_RAPIDXML_HPP_INCLUDED
|
Chris@16
|
12
|
Chris@16
|
13 //! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation
|
Chris@16
|
14
|
Chris@16
|
15 #include <boost/assert.hpp>
|
Chris@16
|
16 #include <cstdlib> // For std::size_t
|
Chris@16
|
17 #include <new> // For placement new
|
Chris@16
|
18
|
Chris@16
|
19 // On MSVC, disable "conditional expression is constant" warning (level 4).
|
Chris@16
|
20 // This warning is almost impossible to avoid with certain types of templated code
|
Chris@16
|
21 #ifdef _MSC_VER
|
Chris@16
|
22 #pragma warning(push)
|
Chris@16
|
23 #pragma warning(disable:4127) // Conditional expression is constant
|
Chris@16
|
24 #endif
|
Chris@16
|
25
|
Chris@16
|
26 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
27 // BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR
|
Chris@16
|
28
|
Chris@16
|
29 #include <exception> // For std::exception
|
Chris@16
|
30
|
Chris@16
|
31 #define BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where)
|
Chris@16
|
32
|
Chris@16
|
33 namespace boost { namespace property_tree { namespace detail {namespace rapidxml
|
Chris@16
|
34 {
|
Chris@16
|
35
|
Chris@16
|
36 //! Parse error exception.
|
Chris@16
|
37 //! This exception is thrown by the parser when an error occurs.
|
Chris@16
|
38 //! Use what() function to get human-readable error message.
|
Chris@16
|
39 //! Use where() function to get a pointer to position within source text where error was detected.
|
Chris@16
|
40 //! <br><br>
|
Chris@16
|
41 //! If throwing exceptions by the parser is undesirable,
|
Chris@16
|
42 //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included.
|
Chris@16
|
43 //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception.
|
Chris@16
|
44 //! This function must be defined by the user.
|
Chris@16
|
45 //! <br><br>
|
Chris@16
|
46 //! This class derives from <code>std::exception</code> class.
|
Chris@16
|
47 class parse_error: public std::exception
|
Chris@16
|
48 {
|
Chris@16
|
49
|
Chris@16
|
50 public:
|
Chris@16
|
51
|
Chris@16
|
52 //! Constructs parse error
|
Chris@16
|
53 parse_error(const char *wa, void *we)
|
Chris@16
|
54 : m_what(wa)
|
Chris@16
|
55 , m_where(we)
|
Chris@16
|
56 {
|
Chris@16
|
57 }
|
Chris@16
|
58
|
Chris@16
|
59 //! Gets human readable description of error.
|
Chris@16
|
60 //! \return Pointer to null terminated description of the error.
|
Chris@16
|
61 virtual const char *what() const throw()
|
Chris@16
|
62 {
|
Chris@16
|
63 return m_what;
|
Chris@16
|
64 }
|
Chris@16
|
65
|
Chris@16
|
66 //! Gets pointer to character data where error happened.
|
Chris@16
|
67 //! Ch should be the same as char type of xml_document that produced the error.
|
Chris@16
|
68 //! \return Pointer to location within the parsed string where error occured.
|
Chris@16
|
69 template<class Ch>
|
Chris@16
|
70 Ch *where() const
|
Chris@16
|
71 {
|
Chris@16
|
72 return reinterpret_cast<Ch *>(m_where);
|
Chris@16
|
73 }
|
Chris@16
|
74
|
Chris@16
|
75 private:
|
Chris@16
|
76
|
Chris@16
|
77 const char *m_what;
|
Chris@16
|
78 void *m_where;
|
Chris@16
|
79
|
Chris@16
|
80 };
|
Chris@16
|
81 }}}}
|
Chris@16
|
82
|
Chris@16
|
83 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
84 // Pool sizes
|
Chris@16
|
85
|
Chris@16
|
86 #ifndef BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE
|
Chris@16
|
87 // Size of static memory block of memory_pool.
|
Chris@16
|
88 // Define BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
|
Chris@16
|
89 // No dynamic memory allocations are performed by memory_pool until static memory is exhausted.
|
Chris@16
|
90 #define BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE (64 * 1024)
|
Chris@16
|
91 #endif
|
Chris@16
|
92
|
Chris@16
|
93 #ifndef BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE
|
Chris@16
|
94 // Size of dynamic memory block of memory_pool.
|
Chris@16
|
95 // Define BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
|
Chris@16
|
96 // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool.
|
Chris@16
|
97 #define BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024)
|
Chris@16
|
98 #endif
|
Chris@16
|
99
|
Chris@16
|
100 #ifndef BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT
|
Chris@16
|
101 // Memory allocation alignment.
|
Chris@16
|
102 // Define BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer.
|
Chris@16
|
103 // All memory allocations for nodes, attributes and strings will be aligned to this value.
|
Chris@16
|
104 // This must be a power of 2 and at least 1, otherwise memory_pool will not work.
|
Chris@16
|
105 #define BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT sizeof(void *)
|
Chris@16
|
106 #endif
|
Chris@16
|
107
|
Chris@16
|
108 namespace boost { namespace property_tree { namespace detail {namespace rapidxml
|
Chris@16
|
109 {
|
Chris@16
|
110 // Forward declarations
|
Chris@16
|
111 template<class Ch> class xml_node;
|
Chris@16
|
112 template<class Ch> class xml_attribute;
|
Chris@16
|
113 template<class Ch> class xml_document;
|
Chris@16
|
114
|
Chris@16
|
115 //! Enumeration listing all node types produced by the parser.
|
Chris@16
|
116 //! Use xml_node::type() function to query node type.
|
Chris@16
|
117 enum node_type
|
Chris@16
|
118 {
|
Chris@16
|
119 node_document, //!< A document node. Name and value are empty.
|
Chris@16
|
120 node_element, //!< An element node. Name contains element name. Value contains text of first data node.
|
Chris@16
|
121 node_data, //!< A data node. Name is empty. Value contains data text.
|
Chris@16
|
122 node_cdata, //!< A CDATA node. Name is empty. Value contains data text.
|
Chris@16
|
123 node_comment, //!< A comment node. Name is empty. Value contains comment text.
|
Chris@16
|
124 node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
|
Chris@16
|
125 node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
|
Chris@16
|
126 node_pi //!< A PI node. Name contains target. Value contains instructions.
|
Chris@16
|
127 };
|
Chris@16
|
128
|
Chris@16
|
129 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
130 // Parsing flags
|
Chris@16
|
131
|
Chris@16
|
132 //! Parse flag instructing the parser to not create data nodes.
|
Chris@16
|
133 //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified.
|
Chris@16
|
134 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
135 //! <br><br>
|
Chris@16
|
136 //! See xml_document::parse() function.
|
Chris@16
|
137 const int parse_no_data_nodes = 0x1;
|
Chris@16
|
138
|
Chris@16
|
139 //! Parse flag instructing the parser to not use text of first data node as a value of parent element.
|
Chris@16
|
140 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
141 //! Note that child data nodes of element node take precendence over its value when printing.
|
Chris@16
|
142 //! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored.
|
Chris@16
|
143 //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.
|
Chris@16
|
144 //! <br><br>
|
Chris@16
|
145 //! See xml_document::parse() function.
|
Chris@16
|
146 const int parse_no_element_values = 0x2;
|
Chris@16
|
147
|
Chris@16
|
148 //! Parse flag instructing the parser to not place zero terminators after strings in the source text.
|
Chris@16
|
149 //! By default zero terminators are placed, modifying source text.
|
Chris@16
|
150 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
151 //! <br><br>
|
Chris@16
|
152 //! See xml_document::parse() function.
|
Chris@16
|
153 const int parse_no_string_terminators = 0x4;
|
Chris@16
|
154
|
Chris@16
|
155 //! Parse flag instructing the parser to not translate entities in the source text.
|
Chris@16
|
156 //! By default entities are translated, modifying source text.
|
Chris@16
|
157 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
158 //! <br><br>
|
Chris@16
|
159 //! See xml_document::parse() function.
|
Chris@16
|
160 const int parse_no_entity_translation = 0x8;
|
Chris@16
|
161
|
Chris@16
|
162 //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters.
|
Chris@16
|
163 //! By default, UTF-8 handling is enabled.
|
Chris@16
|
164 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
165 //! <br><br>
|
Chris@16
|
166 //! See xml_document::parse() function.
|
Chris@16
|
167 const int parse_no_utf8 = 0x10;
|
Chris@16
|
168
|
Chris@16
|
169 //! Parse flag instructing the parser to create XML declaration node.
|
Chris@16
|
170 //! By default, declaration node is not created.
|
Chris@16
|
171 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
172 //! <br><br>
|
Chris@16
|
173 //! See xml_document::parse() function.
|
Chris@16
|
174 const int parse_declaration_node = 0x20;
|
Chris@16
|
175
|
Chris@16
|
176 //! Parse flag instructing the parser to create comments nodes.
|
Chris@16
|
177 //! By default, comment nodes are not created.
|
Chris@16
|
178 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
179 //! <br><br>
|
Chris@16
|
180 //! See xml_document::parse() function.
|
Chris@16
|
181 const int parse_comment_nodes = 0x40;
|
Chris@16
|
182
|
Chris@16
|
183 //! Parse flag instructing the parser to create DOCTYPE node.
|
Chris@16
|
184 //! By default, doctype node is not created.
|
Chris@16
|
185 //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one.
|
Chris@16
|
186 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
187 //! <br><br>
|
Chris@16
|
188 //! See xml_document::parse() function.
|
Chris@16
|
189 const int parse_doctype_node = 0x80;
|
Chris@16
|
190
|
Chris@16
|
191 //! Parse flag instructing the parser to create PI nodes.
|
Chris@16
|
192 //! By default, PI nodes are not created.
|
Chris@16
|
193 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
194 //! <br><br>
|
Chris@16
|
195 //! See xml_document::parse() function.
|
Chris@16
|
196 const int parse_pi_nodes = 0x100;
|
Chris@16
|
197
|
Chris@16
|
198 //! Parse flag instructing the parser to validate closing tag names.
|
Chris@16
|
199 //! If not set, name inside closing tag is irrelevant to the parser.
|
Chris@16
|
200 //! By default, closing tags are not validated.
|
Chris@16
|
201 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
202 //! <br><br>
|
Chris@16
|
203 //! See xml_document::parse() function.
|
Chris@16
|
204 const int parse_validate_closing_tags = 0x200;
|
Chris@16
|
205
|
Chris@16
|
206 //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes.
|
Chris@16
|
207 //! By default, whitespace is not trimmed.
|
Chris@16
|
208 //! This flag does not cause the parser to modify source text.
|
Chris@16
|
209 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
210 //! <br><br>
|
Chris@16
|
211 //! See xml_document::parse() function.
|
Chris@16
|
212 const int parse_trim_whitespace = 0x400;
|
Chris@16
|
213
|
Chris@16
|
214 //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character.
|
Chris@16
|
215 //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag.
|
Chris@16
|
216 //! By default, whitespace is not normalized.
|
Chris@16
|
217 //! If this flag is specified, source text will be modified.
|
Chris@16
|
218 //! Can be combined with other flags by use of | operator.
|
Chris@16
|
219 //! <br><br>
|
Chris@16
|
220 //! See xml_document::parse() function.
|
Chris@16
|
221 const int parse_normalize_whitespace = 0x800;
|
Chris@16
|
222
|
Chris@16
|
223 // Compound flags
|
Chris@16
|
224
|
Chris@16
|
225 //! Parse flags which represent default behaviour of the parser.
|
Chris@16
|
226 //! This is always equal to 0, so that all other flags can be simply ored together.
|
Chris@16
|
227 //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values.
|
Chris@16
|
228 //! This also means that meaning of each flag is a <i>negation</i> of the default setting.
|
Chris@16
|
229 //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default,
|
Chris@16
|
230 //! and using the flag will disable it.
|
Chris@16
|
231 //! <br><br>
|
Chris@16
|
232 //! See xml_document::parse() function.
|
Chris@16
|
233 const int parse_default = 0;
|
Chris@16
|
234
|
Chris@16
|
235 //! A combination of parse flags that forbids any modifications of the source text.
|
Chris@16
|
236 //! This also results in faster parsing. However, note that the following will occur:
|
Chris@16
|
237 //! <ul>
|
Chris@16
|
238 //! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li>
|
Chris@16
|
239 //! <li>entities will not be translated</li>
|
Chris@16
|
240 //! <li>whitespace will not be normalized</li>
|
Chris@16
|
241 //! </ul>
|
Chris@16
|
242 //! See xml_document::parse() function.
|
Chris@16
|
243 const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation;
|
Chris@16
|
244
|
Chris@16
|
245 //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.
|
Chris@16
|
246 //! <br><br>
|
Chris@16
|
247 //! See xml_document::parse() function.
|
Chris@16
|
248 const int parse_fastest = parse_non_destructive | parse_no_data_nodes;
|
Chris@16
|
249
|
Chris@16
|
250 //! A combination of parse flags resulting in largest amount of data being extracted.
|
Chris@16
|
251 //! This usually results in slowest parsing.
|
Chris@16
|
252 //! <br><br>
|
Chris@16
|
253 //! See xml_document::parse() function.
|
Chris@16
|
254 const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags;
|
Chris@16
|
255
|
Chris@16
|
256 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
257 // Internals
|
Chris@16
|
258
|
Chris@16
|
259 //! \cond internal
|
Chris@16
|
260 namespace internal
|
Chris@16
|
261 {
|
Chris@16
|
262
|
Chris@16
|
263 // Struct that contains lookup tables for the parser
|
Chris@16
|
264 // It must be a template to allow correct linking (because it has static data members, which are defined in a header file).
|
Chris@16
|
265 template<int Dummy>
|
Chris@16
|
266 struct lookup_tables
|
Chris@16
|
267 {
|
Chris@16
|
268 static const unsigned char lookup_whitespace[256]; // Whitespace table
|
Chris@16
|
269 static const unsigned char lookup_node_name[256]; // Node name table
|
Chris@16
|
270 static const unsigned char lookup_text[256]; // Text table
|
Chris@16
|
271 static const unsigned char lookup_text_pure_no_ws[256]; // Text table
|
Chris@16
|
272 static const unsigned char lookup_text_pure_with_ws[256]; // Text table
|
Chris@16
|
273 static const unsigned char lookup_attribute_name[256]; // Attribute name table
|
Chris@16
|
274 static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote
|
Chris@16
|
275 static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote
|
Chris@16
|
276 static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes
|
Chris@16
|
277 static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes
|
Chris@16
|
278 static const unsigned char lookup_digits[256]; // Digits
|
Chris@16
|
279 static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters
|
Chris@16
|
280 };
|
Chris@16
|
281
|
Chris@16
|
282 // Find length of the string
|
Chris@16
|
283 template<class Ch>
|
Chris@16
|
284 inline std::size_t measure(const Ch *p)
|
Chris@16
|
285 {
|
Chris@16
|
286 const Ch *tmp = p;
|
Chris@16
|
287 while (*tmp)
|
Chris@16
|
288 ++tmp;
|
Chris@16
|
289 return tmp - p;
|
Chris@16
|
290 }
|
Chris@16
|
291
|
Chris@16
|
292 // Compare strings for equality
|
Chris@16
|
293 template<class Ch>
|
Chris@16
|
294 inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive)
|
Chris@16
|
295 {
|
Chris@16
|
296 if (size1 != size2)
|
Chris@16
|
297 return false;
|
Chris@16
|
298 if (case_sensitive)
|
Chris@16
|
299 {
|
Chris@16
|
300 for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)
|
Chris@16
|
301 if (*p1 != *p2)
|
Chris@16
|
302 return false;
|
Chris@16
|
303 }
|
Chris@16
|
304 else
|
Chris@16
|
305 {
|
Chris@16
|
306 for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)
|
Chris@16
|
307 if (lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p2)])
|
Chris@16
|
308 return false;
|
Chris@16
|
309 }
|
Chris@16
|
310 return true;
|
Chris@16
|
311 }
|
Chris@16
|
312
|
Chris@16
|
313 template<class Ch>
|
Chris@16
|
314 inline size_t get_index(const Ch c)
|
Chris@16
|
315 {
|
Chris@16
|
316 // If not ASCII char, its semantic is same as plain 'z'.
|
Chris@16
|
317 // char could be signed, so first stretch and make unsigned.
|
Chris@16
|
318 unsigned n = c;
|
Chris@16
|
319 if (n > 127)
|
Chris@16
|
320 {
|
Chris@16
|
321 return 'z';
|
Chris@16
|
322 }
|
Chris@16
|
323 return c;
|
Chris@16
|
324 }
|
Chris@16
|
325 }
|
Chris@16
|
326 //! \endcond
|
Chris@16
|
327
|
Chris@16
|
328 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
329 // Memory pool
|
Chris@16
|
330
|
Chris@16
|
331 //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation.
|
Chris@16
|
332 //! In most cases, you will not need to use this class directly.
|
Chris@16
|
333 //! However, if you need to create nodes manually or modify names/values of nodes,
|
Chris@16
|
334 //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory.
|
Chris@16
|
335 //! Not only is this faster than allocating them by using <code>new</code> operator,
|
Chris@16
|
336 //! but also their lifetime will be tied to the lifetime of document,
|
Chris@16
|
337 //! possibly simplyfing memory management.
|
Chris@16
|
338 //! <br><br>
|
Chris@16
|
339 //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool.
|
Chris@16
|
340 //! You can also call allocate_string() function to allocate strings.
|
Chris@16
|
341 //! Such strings can then be used as names or values of nodes without worrying about their lifetime.
|
Chris@16
|
342 //! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called,
|
Chris@16
|
343 //! or when the pool is destroyed.
|
Chris@16
|
344 //! <br><br>
|
Chris@16
|
345 //! It is also possible to create a standalone memory_pool, and use it
|
Chris@16
|
346 //! to allocate nodes, whose lifetime will not be tied to any document.
|
Chris@16
|
347 //! <br><br>
|
Chris@16
|
348 //! Pool maintains <code>BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory.
|
Chris@16
|
349 //! Until static memory is exhausted, no dynamic memory allocations are done.
|
Chris@16
|
350 //! When static memory is exhausted, pool allocates additional blocks of memory of size <code>BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE</code> each,
|
Chris@16
|
351 //! by using global <code>new[]</code> and <code>delete[]</code> operators.
|
Chris@16
|
352 //! This behaviour can be changed by setting custom allocation routines.
|
Chris@16
|
353 //! Use set_allocator() function to set them.
|
Chris@16
|
354 //! <br><br>
|
Chris@16
|
355 //! Allocations for nodes, attributes and strings are aligned at <code>BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT</code> bytes.
|
Chris@16
|
356 //! This value defaults to the size of pointer on target architecture.
|
Chris@16
|
357 //! <br><br>
|
Chris@16
|
358 //! To obtain absolutely top performance from the parser,
|
Chris@16
|
359 //! it is important that all nodes are allocated from a single, contiguous block of memory.
|
Chris@16
|
360 //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably.
|
Chris@16
|
361 //! If required, you can tweak <code>BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE</code>, <code>BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT</code>
|
Chris@16
|
362 //! to obtain best wasted memory to performance compromise.
|
Chris@16
|
363 //! To do it, define their values before rapidxml.hpp file is included.
|
Chris@16
|
364 //! \param Ch Character type of created nodes.
|
Chris@16
|
365 template<class Ch = char>
|
Chris@16
|
366 class memory_pool
|
Chris@16
|
367 {
|
Chris@16
|
368
|
Chris@16
|
369 public:
|
Chris@16
|
370
|
Chris@16
|
371 //! \cond internal
|
Chris@16
|
372 // Prefixed names to work around weird MSVC lookup bug.
|
Chris@16
|
373 typedef void *(boost_ptree_raw_alloc_func)(std::size_t); // Type of user-defined function used to allocate memory
|
Chris@16
|
374 typedef void (boost_ptree_raw_free_func)(void *); // Type of user-defined function used to free memory
|
Chris@16
|
375 //! \endcond
|
Chris@16
|
376
|
Chris@16
|
377 //! Constructs empty pool with default allocator functions.
|
Chris@16
|
378 memory_pool()
|
Chris@16
|
379 : m_alloc_func(0)
|
Chris@16
|
380 , m_free_func(0)
|
Chris@16
|
381 {
|
Chris@16
|
382 init();
|
Chris@16
|
383 }
|
Chris@16
|
384
|
Chris@16
|
385 //! Destroys pool and frees all the memory.
|
Chris@16
|
386 //! This causes memory occupied by nodes allocated by the pool to be freed.
|
Chris@16
|
387 //! Nodes allocated from the pool are no longer valid.
|
Chris@16
|
388 ~memory_pool()
|
Chris@16
|
389 {
|
Chris@16
|
390 clear();
|
Chris@16
|
391 }
|
Chris@16
|
392
|
Chris@16
|
393 //! Allocates a new node from the pool, and optionally assigns name and value to it.
|
Chris@16
|
394 //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
|
Chris@16
|
395 //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
|
Chris@16
|
396 //! will call rapidxml::parse_error_handler() function.
|
Chris@16
|
397 //! \param type Type of node to create.
|
Chris@16
|
398 //! \param name Name to assign to the node, or 0 to assign no name.
|
Chris@16
|
399 //! \param value Value to assign to the node, or 0 to assign no value.
|
Chris@16
|
400 //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
|
Chris@16
|
401 //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
|
Chris@16
|
402 //! \return Pointer to allocated node. This pointer will never be NULL.
|
Chris@16
|
403 xml_node<Ch> *allocate_node(node_type type,
|
Chris@16
|
404 const Ch *name = 0, const Ch *value = 0,
|
Chris@16
|
405 std::size_t name_size = 0, std::size_t value_size = 0)
|
Chris@16
|
406 {
|
Chris@16
|
407 void *memory = allocate_aligned(sizeof(xml_node<Ch>));
|
Chris@16
|
408 xml_node<Ch> *node = new(memory) xml_node<Ch>(type);
|
Chris@16
|
409 if (name)
|
Chris@16
|
410 {
|
Chris@16
|
411 if (name_size > 0)
|
Chris@16
|
412 node->name(name, name_size);
|
Chris@16
|
413 else
|
Chris@16
|
414 node->name(name);
|
Chris@16
|
415 }
|
Chris@16
|
416 if (value)
|
Chris@16
|
417 {
|
Chris@16
|
418 if (value_size > 0)
|
Chris@16
|
419 node->value(value, value_size);
|
Chris@16
|
420 else
|
Chris@16
|
421 node->value(value);
|
Chris@16
|
422 }
|
Chris@16
|
423 return node;
|
Chris@16
|
424 }
|
Chris@16
|
425
|
Chris@16
|
426 //! Allocates a new attribute from the pool, and optionally assigns name and value to it.
|
Chris@16
|
427 //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
|
Chris@16
|
428 //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
|
Chris@16
|
429 //! will call rapidxml::parse_error_handler() function.
|
Chris@16
|
430 //! \param name Name to assign to the attribute, or 0 to assign no name.
|
Chris@16
|
431 //! \param value Value to assign to the attribute, or 0 to assign no value.
|
Chris@16
|
432 //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
|
Chris@16
|
433 //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
|
Chris@16
|
434 //! \return Pointer to allocated attribute. This pointer will never be NULL.
|
Chris@16
|
435 xml_attribute<Ch> *allocate_attribute(const Ch *name = 0, const Ch *value = 0,
|
Chris@16
|
436 std::size_t name_size = 0, std::size_t value_size = 0)
|
Chris@16
|
437 {
|
Chris@16
|
438 void *memory = allocate_aligned(sizeof(xml_attribute<Ch>));
|
Chris@16
|
439 xml_attribute<Ch> *attribute = new(memory) xml_attribute<Ch>;
|
Chris@16
|
440 if (name)
|
Chris@16
|
441 {
|
Chris@16
|
442 if (name_size > 0)
|
Chris@16
|
443 attribute->name(name, name_size);
|
Chris@16
|
444 else
|
Chris@16
|
445 attribute->name(name);
|
Chris@16
|
446 }
|
Chris@16
|
447 if (value)
|
Chris@16
|
448 {
|
Chris@16
|
449 if (value_size > 0)
|
Chris@16
|
450 attribute->value(value, value_size);
|
Chris@16
|
451 else
|
Chris@16
|
452 attribute->value(value);
|
Chris@16
|
453 }
|
Chris@16
|
454 return attribute;
|
Chris@16
|
455 }
|
Chris@16
|
456
|
Chris@16
|
457 //! Allocates a char array of given size from the pool, and optionally copies a given string to it.
|
Chris@16
|
458 //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
|
Chris@16
|
459 //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
|
Chris@16
|
460 //! will call rapidxml::parse_error_handler() function.
|
Chris@16
|
461 //! \param source String to initialize the allocated memory with, or 0 to not initialize it.
|
Chris@16
|
462 //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.
|
Chris@16
|
463 //! \return Pointer to allocated char array. This pointer will never be NULL.
|
Chris@16
|
464 Ch *allocate_string(const Ch *source = 0, std::size_t size = 0)
|
Chris@16
|
465 {
|
Chris@16
|
466 BOOST_ASSERT(source || size); // Either source or size (or both) must be specified
|
Chris@16
|
467 if (size == 0)
|
Chris@16
|
468 size = internal::measure(source) + 1;
|
Chris@16
|
469 Ch *result = static_cast<Ch *>(allocate_aligned(size * sizeof(Ch)));
|
Chris@16
|
470 if (source)
|
Chris@16
|
471 for (std::size_t i = 0; i < size; ++i)
|
Chris@16
|
472 result[i] = source[i];
|
Chris@16
|
473 return result;
|
Chris@16
|
474 }
|
Chris@16
|
475
|
Chris@16
|
476 //! Clones an xml_node and its hierarchy of child nodes and attributes.
|
Chris@16
|
477 //! Nodes and attributes are allocated from this memory pool.
|
Chris@16
|
478 //! Names and values are not cloned, they are shared between the clone and the source.
|
Chris@16
|
479 //! Result node can be optionally specified as a second parameter,
|
Chris@16
|
480 //! in which case its contents will be replaced with cloned source node.
|
Chris@16
|
481 //! This is useful when you want to clone entire document.
|
Chris@16
|
482 //! \param source Node to clone.
|
Chris@16
|
483 //! \param result Node to put results in, or 0 to automatically allocate result node
|
Chris@16
|
484 //! \return Pointer to cloned node. This pointer will never be NULL.
|
Chris@16
|
485 xml_node<Ch> *clone_node(const xml_node<Ch> *source, xml_node<Ch> *result = 0)
|
Chris@16
|
486 {
|
Chris@16
|
487 // Prepare result node
|
Chris@16
|
488 if (result)
|
Chris@16
|
489 {
|
Chris@16
|
490 result->remove_all_attributes();
|
Chris@16
|
491 result->remove_all_nodes();
|
Chris@16
|
492 result->type(source->type());
|
Chris@16
|
493 }
|
Chris@16
|
494 else
|
Chris@16
|
495 result = allocate_node(source->type());
|
Chris@16
|
496
|
Chris@16
|
497 // Clone name and value
|
Chris@16
|
498 result->name(source->name(), source->name_size());
|
Chris@16
|
499 result->value(source->value(), source->value_size());
|
Chris@16
|
500
|
Chris@16
|
501 // Clone child nodes and attributes
|
Chris@16
|
502 for (xml_node<Ch> *child = source->first_node(); child; child = child->next_sibling())
|
Chris@16
|
503 result->append_node(clone_node(child));
|
Chris@16
|
504 for (xml_attribute<Ch> *attr = source->first_attribute(); attr; attr = attr->next_attribute())
|
Chris@16
|
505 result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size()));
|
Chris@16
|
506
|
Chris@16
|
507 return result;
|
Chris@16
|
508 }
|
Chris@16
|
509
|
Chris@16
|
510 //! Clears the pool.
|
Chris@16
|
511 //! This causes memory occupied by nodes allocated by the pool to be freed.
|
Chris@16
|
512 //! Any nodes or strings allocated from the pool will no longer be valid.
|
Chris@16
|
513 void clear()
|
Chris@16
|
514 {
|
Chris@16
|
515 while (m_begin != m_static_memory)
|
Chris@16
|
516 {
|
Chris@16
|
517 char *previous_begin = reinterpret_cast<header *>(align(m_begin))->previous_begin;
|
Chris@16
|
518 if (m_free_func)
|
Chris@16
|
519 m_free_func(m_begin);
|
Chris@16
|
520 else
|
Chris@16
|
521 delete[] m_begin;
|
Chris@16
|
522 m_begin = previous_begin;
|
Chris@16
|
523 }
|
Chris@16
|
524 init();
|
Chris@16
|
525 }
|
Chris@16
|
526
|
Chris@16
|
527 //! Sets or resets the user-defined memory allocation functions for the pool.
|
Chris@16
|
528 //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined.
|
Chris@16
|
529 //! Allocation function must not return invalid pointer on failure. It should either throw,
|
Chris@16
|
530 //! stop the program, or use <code>longjmp()</code> function to pass control to other place of program.
|
Chris@16
|
531 //! If it returns invalid pointer, results are undefined.
|
Chris@16
|
532 //! <br><br>
|
Chris@16
|
533 //! User defined allocation functions must have the following forms:
|
Chris@16
|
534 //! <br><code>
|
Chris@16
|
535 //! <br>void *allocate(std::size_t size);
|
Chris@16
|
536 //! <br>void free(void *pointer);
|
Chris@16
|
537 //! </code><br>
|
Chris@16
|
538 //! \param af Allocation function, or 0 to restore default function
|
Chris@16
|
539 //! \param ff Free function, or 0 to restore default function
|
Chris@16
|
540 void set_allocator(boost_ptree_raw_alloc_func *af, boost_ptree_raw_free_func *ff)
|
Chris@16
|
541 {
|
Chris@16
|
542 BOOST_ASSERT(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet
|
Chris@16
|
543 m_alloc_func = af;
|
Chris@16
|
544 m_free_func = ff;
|
Chris@16
|
545 }
|
Chris@16
|
546
|
Chris@16
|
547 private:
|
Chris@16
|
548
|
Chris@16
|
549 struct header
|
Chris@16
|
550 {
|
Chris@16
|
551 char *previous_begin;
|
Chris@16
|
552 };
|
Chris@16
|
553
|
Chris@16
|
554 void init()
|
Chris@16
|
555 {
|
Chris@16
|
556 m_begin = m_static_memory;
|
Chris@16
|
557 m_ptr = align(m_begin);
|
Chris@16
|
558 m_end = m_static_memory + sizeof(m_static_memory);
|
Chris@16
|
559 }
|
Chris@16
|
560
|
Chris@16
|
561 char *align(char *ptr)
|
Chris@16
|
562 {
|
Chris@16
|
563 std::size_t alignment = ((BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT - 1))) & (BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT - 1));
|
Chris@16
|
564 return ptr + alignment;
|
Chris@16
|
565 }
|
Chris@16
|
566
|
Chris@16
|
567 char *allocate_raw(std::size_t size)
|
Chris@16
|
568 {
|
Chris@16
|
569 // Allocate
|
Chris@16
|
570 void *memory;
|
Chris@16
|
571 if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[]
|
Chris@16
|
572 {
|
Chris@16
|
573 memory = m_alloc_func(size);
|
Chris@16
|
574 BOOST_ASSERT(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp
|
Chris@16
|
575 }
|
Chris@16
|
576 else
|
Chris@16
|
577 {
|
Chris@16
|
578 memory = new char[size];
|
Chris@16
|
579 }
|
Chris@16
|
580 return static_cast<char *>(memory);
|
Chris@16
|
581 }
|
Chris@16
|
582
|
Chris@16
|
583 void *allocate_aligned(std::size_t size)
|
Chris@16
|
584 {
|
Chris@16
|
585 // Calculate aligned pointer
|
Chris@16
|
586 char *result = align(m_ptr);
|
Chris@16
|
587
|
Chris@16
|
588 // If not enough memory left in current pool, allocate a new pool
|
Chris@16
|
589 if (result + size > m_end)
|
Chris@16
|
590 {
|
Chris@16
|
591 // Calculate required pool size (may be bigger than BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE)
|
Chris@16
|
592 std::size_t pool_size = BOOST_PROPERTY_TREE_RAPIDXML_DYNAMIC_POOL_SIZE;
|
Chris@16
|
593 if (pool_size < size)
|
Chris@16
|
594 pool_size = size;
|
Chris@16
|
595
|
Chris@16
|
596 // Allocate
|
Chris@16
|
597 std::size_t alloc_size = sizeof(header) + (2 * BOOST_PROPERTY_TREE_RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation
|
Chris@16
|
598 char *raw_memory = allocate_raw(alloc_size);
|
Chris@16
|
599
|
Chris@16
|
600 // Setup new pool in allocated memory
|
Chris@16
|
601 char *pool = align(raw_memory);
|
Chris@16
|
602 header *new_header = reinterpret_cast<header *>(pool);
|
Chris@16
|
603 new_header->previous_begin = m_begin;
|
Chris@16
|
604 m_begin = raw_memory;
|
Chris@16
|
605 m_ptr = pool + sizeof(header);
|
Chris@16
|
606 m_end = raw_memory + alloc_size;
|
Chris@16
|
607
|
Chris@16
|
608 // Calculate aligned pointer again using new pool
|
Chris@16
|
609 result = align(m_ptr);
|
Chris@16
|
610 }
|
Chris@16
|
611
|
Chris@16
|
612 // Update pool and return aligned pointer
|
Chris@16
|
613 m_ptr = result + size;
|
Chris@16
|
614 return result;
|
Chris@16
|
615 }
|
Chris@16
|
616
|
Chris@16
|
617 char *m_begin; // Start of raw memory making up current pool
|
Chris@16
|
618 char *m_ptr; // First free byte in current pool
|
Chris@16
|
619 char *m_end; // One past last available byte in current pool
|
Chris@16
|
620 char m_static_memory[BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory
|
Chris@16
|
621 boost_ptree_raw_alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used
|
Chris@16
|
622 boost_ptree_raw_free_func *m_free_func; // Free function, or 0 if default is to be used
|
Chris@16
|
623 };
|
Chris@16
|
624
|
Chris@16
|
625 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
626 // XML base
|
Chris@16
|
627
|
Chris@16
|
628 //! Base class for xml_node and xml_attribute implementing common functions:
|
Chris@16
|
629 //! name(), name_size(), value(), value_size() and parent().
|
Chris@16
|
630 //! \param Ch Character type to use
|
Chris@16
|
631 template<class Ch = char>
|
Chris@16
|
632 class xml_base
|
Chris@16
|
633 {
|
Chris@16
|
634
|
Chris@16
|
635 public:
|
Chris@16
|
636
|
Chris@16
|
637 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
638 // Construction & destruction
|
Chris@16
|
639
|
Chris@16
|
640 // Construct a base with empty name, value and parent
|
Chris@16
|
641 xml_base()
|
Chris@16
|
642 : m_name(0)
|
Chris@16
|
643 , m_value(0)
|
Chris@16
|
644 , m_parent(0)
|
Chris@16
|
645 {
|
Chris@16
|
646 }
|
Chris@16
|
647
|
Chris@16
|
648 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
649 // Node data access
|
Chris@16
|
650
|
Chris@16
|
651 //! Gets name of the node.
|
Chris@16
|
652 //! Interpretation of name depends on type of node.
|
Chris@16
|
653 //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
|
Chris@16
|
654 //! <br><br>
|
Chris@16
|
655 //! Use name_size() function to determine length of the name.
|
Chris@16
|
656 //! \return Name of node, or empty string if node has no name.
|
Chris@16
|
657 Ch *name() const
|
Chris@16
|
658 {
|
Chris@16
|
659 return m_name ? m_name : nullstr();
|
Chris@16
|
660 }
|
Chris@16
|
661
|
Chris@16
|
662 //! Gets size of node name, not including terminator character.
|
Chris@16
|
663 //! This function works correctly irrespective of whether name is or is not zero terminated.
|
Chris@16
|
664 //! \return Size of node name, in characters.
|
Chris@16
|
665 std::size_t name_size() const
|
Chris@16
|
666 {
|
Chris@16
|
667 return m_name ? m_name_size : 0;
|
Chris@16
|
668 }
|
Chris@16
|
669
|
Chris@16
|
670 //! Gets value of node.
|
Chris@16
|
671 //! Interpretation of value depends on type of node.
|
Chris@16
|
672 //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
|
Chris@16
|
673 //! <br><br>
|
Chris@16
|
674 //! Use value_size() function to determine length of the value.
|
Chris@16
|
675 //! \return Value of node, or empty string if node has no value.
|
Chris@16
|
676 Ch *value() const
|
Chris@16
|
677 {
|
Chris@16
|
678 return m_value ? m_value : nullstr();
|
Chris@16
|
679 }
|
Chris@16
|
680
|
Chris@16
|
681 //! Gets size of node value, not including terminator character.
|
Chris@16
|
682 //! This function works correctly irrespective of whether value is or is not zero terminated.
|
Chris@16
|
683 //! \return Size of node value, in characters.
|
Chris@16
|
684 std::size_t value_size() const
|
Chris@16
|
685 {
|
Chris@16
|
686 return m_value ? m_value_size : 0;
|
Chris@16
|
687 }
|
Chris@16
|
688
|
Chris@16
|
689 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
690 // Node modification
|
Chris@16
|
691
|
Chris@16
|
692 //! Sets name of node to a non zero-terminated string.
|
Chris@16
|
693 //! See \ref ownership_of_strings.
|
Chris@16
|
694 //! <br><br>
|
Chris@16
|
695 //! Note that node does not own its name or value, it only stores a pointer to it.
|
Chris@16
|
696 //! It will not delete or otherwise free the pointer on destruction.
|
Chris@16
|
697 //! It is reponsibility of the user to properly manage lifetime of the string.
|
Chris@16
|
698 //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
|
Chris@16
|
699 //! on destruction of the document the string will be automatically freed.
|
Chris@16
|
700 //! <br><br>
|
Chris@16
|
701 //! Size of name must be specified separately, because name does not have to be zero terminated.
|
Chris@16
|
702 //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).
|
Chris@16
|
703 //! \param n Name of node to set. Does not have to be zero terminated.
|
Chris@16
|
704 //! \param size Size of name, in characters. This does not include zero terminator, if one is present.
|
Chris@16
|
705 void name(const Ch *n, std::size_t size)
|
Chris@16
|
706 {
|
Chris@16
|
707 m_name = const_cast<Ch *>(n);
|
Chris@16
|
708 m_name_size = size;
|
Chris@16
|
709 }
|
Chris@16
|
710
|
Chris@16
|
711 //! Sets name of node to a zero-terminated string.
|
Chris@16
|
712 //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t).
|
Chris@16
|
713 //! \param n Name of node to set. Must be zero terminated.
|
Chris@16
|
714 void name(const Ch *n)
|
Chris@16
|
715 {
|
Chris@16
|
716 name(n, internal::measure(n));
|
Chris@16
|
717 }
|
Chris@16
|
718
|
Chris@16
|
719 //! Sets value of node to a non zero-terminated string.
|
Chris@16
|
720 //! See \ref ownership_of_strings.
|
Chris@16
|
721 //! <br><br>
|
Chris@16
|
722 //! Note that node does not own its name or value, it only stores a pointer to it.
|
Chris@16
|
723 //! It will not delete or otherwise free the pointer on destruction.
|
Chris@16
|
724 //! It is reponsibility of the user to properly manage lifetime of the string.
|
Chris@16
|
725 //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
|
Chris@16
|
726 //! on destruction of the document the string will be automatically freed.
|
Chris@16
|
727 //! <br><br>
|
Chris@16
|
728 //! Size of value must be specified separately, because it does not have to be zero terminated.
|
Chris@16
|
729 //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).
|
Chris@16
|
730 //! <br><br>
|
Chris@16
|
731 //! If an element has a child node of type node_data, it will take precedence over element value when printing.
|
Chris@16
|
732 //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.
|
Chris@16
|
733 //! \param val value of node to set. Does not have to be zero terminated.
|
Chris@16
|
734 //! \param size Size of value, in characters. This does not include zero terminator, if one is present.
|
Chris@16
|
735 void value(const Ch *val, std::size_t size)
|
Chris@16
|
736 {
|
Chris@16
|
737 m_value = const_cast<Ch *>(val);
|
Chris@16
|
738 m_value_size = size;
|
Chris@16
|
739 }
|
Chris@16
|
740
|
Chris@16
|
741 //! Sets value of node to a zero-terminated string.
|
Chris@16
|
742 //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t).
|
Chris@16
|
743 //! \param val Vame of node to set. Must be zero terminated.
|
Chris@16
|
744 void value(const Ch *val)
|
Chris@16
|
745 {
|
Chris@16
|
746 this->value(val, internal::measure(val));
|
Chris@16
|
747 }
|
Chris@16
|
748
|
Chris@16
|
749 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
750 // Related nodes access
|
Chris@16
|
751
|
Chris@16
|
752 //! Gets node parent.
|
Chris@16
|
753 //! \return Pointer to parent node, or 0 if there is no parent.
|
Chris@16
|
754 xml_node<Ch> *parent() const
|
Chris@16
|
755 {
|
Chris@16
|
756 return m_parent;
|
Chris@16
|
757 }
|
Chris@16
|
758
|
Chris@16
|
759 protected:
|
Chris@16
|
760
|
Chris@16
|
761 // Return empty string
|
Chris@16
|
762 static Ch *nullstr()
|
Chris@16
|
763 {
|
Chris@16
|
764 static Ch zero = Ch('\0');
|
Chris@16
|
765 return &zero;
|
Chris@16
|
766 }
|
Chris@16
|
767
|
Chris@16
|
768 Ch *m_name; // Name of node, or 0 if no name
|
Chris@16
|
769 Ch *m_value; // Value of node, or 0 if no value
|
Chris@16
|
770 std::size_t m_name_size; // Length of node name, or undefined of no name
|
Chris@16
|
771 std::size_t m_value_size; // Length of node value, or undefined if no value
|
Chris@16
|
772 xml_node<Ch> *m_parent; // Pointer to parent node, or 0 if none
|
Chris@16
|
773
|
Chris@16
|
774 };
|
Chris@16
|
775
|
Chris@16
|
776 //! Class representing attribute node of XML document.
|
Chris@16
|
777 //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base).
|
Chris@16
|
778 //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing.
|
Chris@16
|
779 //! Thus, this text must persist in memory for the lifetime of attribute.
|
Chris@16
|
780 //! \param Ch Character type to use.
|
Chris@16
|
781 template<class Ch = char>
|
Chris@16
|
782 class xml_attribute: public xml_base<Ch>
|
Chris@16
|
783 {
|
Chris@16
|
784
|
Chris@16
|
785 friend class xml_node<Ch>;
|
Chris@16
|
786
|
Chris@16
|
787 public:
|
Chris@16
|
788
|
Chris@16
|
789 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
790 // Construction & destruction
|
Chris@16
|
791
|
Chris@16
|
792 //! Constructs an empty attribute with the specified type.
|
Chris@16
|
793 //! Consider using memory_pool of appropriate xml_document if allocating attributes manually.
|
Chris@16
|
794 xml_attribute()
|
Chris@16
|
795 {
|
Chris@16
|
796 }
|
Chris@16
|
797
|
Chris@16
|
798 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
799 // Related nodes access
|
Chris@16
|
800
|
Chris@16
|
801 //! Gets document of which attribute is a child.
|
Chris@16
|
802 //! \return Pointer to document that contains this attribute, or 0 if there is no parent document.
|
Chris@16
|
803 xml_document<Ch> *document() const
|
Chris@16
|
804 {
|
Chris@16
|
805 if (xml_node<Ch> *node = this->parent())
|
Chris@16
|
806 {
|
Chris@16
|
807 while (node->parent())
|
Chris@16
|
808 node = node->parent();
|
Chris@16
|
809 return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;
|
Chris@16
|
810 }
|
Chris@16
|
811 else
|
Chris@16
|
812 return 0;
|
Chris@16
|
813 }
|
Chris@16
|
814
|
Chris@16
|
815 //! Gets previous attribute, optionally matching attribute name.
|
Chris@16
|
816 //! \param n Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
817 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
818 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
819 //! \return Pointer to found attribute, or 0 if not found.
|
Chris@16
|
820 xml_attribute<Ch> *previous_attribute(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
821 {
|
Chris@16
|
822 if (n)
|
Chris@16
|
823 {
|
Chris@16
|
824 if (nsize == 0)
|
Chris@16
|
825 nsize = internal::measure(n);
|
Chris@16
|
826 for (xml_attribute<Ch> *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute)
|
Chris@16
|
827 if (internal::compare(attribute->name(), attribute->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
828 return attribute;
|
Chris@16
|
829 return 0;
|
Chris@16
|
830 }
|
Chris@16
|
831 else
|
Chris@16
|
832 return this->m_parent ? m_prev_attribute : 0;
|
Chris@16
|
833 }
|
Chris@16
|
834
|
Chris@16
|
835 //! Gets next attribute, optionally matching attribute name.
|
Chris@16
|
836 //! \param n Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
837 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
838 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
839 //! \return Pointer to found attribute, or 0 if not found.
|
Chris@16
|
840 xml_attribute<Ch> *next_attribute(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
841 {
|
Chris@16
|
842 if (n)
|
Chris@16
|
843 {
|
Chris@16
|
844 if (nsize == 0)
|
Chris@16
|
845 nsize = internal::measure(n);
|
Chris@16
|
846 for (xml_attribute<Ch> *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute)
|
Chris@16
|
847 if (internal::compare(attribute->name(), attribute->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
848 return attribute;
|
Chris@16
|
849 return 0;
|
Chris@16
|
850 }
|
Chris@16
|
851 else
|
Chris@16
|
852 return this->m_parent ? m_next_attribute : 0;
|
Chris@16
|
853 }
|
Chris@16
|
854
|
Chris@16
|
855 private:
|
Chris@16
|
856
|
Chris@16
|
857 xml_attribute<Ch> *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero
|
Chris@16
|
858 xml_attribute<Ch> *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero
|
Chris@16
|
859
|
Chris@16
|
860 };
|
Chris@16
|
861
|
Chris@16
|
862 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
863 // XML node
|
Chris@16
|
864
|
Chris@16
|
865 //! Class representing a node of XML document.
|
Chris@16
|
866 //! Each node may have associated name and value strings, which are available through name() and value() functions.
|
Chris@16
|
867 //! Interpretation of name and value depends on type of the node.
|
Chris@16
|
868 //! Type of node can be determined by using type() function.
|
Chris@16
|
869 //! <br><br>
|
Chris@16
|
870 //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing.
|
Chris@16
|
871 //! Thus, this text must persist in the memory for the lifetime of node.
|
Chris@16
|
872 //! \param Ch Character type to use.
|
Chris@16
|
873 template<class Ch = char>
|
Chris@16
|
874 class xml_node: public xml_base<Ch>
|
Chris@16
|
875 {
|
Chris@16
|
876
|
Chris@16
|
877 public:
|
Chris@16
|
878
|
Chris@16
|
879 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
880 // Construction & destruction
|
Chris@16
|
881
|
Chris@16
|
882 //! Constructs an empty node with the specified type.
|
Chris@16
|
883 //! Consider using memory_pool of appropriate document to allocate nodes manually.
|
Chris@16
|
884 //! \param t Type of node to construct.
|
Chris@16
|
885 xml_node(node_type t)
|
Chris@16
|
886 : m_type(t)
|
Chris@16
|
887 , m_first_node(0)
|
Chris@16
|
888 , m_first_attribute(0)
|
Chris@16
|
889 {
|
Chris@16
|
890 }
|
Chris@16
|
891
|
Chris@16
|
892 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
893 // Node data access
|
Chris@16
|
894
|
Chris@16
|
895 //! Gets type of node.
|
Chris@16
|
896 //! \return Type of node.
|
Chris@16
|
897 node_type type() const
|
Chris@16
|
898 {
|
Chris@16
|
899 return m_type;
|
Chris@16
|
900 }
|
Chris@16
|
901
|
Chris@16
|
902 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
903 // Related nodes access
|
Chris@16
|
904
|
Chris@16
|
905 //! Gets document of which node is a child.
|
Chris@16
|
906 //! \return Pointer to document that contains this node, or 0 if there is no parent document.
|
Chris@16
|
907 xml_document<Ch> *document() const
|
Chris@16
|
908 {
|
Chris@16
|
909 xml_node<Ch> *node = const_cast<xml_node<Ch> *>(this);
|
Chris@16
|
910 while (node->parent())
|
Chris@16
|
911 node = node->parent();
|
Chris@16
|
912 return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;
|
Chris@16
|
913 }
|
Chris@16
|
914
|
Chris@16
|
915 //! Gets first child node, optionally matching node name.
|
Chris@16
|
916 //! \param n Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
917 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
918 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
919 //! \return Pointer to found child, or 0 if not found.
|
Chris@16
|
920 xml_node<Ch> *first_node(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
921 {
|
Chris@16
|
922 if (n)
|
Chris@16
|
923 {
|
Chris@16
|
924 if (nsize == 0)
|
Chris@16
|
925 nsize = internal::measure(n);
|
Chris@16
|
926 for (xml_node<Ch> *child = m_first_node; child; child = child->next_sibling())
|
Chris@16
|
927 if (internal::compare(child->name(), child->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
928 return child;
|
Chris@16
|
929 return 0;
|
Chris@16
|
930 }
|
Chris@16
|
931 else
|
Chris@16
|
932 return m_first_node;
|
Chris@16
|
933 }
|
Chris@16
|
934
|
Chris@16
|
935 //! Gets last child node, optionally matching node name.
|
Chris@16
|
936 //! Behaviour is undefined if node has no children.
|
Chris@16
|
937 //! Use first_node() to test if node has children.
|
Chris@16
|
938 //! \param n Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
939 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
940 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
941 //! \return Pointer to found child, or 0 if not found.
|
Chris@16
|
942 xml_node<Ch> *last_node(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
943 {
|
Chris@16
|
944 BOOST_ASSERT(m_first_node); // Cannot query for last child if node has no children
|
Chris@16
|
945 if (n)
|
Chris@16
|
946 {
|
Chris@16
|
947 if (nsize == 0)
|
Chris@16
|
948 nsize = internal::measure(n);
|
Chris@16
|
949 for (xml_node<Ch> *child = m_last_node; child; child = child->previous_sibling())
|
Chris@16
|
950 if (internal::compare(child->name(), child->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
951 return child;
|
Chris@16
|
952 return 0;
|
Chris@16
|
953 }
|
Chris@16
|
954 else
|
Chris@16
|
955 return m_last_node;
|
Chris@16
|
956 }
|
Chris@16
|
957
|
Chris@16
|
958 //! Gets previous sibling node, optionally matching node name.
|
Chris@16
|
959 //! Behaviour is undefined if node has no parent.
|
Chris@16
|
960 //! Use parent() to test if node has a parent.
|
Chris@16
|
961 //! \param n Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
962 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
963 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
964 //! \return Pointer to found sibling, or 0 if not found.
|
Chris@16
|
965 xml_node<Ch> *previous_sibling(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
966 {
|
Chris@16
|
967 BOOST_ASSERT(this->m_parent); // Cannot query for siblings if node has no parent
|
Chris@16
|
968 if (n)
|
Chris@16
|
969 {
|
Chris@16
|
970 if (nsize == 0)
|
Chris@16
|
971 nsize = internal::measure(n);
|
Chris@16
|
972 for (xml_node<Ch> *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling)
|
Chris@16
|
973 if (internal::compare(sibling->name(), sibling->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
974 return sibling;
|
Chris@16
|
975 return 0;
|
Chris@16
|
976 }
|
Chris@16
|
977 else
|
Chris@16
|
978 return m_prev_sibling;
|
Chris@16
|
979 }
|
Chris@16
|
980
|
Chris@16
|
981 //! Gets next sibling node, optionally matching node name.
|
Chris@16
|
982 //! Behaviour is undefined if node has no parent.
|
Chris@16
|
983 //! Use parent() to test if node has a parent.
|
Chris@16
|
984 //! \param n Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
985 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
986 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
987 //! \return Pointer to found sibling, or 0 if not found.
|
Chris@16
|
988 xml_node<Ch> *next_sibling(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
989 {
|
Chris@16
|
990 BOOST_ASSERT(this->m_parent); // Cannot query for siblings if node has no parent
|
Chris@16
|
991 if (n)
|
Chris@16
|
992 {
|
Chris@16
|
993 if (nsize == 0)
|
Chris@16
|
994 nsize = internal::measure(n);
|
Chris@16
|
995 for (xml_node<Ch> *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling)
|
Chris@16
|
996 if (internal::compare(sibling->name(), sibling->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
997 return sibling;
|
Chris@16
|
998 return 0;
|
Chris@16
|
999 }
|
Chris@16
|
1000 else
|
Chris@16
|
1001 return m_next_sibling;
|
Chris@16
|
1002 }
|
Chris@16
|
1003
|
Chris@16
|
1004 //! Gets first attribute of node, optionally matching attribute name.
|
Chris@16
|
1005 //! \param n Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
1006 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
1007 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
1008 //! \return Pointer to found attribute, or 0 if not found.
|
Chris@16
|
1009 xml_attribute<Ch> *first_attribute(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
1010 {
|
Chris@16
|
1011 if (n)
|
Chris@16
|
1012 {
|
Chris@16
|
1013 if (nsize == 0)
|
Chris@16
|
1014 nsize = internal::measure(n);
|
Chris@16
|
1015 for (xml_attribute<Ch> *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute)
|
Chris@16
|
1016 if (internal::compare(attribute->name(), attribute->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
1017 return attribute;
|
Chris@16
|
1018 return 0;
|
Chris@16
|
1019 }
|
Chris@16
|
1020 else
|
Chris@16
|
1021 return m_first_attribute;
|
Chris@16
|
1022 }
|
Chris@16
|
1023
|
Chris@16
|
1024 //! Gets last attribute of node, optionally matching attribute name.
|
Chris@16
|
1025 //! \param n Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if nsize is non-zero
|
Chris@16
|
1026 //! \param nsize Size of name, in characters, or 0 to have size calculated automatically from string
|
Chris@16
|
1027 //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
|
Chris@16
|
1028 //! \return Pointer to found attribute, or 0 if not found.
|
Chris@16
|
1029 xml_attribute<Ch> *last_attribute(const Ch *n = 0, std::size_t nsize = 0, bool case_sensitive = true) const
|
Chris@16
|
1030 {
|
Chris@16
|
1031 if (n)
|
Chris@16
|
1032 {
|
Chris@16
|
1033 if (nsize == 0)
|
Chris@16
|
1034 nsize = internal::measure(n);
|
Chris@16
|
1035 for (xml_attribute<Ch> *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute)
|
Chris@16
|
1036 if (internal::compare(attribute->name(), attribute->name_size(), n, nsize, case_sensitive))
|
Chris@16
|
1037 return attribute;
|
Chris@16
|
1038 return 0;
|
Chris@16
|
1039 }
|
Chris@16
|
1040 else
|
Chris@16
|
1041 return m_first_attribute ? m_last_attribute : 0;
|
Chris@16
|
1042 }
|
Chris@16
|
1043
|
Chris@16
|
1044 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
1045 // Node modification
|
Chris@16
|
1046
|
Chris@16
|
1047 //! Sets type of node.
|
Chris@16
|
1048 //! \param t Type of node to set.
|
Chris@16
|
1049 void type(node_type t)
|
Chris@16
|
1050 {
|
Chris@16
|
1051 m_type = t;
|
Chris@16
|
1052 }
|
Chris@16
|
1053
|
Chris@16
|
1054 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
1055 // Node manipulation
|
Chris@16
|
1056
|
Chris@16
|
1057 //! Prepends a new child node.
|
Chris@16
|
1058 //! The prepended child becomes the first child, and all existing children are moved one position back.
|
Chris@16
|
1059 //! \param child Node to prepend.
|
Chris@16
|
1060 void prepend_node(xml_node<Ch> *child)
|
Chris@16
|
1061 {
|
Chris@16
|
1062 BOOST_ASSERT(child && !child->parent() && child->type() != node_document);
|
Chris@16
|
1063 if (first_node())
|
Chris@16
|
1064 {
|
Chris@16
|
1065 child->m_next_sibling = m_first_node;
|
Chris@16
|
1066 m_first_node->m_prev_sibling = child;
|
Chris@16
|
1067 }
|
Chris@16
|
1068 else
|
Chris@16
|
1069 {
|
Chris@16
|
1070 child->m_next_sibling = 0;
|
Chris@16
|
1071 m_last_node = child;
|
Chris@16
|
1072 }
|
Chris@16
|
1073 m_first_node = child;
|
Chris@16
|
1074 child->m_parent = this;
|
Chris@16
|
1075 child->m_prev_sibling = 0;
|
Chris@16
|
1076 }
|
Chris@16
|
1077
|
Chris@16
|
1078 //! Appends a new child node.
|
Chris@16
|
1079 //! The appended child becomes the last child.
|
Chris@16
|
1080 //! \param child Node to append.
|
Chris@16
|
1081 void append_node(xml_node<Ch> *child)
|
Chris@16
|
1082 {
|
Chris@16
|
1083 BOOST_ASSERT(child && !child->parent() && child->type() != node_document);
|
Chris@16
|
1084 if (first_node())
|
Chris@16
|
1085 {
|
Chris@16
|
1086 child->m_prev_sibling = m_last_node;
|
Chris@16
|
1087 m_last_node->m_next_sibling = child;
|
Chris@16
|
1088 }
|
Chris@16
|
1089 else
|
Chris@16
|
1090 {
|
Chris@16
|
1091 child->m_prev_sibling = 0;
|
Chris@16
|
1092 m_first_node = child;
|
Chris@16
|
1093 }
|
Chris@16
|
1094 m_last_node = child;
|
Chris@16
|
1095 child->m_parent = this;
|
Chris@16
|
1096 child->m_next_sibling = 0;
|
Chris@16
|
1097 }
|
Chris@16
|
1098
|
Chris@16
|
1099 //! Inserts a new child node at specified place inside the node.
|
Chris@16
|
1100 //! All children after and including the specified node are moved one position back.
|
Chris@16
|
1101 //! \param where Place where to insert the child, or 0 to insert at the back.
|
Chris@16
|
1102 //! \param child Node to insert.
|
Chris@16
|
1103 void insert_node(xml_node<Ch> *where, xml_node<Ch> *child)
|
Chris@16
|
1104 {
|
Chris@16
|
1105 BOOST_ASSERT(!where || where->parent() == this);
|
Chris@16
|
1106 BOOST_ASSERT(child && !child->parent() && child->type() != node_document);
|
Chris@16
|
1107 if (where == m_first_node)
|
Chris@16
|
1108 prepend_node(child);
|
Chris@16
|
1109 else if (where == 0)
|
Chris@16
|
1110 append_node(child);
|
Chris@16
|
1111 else
|
Chris@16
|
1112 {
|
Chris@16
|
1113 child->m_prev_sibling = where->m_prev_sibling;
|
Chris@16
|
1114 child->m_next_sibling = where;
|
Chris@16
|
1115 where->m_prev_sibling->m_next_sibling = child;
|
Chris@16
|
1116 where->m_prev_sibling = child;
|
Chris@16
|
1117 child->m_parent = this;
|
Chris@16
|
1118 }
|
Chris@16
|
1119 }
|
Chris@16
|
1120
|
Chris@16
|
1121 //! Removes first child node.
|
Chris@16
|
1122 //! If node has no children, behaviour is undefined.
|
Chris@16
|
1123 //! Use first_node() to test if node has children.
|
Chris@16
|
1124 void remove_first_node()
|
Chris@16
|
1125 {
|
Chris@16
|
1126 BOOST_ASSERT(first_node());
|
Chris@16
|
1127 xml_node<Ch> *child = m_first_node;
|
Chris@16
|
1128 m_first_node = child->m_next_sibling;
|
Chris@16
|
1129 if (child->m_next_sibling)
|
Chris@16
|
1130 child->m_next_sibling->m_prev_sibling = 0;
|
Chris@16
|
1131 else
|
Chris@16
|
1132 m_last_node = 0;
|
Chris@16
|
1133 child->m_parent = 0;
|
Chris@16
|
1134 }
|
Chris@16
|
1135
|
Chris@16
|
1136 //! Removes last child of the node.
|
Chris@16
|
1137 //! If node has no children, behaviour is undefined.
|
Chris@16
|
1138 //! Use first_node() to test if node has children.
|
Chris@16
|
1139 void remove_last_node()
|
Chris@16
|
1140 {
|
Chris@16
|
1141 BOOST_ASSERT(first_node());
|
Chris@16
|
1142 xml_node<Ch> *child = m_last_node;
|
Chris@16
|
1143 if (child->m_prev_sibling)
|
Chris@16
|
1144 {
|
Chris@16
|
1145 m_last_node = child->m_prev_sibling;
|
Chris@16
|
1146 child->m_prev_sibling->m_next_sibling = 0;
|
Chris@16
|
1147 }
|
Chris@16
|
1148 else
|
Chris@16
|
1149 m_first_node = 0;
|
Chris@16
|
1150 child->m_parent = 0;
|
Chris@16
|
1151 }
|
Chris@16
|
1152
|
Chris@16
|
1153 //! Removes specified child from the node
|
Chris@16
|
1154 // \param where Pointer to child to be removed.
|
Chris@16
|
1155 void remove_node(xml_node<Ch> *where)
|
Chris@16
|
1156 {
|
Chris@16
|
1157 BOOST_ASSERT(where && where->parent() == this);
|
Chris@16
|
1158 BOOST_ASSERT(first_node());
|
Chris@16
|
1159 if (where == m_first_node)
|
Chris@16
|
1160 remove_first_node();
|
Chris@16
|
1161 else if (where == m_last_node)
|
Chris@16
|
1162 remove_last_node();
|
Chris@16
|
1163 else
|
Chris@16
|
1164 {
|
Chris@16
|
1165 where->m_prev_sibling->m_next_sibling = where->m_next_sibling;
|
Chris@16
|
1166 where->m_next_sibling->m_prev_sibling = where->m_prev_sibling;
|
Chris@16
|
1167 where->m_parent = 0;
|
Chris@16
|
1168 }
|
Chris@16
|
1169 }
|
Chris@16
|
1170
|
Chris@16
|
1171 //! Removes all child nodes (but not attributes).
|
Chris@16
|
1172 void remove_all_nodes()
|
Chris@16
|
1173 {
|
Chris@16
|
1174 for (xml_node<Ch> *node = first_node(); node; node = node->m_next_sibling)
|
Chris@16
|
1175 node->m_parent = 0;
|
Chris@16
|
1176 m_first_node = 0;
|
Chris@16
|
1177 }
|
Chris@16
|
1178
|
Chris@16
|
1179 //! Prepends a new attribute to the node.
|
Chris@16
|
1180 //! \param attribute Attribute to prepend.
|
Chris@16
|
1181 void prepend_attribute(xml_attribute<Ch> *attribute)
|
Chris@16
|
1182 {
|
Chris@16
|
1183 BOOST_ASSERT(attribute && !attribute->parent());
|
Chris@16
|
1184 if (first_attribute())
|
Chris@16
|
1185 {
|
Chris@16
|
1186 attribute->m_next_attribute = m_first_attribute;
|
Chris@16
|
1187 m_first_attribute->m_prev_attribute = attribute;
|
Chris@16
|
1188 }
|
Chris@16
|
1189 else
|
Chris@16
|
1190 {
|
Chris@16
|
1191 attribute->m_next_attribute = 0;
|
Chris@16
|
1192 m_last_attribute = attribute;
|
Chris@16
|
1193 }
|
Chris@16
|
1194 m_first_attribute = attribute;
|
Chris@16
|
1195 attribute->m_parent = this;
|
Chris@16
|
1196 attribute->m_prev_attribute = 0;
|
Chris@16
|
1197 }
|
Chris@16
|
1198
|
Chris@16
|
1199 //! Appends a new attribute to the node.
|
Chris@16
|
1200 //! \param attribute Attribute to append.
|
Chris@16
|
1201 void append_attribute(xml_attribute<Ch> *attribute)
|
Chris@16
|
1202 {
|
Chris@16
|
1203 BOOST_ASSERT(attribute && !attribute->parent());
|
Chris@16
|
1204 if (first_attribute())
|
Chris@16
|
1205 {
|
Chris@16
|
1206 attribute->m_prev_attribute = m_last_attribute;
|
Chris@16
|
1207 m_last_attribute->m_next_attribute = attribute;
|
Chris@16
|
1208 }
|
Chris@16
|
1209 else
|
Chris@16
|
1210 {
|
Chris@16
|
1211 attribute->m_prev_attribute = 0;
|
Chris@16
|
1212 m_first_attribute = attribute;
|
Chris@16
|
1213 }
|
Chris@16
|
1214 m_last_attribute = attribute;
|
Chris@16
|
1215 attribute->m_parent = this;
|
Chris@16
|
1216 attribute->m_next_attribute = 0;
|
Chris@16
|
1217 }
|
Chris@16
|
1218
|
Chris@16
|
1219 //! Inserts a new attribute at specified place inside the node.
|
Chris@16
|
1220 //! All attributes after and including the specified attribute are moved one position back.
|
Chris@16
|
1221 //! \param where Place where to insert the attribute, or 0 to insert at the back.
|
Chris@16
|
1222 //! \param attribute Attribute to insert.
|
Chris@16
|
1223 void insert_attribute(xml_attribute<Ch> *where, xml_attribute<Ch> *attribute)
|
Chris@16
|
1224 {
|
Chris@16
|
1225 BOOST_ASSERT(!where || where->parent() == this);
|
Chris@16
|
1226 BOOST_ASSERT(attribute && !attribute->parent());
|
Chris@16
|
1227 if (where == m_first_attribute)
|
Chris@16
|
1228 prepend_attribute(attribute);
|
Chris@16
|
1229 else if (where == 0)
|
Chris@16
|
1230 append_attribute(attribute);
|
Chris@16
|
1231 else
|
Chris@16
|
1232 {
|
Chris@16
|
1233 attribute->m_prev_attribute = where->m_prev_attribute;
|
Chris@16
|
1234 attribute->m_next_attribute = where;
|
Chris@16
|
1235 where->m_prev_attribute->m_next_attribute = attribute;
|
Chris@16
|
1236 where->m_prev_attribute = attribute;
|
Chris@16
|
1237 attribute->m_parent = this;
|
Chris@16
|
1238 }
|
Chris@16
|
1239 }
|
Chris@16
|
1240
|
Chris@16
|
1241 //! Removes first attribute of the node.
|
Chris@16
|
1242 //! If node has no attributes, behaviour is undefined.
|
Chris@16
|
1243 //! Use first_attribute() to test if node has attributes.
|
Chris@16
|
1244 void remove_first_attribute()
|
Chris@16
|
1245 {
|
Chris@16
|
1246 BOOST_ASSERT(first_attribute());
|
Chris@16
|
1247 xml_attribute<Ch> *attribute = m_first_attribute;
|
Chris@16
|
1248 if (attribute->m_next_attribute)
|
Chris@16
|
1249 {
|
Chris@16
|
1250 attribute->m_next_attribute->m_prev_attribute = 0;
|
Chris@16
|
1251 }
|
Chris@16
|
1252 else
|
Chris@16
|
1253 m_last_attribute = 0;
|
Chris@16
|
1254 attribute->m_parent = 0;
|
Chris@16
|
1255 m_first_attribute = attribute->m_next_attribute;
|
Chris@16
|
1256 }
|
Chris@16
|
1257
|
Chris@16
|
1258 //! Removes last attribute of the node.
|
Chris@16
|
1259 //! If node has no attributes, behaviour is undefined.
|
Chris@16
|
1260 //! Use first_attribute() to test if node has attributes.
|
Chris@16
|
1261 void remove_last_attribute()
|
Chris@16
|
1262 {
|
Chris@16
|
1263 BOOST_ASSERT(first_attribute());
|
Chris@16
|
1264 xml_attribute<Ch> *attribute = m_last_attribute;
|
Chris@16
|
1265 if (attribute->m_prev_attribute)
|
Chris@16
|
1266 {
|
Chris@16
|
1267 attribute->m_prev_attribute->m_next_attribute = 0;
|
Chris@16
|
1268 m_last_attribute = attribute->m_prev_attribute;
|
Chris@16
|
1269 }
|
Chris@16
|
1270 else
|
Chris@16
|
1271 m_first_attribute = 0;
|
Chris@16
|
1272 attribute->m_parent = 0;
|
Chris@16
|
1273 }
|
Chris@16
|
1274
|
Chris@16
|
1275 //! Removes specified attribute from node.
|
Chris@16
|
1276 //! \param where Pointer to attribute to be removed.
|
Chris@16
|
1277 void remove_attribute(xml_attribute<Ch> *where)
|
Chris@16
|
1278 {
|
Chris@16
|
1279 BOOST_ASSERT(first_attribute() && where->parent() == this);
|
Chris@16
|
1280 if (where == m_first_attribute)
|
Chris@16
|
1281 remove_first_attribute();
|
Chris@16
|
1282 else if (where == m_last_attribute)
|
Chris@16
|
1283 remove_last_attribute();
|
Chris@16
|
1284 else
|
Chris@16
|
1285 {
|
Chris@16
|
1286 where->m_prev_attribute->m_next_attribute = where->m_next_attribute;
|
Chris@16
|
1287 where->m_next_attribute->m_prev_attribute = where->m_prev_attribute;
|
Chris@16
|
1288 where->m_parent = 0;
|
Chris@16
|
1289 }
|
Chris@16
|
1290 }
|
Chris@16
|
1291
|
Chris@16
|
1292 //! Removes all attributes of node.
|
Chris@16
|
1293 void remove_all_attributes()
|
Chris@16
|
1294 {
|
Chris@16
|
1295 for (xml_attribute<Ch> *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute)
|
Chris@16
|
1296 attribute->m_parent = 0;
|
Chris@16
|
1297 m_first_attribute = 0;
|
Chris@16
|
1298 }
|
Chris@16
|
1299
|
Chris@16
|
1300 private:
|
Chris@16
|
1301
|
Chris@16
|
1302 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
1303 // Restrictions
|
Chris@16
|
1304
|
Chris@16
|
1305 // No copying
|
Chris@16
|
1306 xml_node(const xml_node &);
|
Chris@16
|
1307 void operator =(const xml_node &);
|
Chris@16
|
1308
|
Chris@16
|
1309 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
1310 // Data members
|
Chris@16
|
1311
|
Chris@16
|
1312 // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0.
|
Chris@16
|
1313 // This is required for maximum performance, as it allows the parser to omit initialization of
|
Chris@16
|
1314 // unneded/redundant values.
|
Chris@16
|
1315 //
|
Chris@16
|
1316 // The rules are as follows:
|
Chris@16
|
1317 // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively
|
Chris@16
|
1318 // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage
|
Chris@16
|
1319 // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage
|
Chris@16
|
1320
|
Chris@16
|
1321 node_type m_type; // Type of node; always valid
|
Chris@16
|
1322 xml_node<Ch> *m_first_node; // Pointer to first child node, or 0 if none; always valid
|
Chris@16
|
1323 xml_node<Ch> *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero
|
Chris@16
|
1324 xml_attribute<Ch> *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid
|
Chris@16
|
1325 xml_attribute<Ch> *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero
|
Chris@16
|
1326 xml_node<Ch> *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero
|
Chris@16
|
1327 xml_node<Ch> *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero
|
Chris@16
|
1328
|
Chris@16
|
1329 };
|
Chris@16
|
1330
|
Chris@16
|
1331 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
1332 // XML document
|
Chris@16
|
1333
|
Chris@16
|
1334 //! This class represents root of the DOM hierarchy.
|
Chris@16
|
1335 //! It is also an xml_node and a memory_pool through public inheritance.
|
Chris@16
|
1336 //! Use parse() function to build a DOM tree from a zero-terminated XML text string.
|
Chris@16
|
1337 //! parse() function allocates memory for nodes and attributes by using functions of xml_document,
|
Chris@16
|
1338 //! which are inherited from memory_pool.
|
Chris@16
|
1339 //! To access root node of the document, use the document itself, as if it was an xml_node.
|
Chris@16
|
1340 //! \param Ch Character type to use.
|
Chris@16
|
1341 template<class Ch = char>
|
Chris@16
|
1342 class xml_document: public xml_node<Ch>, public memory_pool<Ch>
|
Chris@16
|
1343 {
|
Chris@16
|
1344
|
Chris@16
|
1345 public:
|
Chris@16
|
1346
|
Chris@16
|
1347 //! Constructs empty XML document
|
Chris@16
|
1348 xml_document()
|
Chris@16
|
1349 : xml_node<Ch>(node_document)
|
Chris@16
|
1350 {
|
Chris@16
|
1351 }
|
Chris@16
|
1352
|
Chris@16
|
1353 //! Parses zero-terminated XML string according to given flags.
|
Chris@16
|
1354 //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used.
|
Chris@16
|
1355 //! The string must persist for the lifetime of the document.
|
Chris@16
|
1356 //! In case of error, rapidxml::parse_error exception will be thrown.
|
Chris@16
|
1357 //! <br><br>
|
Chris@16
|
1358 //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning.
|
Chris@16
|
1359 //! Make sure that data is zero-terminated.
|
Chris@16
|
1360 //! <br><br>
|
Chris@16
|
1361 //! Document can be parsed into multiple times.
|
Chris@16
|
1362 //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.
|
Chris@16
|
1363 //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.
|
Chris@16
|
1364 template<int Flags>
|
Chris@16
|
1365 void parse(Ch *text)
|
Chris@16
|
1366 {
|
Chris@16
|
1367 BOOST_ASSERT(text);
|
Chris@16
|
1368
|
Chris@16
|
1369 // Remove current contents
|
Chris@16
|
1370 this->remove_all_nodes();
|
Chris@16
|
1371 this->remove_all_attributes();
|
Chris@16
|
1372
|
Chris@16
|
1373 // Parse BOM, if any
|
Chris@16
|
1374 parse_bom<Flags>(text);
|
Chris@16
|
1375
|
Chris@16
|
1376 // Parse children
|
Chris@16
|
1377 while (1)
|
Chris@16
|
1378 {
|
Chris@16
|
1379 // Skip whitespace before node
|
Chris@16
|
1380 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
1381 if (*text == 0)
|
Chris@16
|
1382 break;
|
Chris@16
|
1383
|
Chris@16
|
1384 // Parse and append new child
|
Chris@16
|
1385 if (*text == Ch('<'))
|
Chris@16
|
1386 {
|
Chris@16
|
1387 ++text; // Skip '<'
|
Chris@16
|
1388 if (xml_node<Ch> *node = parse_node<Flags>(text))
|
Chris@16
|
1389 this->append_node(node);
|
Chris@16
|
1390 }
|
Chris@16
|
1391 else
|
Chris@16
|
1392 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected <", text);
|
Chris@16
|
1393 }
|
Chris@16
|
1394
|
Chris@16
|
1395 }
|
Chris@16
|
1396
|
Chris@16
|
1397 //! Clears the document by deleting all nodes and clearing the memory pool.
|
Chris@16
|
1398 //! All nodes owned by document pool are destroyed.
|
Chris@16
|
1399 void clear()
|
Chris@16
|
1400 {
|
Chris@16
|
1401 this->remove_all_nodes();
|
Chris@16
|
1402 this->remove_all_attributes();
|
Chris@16
|
1403 memory_pool<Ch>::clear();
|
Chris@16
|
1404 }
|
Chris@16
|
1405
|
Chris@16
|
1406 private:
|
Chris@16
|
1407
|
Chris@16
|
1408 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
1409 // Internal character utility functions
|
Chris@16
|
1410
|
Chris@16
|
1411 // Detect whitespace character
|
Chris@16
|
1412 struct whitespace_pred
|
Chris@16
|
1413 {
|
Chris@16
|
1414 static unsigned char test(Ch ch)
|
Chris@16
|
1415 {
|
Chris@16
|
1416 return internal::lookup_tables<0>::lookup_whitespace[internal::get_index(ch)];
|
Chris@16
|
1417 }
|
Chris@16
|
1418 };
|
Chris@16
|
1419
|
Chris@16
|
1420 // Detect node name character
|
Chris@16
|
1421 struct node_name_pred
|
Chris@16
|
1422 {
|
Chris@16
|
1423 static unsigned char test(Ch ch)
|
Chris@16
|
1424 {
|
Chris@16
|
1425 return internal::lookup_tables<0>::lookup_node_name[internal::get_index(ch)];
|
Chris@16
|
1426 }
|
Chris@16
|
1427 };
|
Chris@16
|
1428
|
Chris@16
|
1429 // Detect attribute name character
|
Chris@16
|
1430 struct attribute_name_pred
|
Chris@16
|
1431 {
|
Chris@16
|
1432 static unsigned char test(Ch ch)
|
Chris@16
|
1433 {
|
Chris@16
|
1434 return internal::lookup_tables<0>::lookup_attribute_name[internal::get_index(ch)];
|
Chris@16
|
1435 }
|
Chris@16
|
1436 };
|
Chris@16
|
1437
|
Chris@16
|
1438 // Detect text character (PCDATA)
|
Chris@16
|
1439 struct text_pred
|
Chris@16
|
1440 {
|
Chris@16
|
1441 static unsigned char test(Ch ch)
|
Chris@16
|
1442 {
|
Chris@16
|
1443 return internal::lookup_tables<0>::lookup_text[internal::get_index(ch)];
|
Chris@16
|
1444 }
|
Chris@16
|
1445 };
|
Chris@16
|
1446
|
Chris@16
|
1447 // Detect text character (PCDATA) that does not require processing
|
Chris@16
|
1448 struct text_pure_no_ws_pred
|
Chris@16
|
1449 {
|
Chris@16
|
1450 static unsigned char test(Ch ch)
|
Chris@16
|
1451 {
|
Chris@16
|
1452 return internal::lookup_tables<0>::lookup_text_pure_no_ws[internal::get_index(ch)];
|
Chris@16
|
1453 }
|
Chris@16
|
1454 };
|
Chris@16
|
1455
|
Chris@16
|
1456 // Detect text character (PCDATA) that does not require processing
|
Chris@16
|
1457 struct text_pure_with_ws_pred
|
Chris@16
|
1458 {
|
Chris@16
|
1459 static unsigned char test(Ch ch)
|
Chris@16
|
1460 {
|
Chris@16
|
1461 return internal::lookup_tables<0>::lookup_text_pure_with_ws[internal::get_index(ch)];
|
Chris@16
|
1462 }
|
Chris@16
|
1463 };
|
Chris@16
|
1464
|
Chris@16
|
1465 // Detect attribute value character
|
Chris@16
|
1466 template<Ch Quote>
|
Chris@16
|
1467 struct attribute_value_pred
|
Chris@16
|
1468 {
|
Chris@16
|
1469 static unsigned char test(Ch ch)
|
Chris@16
|
1470 {
|
Chris@16
|
1471 if (Quote == Ch('\''))
|
Chris@16
|
1472 return internal::lookup_tables<0>::lookup_attribute_data_1[internal::get_index(ch)];
|
Chris@16
|
1473 if (Quote == Ch('\"'))
|
Chris@16
|
1474 return internal::lookup_tables<0>::lookup_attribute_data_2[internal::get_index(ch)];
|
Chris@16
|
1475 return 0; // Should never be executed, to avoid warnings on Comeau
|
Chris@16
|
1476 }
|
Chris@16
|
1477 };
|
Chris@16
|
1478
|
Chris@16
|
1479 // Detect attribute value character
|
Chris@16
|
1480 template<Ch Quote>
|
Chris@16
|
1481 struct attribute_value_pure_pred
|
Chris@16
|
1482 {
|
Chris@16
|
1483 static unsigned char test(Ch ch)
|
Chris@16
|
1484 {
|
Chris@16
|
1485 if (Quote == Ch('\''))
|
Chris@16
|
1486 return internal::lookup_tables<0>::lookup_attribute_data_1_pure[internal::get_index(ch)];
|
Chris@16
|
1487 if (Quote == Ch('\"'))
|
Chris@16
|
1488 return internal::lookup_tables<0>::lookup_attribute_data_2_pure[internal::get_index(ch)];
|
Chris@16
|
1489 return 0; // Should never be executed, to avoid warnings on Comeau
|
Chris@16
|
1490 }
|
Chris@16
|
1491 };
|
Chris@16
|
1492
|
Chris@16
|
1493 // Insert coded character, using UTF8 or 8-bit ASCII
|
Chris@16
|
1494 template<int Flags>
|
Chris@16
|
1495 static void insert_coded_character(Ch *&text, unsigned long code)
|
Chris@16
|
1496 {
|
Chris@16
|
1497 if (Flags & parse_no_utf8)
|
Chris@16
|
1498 {
|
Chris@16
|
1499 // Insert 8-bit ASCII character
|
Chris@16
|
1500 // Todo: possibly verify that code is less than 256 and use replacement char otherwise?
|
Chris@16
|
1501 text[0] = static_cast<unsigned char>(code);
|
Chris@16
|
1502 text += 1;
|
Chris@16
|
1503 }
|
Chris@16
|
1504 else
|
Chris@16
|
1505 {
|
Chris@16
|
1506 // Insert UTF8 sequence
|
Chris@16
|
1507 if (code < 0x80) // 1 byte sequence
|
Chris@16
|
1508 {
|
Chris@16
|
1509 text[0] = static_cast<unsigned char>(code);
|
Chris@16
|
1510 text += 1;
|
Chris@16
|
1511 }
|
Chris@16
|
1512 else if (code < 0x800) // 2 byte sequence
|
Chris@16
|
1513 {
|
Chris@16
|
1514 text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1515 text[0] = static_cast<unsigned char>(code | 0xC0);
|
Chris@16
|
1516 text += 2;
|
Chris@16
|
1517 }
|
Chris@16
|
1518 else if (code < 0x10000) // 3 byte sequence
|
Chris@16
|
1519 {
|
Chris@16
|
1520 text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1521 text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1522 text[0] = static_cast<unsigned char>(code | 0xE0);
|
Chris@16
|
1523 text += 3;
|
Chris@16
|
1524 }
|
Chris@16
|
1525 else if (code < 0x110000) // 4 byte sequence
|
Chris@16
|
1526 {
|
Chris@16
|
1527 text[3] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1528 text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1529 text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
|
Chris@16
|
1530 text[0] = static_cast<unsigned char>(code | 0xF0);
|
Chris@16
|
1531 text += 4;
|
Chris@16
|
1532 }
|
Chris@16
|
1533 else // Invalid, only codes up to 0x10FFFF are allowed in Unicode
|
Chris@16
|
1534 {
|
Chris@16
|
1535 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("invalid numeric character entity", text);
|
Chris@16
|
1536 }
|
Chris@16
|
1537 }
|
Chris@16
|
1538 }
|
Chris@16
|
1539
|
Chris@16
|
1540 // Skip characters until predicate evaluates to true
|
Chris@16
|
1541 template<class StopPred, int Flags>
|
Chris@16
|
1542 static void skip(Ch *&text)
|
Chris@16
|
1543 {
|
Chris@16
|
1544 Ch *tmp = text;
|
Chris@16
|
1545 while (StopPred::test(*tmp))
|
Chris@16
|
1546 ++tmp;
|
Chris@16
|
1547 text = tmp;
|
Chris@16
|
1548 }
|
Chris@16
|
1549
|
Chris@16
|
1550 // Skip characters until predicate evaluates to true while doing the following:
|
Chris@16
|
1551 // - replacing XML character entity references with proper characters (' & " < > &#...;)
|
Chris@16
|
1552 // - condensing whitespace sequences to single space character
|
Chris@16
|
1553 template<class StopPred, class StopPredPure, int Flags>
|
Chris@16
|
1554 static Ch *skip_and_expand_character_refs(Ch *&text)
|
Chris@16
|
1555 {
|
Chris@16
|
1556 // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip
|
Chris@16
|
1557 if (Flags & parse_no_entity_translation &&
|
Chris@16
|
1558 !(Flags & parse_normalize_whitespace) &&
|
Chris@16
|
1559 !(Flags & parse_trim_whitespace))
|
Chris@16
|
1560 {
|
Chris@16
|
1561 skip<StopPred, Flags>(text);
|
Chris@16
|
1562 return text;
|
Chris@16
|
1563 }
|
Chris@16
|
1564
|
Chris@16
|
1565 // Use simple skip until first modification is detected
|
Chris@16
|
1566 skip<StopPredPure, Flags>(text);
|
Chris@16
|
1567
|
Chris@16
|
1568 // Use translation skip
|
Chris@16
|
1569 Ch *src = text;
|
Chris@16
|
1570 Ch *dest = src;
|
Chris@16
|
1571 while (StopPred::test(*src))
|
Chris@16
|
1572 {
|
Chris@16
|
1573 // If entity translation is enabled
|
Chris@16
|
1574 if (!(Flags & parse_no_entity_translation))
|
Chris@16
|
1575 {
|
Chris@16
|
1576 // Test if replacement is needed
|
Chris@16
|
1577 if (src[0] == Ch('&'))
|
Chris@16
|
1578 {
|
Chris@16
|
1579 switch (src[1])
|
Chris@16
|
1580 {
|
Chris@16
|
1581
|
Chris@16
|
1582 // & '
|
Chris@16
|
1583 case Ch('a'):
|
Chris@16
|
1584 if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';'))
|
Chris@16
|
1585 {
|
Chris@16
|
1586 *dest = Ch('&');
|
Chris@16
|
1587 ++dest;
|
Chris@16
|
1588 src += 5;
|
Chris@16
|
1589 continue;
|
Chris@16
|
1590 }
|
Chris@16
|
1591 if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';'))
|
Chris@16
|
1592 {
|
Chris@16
|
1593 *dest = Ch('\'');
|
Chris@16
|
1594 ++dest;
|
Chris@16
|
1595 src += 6;
|
Chris@16
|
1596 continue;
|
Chris@16
|
1597 }
|
Chris@16
|
1598 break;
|
Chris@16
|
1599
|
Chris@16
|
1600 // "
|
Chris@16
|
1601 case Ch('q'):
|
Chris@16
|
1602 if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';'))
|
Chris@16
|
1603 {
|
Chris@16
|
1604 *dest = Ch('"');
|
Chris@16
|
1605 ++dest;
|
Chris@16
|
1606 src += 6;
|
Chris@16
|
1607 continue;
|
Chris@16
|
1608 }
|
Chris@16
|
1609 break;
|
Chris@16
|
1610
|
Chris@16
|
1611 // >
|
Chris@16
|
1612 case Ch('g'):
|
Chris@16
|
1613 if (src[2] == Ch('t') && src[3] == Ch(';'))
|
Chris@16
|
1614 {
|
Chris@16
|
1615 *dest = Ch('>');
|
Chris@16
|
1616 ++dest;
|
Chris@16
|
1617 src += 4;
|
Chris@16
|
1618 continue;
|
Chris@16
|
1619 }
|
Chris@16
|
1620 break;
|
Chris@16
|
1621
|
Chris@16
|
1622 // <
|
Chris@16
|
1623 case Ch('l'):
|
Chris@16
|
1624 if (src[2] == Ch('t') && src[3] == Ch(';'))
|
Chris@16
|
1625 {
|
Chris@16
|
1626 *dest = Ch('<');
|
Chris@16
|
1627 ++dest;
|
Chris@16
|
1628 src += 4;
|
Chris@16
|
1629 continue;
|
Chris@16
|
1630 }
|
Chris@16
|
1631 break;
|
Chris@16
|
1632
|
Chris@16
|
1633 // &#...; - assumes ASCII
|
Chris@16
|
1634 case Ch('#'):
|
Chris@16
|
1635 if (src[2] == Ch('x'))
|
Chris@16
|
1636 {
|
Chris@16
|
1637 unsigned long code = 0;
|
Chris@16
|
1638 src += 3; // Skip &#x
|
Chris@16
|
1639 while (1)
|
Chris@16
|
1640 {
|
Chris@16
|
1641 unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)];
|
Chris@16
|
1642 if (digit == 0xFF)
|
Chris@16
|
1643 break;
|
Chris@16
|
1644 code = code * 16 + digit;
|
Chris@16
|
1645 ++src;
|
Chris@16
|
1646 }
|
Chris@16
|
1647 insert_coded_character<Flags>(dest, code); // Put character in output
|
Chris@16
|
1648 }
|
Chris@16
|
1649 else
|
Chris@16
|
1650 {
|
Chris@16
|
1651 unsigned long code = 0;
|
Chris@16
|
1652 src += 2; // Skip &#
|
Chris@16
|
1653 while (1)
|
Chris@16
|
1654 {
|
Chris@16
|
1655 unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)];
|
Chris@16
|
1656 if (digit == 0xFF)
|
Chris@16
|
1657 break;
|
Chris@16
|
1658 code = code * 10 + digit;
|
Chris@16
|
1659 ++src;
|
Chris@16
|
1660 }
|
Chris@16
|
1661 insert_coded_character<Flags>(dest, code); // Put character in output
|
Chris@16
|
1662 }
|
Chris@16
|
1663 if (*src == Ch(';'))
|
Chris@16
|
1664 ++src;
|
Chris@16
|
1665 else
|
Chris@16
|
1666 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected ;", src);
|
Chris@16
|
1667 continue;
|
Chris@16
|
1668
|
Chris@16
|
1669 // Something else
|
Chris@16
|
1670 default:
|
Chris@16
|
1671 // Ignore, just copy '&' verbatim
|
Chris@16
|
1672 break;
|
Chris@16
|
1673
|
Chris@16
|
1674 }
|
Chris@16
|
1675 }
|
Chris@16
|
1676 }
|
Chris@16
|
1677
|
Chris@16
|
1678 // If whitespace condensing is enabled
|
Chris@16
|
1679 if (Flags & parse_normalize_whitespace)
|
Chris@16
|
1680 {
|
Chris@16
|
1681 // Test if condensing is needed
|
Chris@16
|
1682 if (whitespace_pred::test(*src))
|
Chris@16
|
1683 {
|
Chris@16
|
1684 *dest = Ch(' '); ++dest; // Put single space in dest
|
Chris@16
|
1685 ++src; // Skip first whitespace char
|
Chris@16
|
1686 // Skip remaining whitespace chars
|
Chris@16
|
1687 while (whitespace_pred::test(*src))
|
Chris@16
|
1688 ++src;
|
Chris@16
|
1689 continue;
|
Chris@16
|
1690 }
|
Chris@16
|
1691 }
|
Chris@16
|
1692
|
Chris@16
|
1693 // No replacement, only copy character
|
Chris@16
|
1694 *dest++ = *src++;
|
Chris@16
|
1695
|
Chris@16
|
1696 }
|
Chris@16
|
1697
|
Chris@16
|
1698 // Return new end
|
Chris@16
|
1699 text = src;
|
Chris@16
|
1700 return dest;
|
Chris@16
|
1701
|
Chris@16
|
1702 }
|
Chris@16
|
1703
|
Chris@16
|
1704 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
1705 // Internal parsing functions
|
Chris@16
|
1706
|
Chris@16
|
1707 // Parse UTF-8 BOM, if any
|
Chris@16
|
1708 template<int Flags>
|
Chris@16
|
1709 void parse_bom(char *&text)
|
Chris@16
|
1710 {
|
Chris@16
|
1711 if (static_cast<unsigned char>(text[0]) == 0xEF &&
|
Chris@16
|
1712 static_cast<unsigned char>(text[1]) == 0xBB &&
|
Chris@16
|
1713 static_cast<unsigned char>(text[2]) == 0xBF)
|
Chris@16
|
1714 {
|
Chris@16
|
1715 text += 3;
|
Chris@16
|
1716 }
|
Chris@16
|
1717 }
|
Chris@16
|
1718
|
Chris@16
|
1719 // Parse UTF-16/32 BOM, if any
|
Chris@16
|
1720 template<int Flags>
|
Chris@16
|
1721 void parse_bom(wchar_t *&text)
|
Chris@16
|
1722 {
|
Chris@16
|
1723 const wchar_t bom = 0xFEFF;
|
Chris@16
|
1724 if (text[0] == bom)
|
Chris@16
|
1725 {
|
Chris@16
|
1726 ++text;
|
Chris@16
|
1727 }
|
Chris@16
|
1728 }
|
Chris@16
|
1729
|
Chris@16
|
1730 // Parse XML declaration (<?xml...)
|
Chris@16
|
1731 template<int Flags>
|
Chris@16
|
1732 xml_node<Ch> *parse_xml_declaration(Ch *&text)
|
Chris@16
|
1733 {
|
Chris@16
|
1734 // If parsing of declaration is disabled
|
Chris@16
|
1735 if (!(Flags & parse_declaration_node))
|
Chris@16
|
1736 {
|
Chris@16
|
1737 // Skip until end of declaration
|
Chris@16
|
1738 while (text[0] != Ch('?') || text[1] != Ch('>'))
|
Chris@16
|
1739 {
|
Chris@16
|
1740 if (!text[0])
|
Chris@16
|
1741 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1742 ++text;
|
Chris@16
|
1743 }
|
Chris@16
|
1744 text += 2; // Skip '?>'
|
Chris@16
|
1745 return 0;
|
Chris@16
|
1746 }
|
Chris@16
|
1747
|
Chris@16
|
1748 // Create declaration
|
Chris@16
|
1749 xml_node<Ch> *declaration = this->allocate_node(node_declaration);
|
Chris@16
|
1750
|
Chris@16
|
1751 // Skip whitespace before attributes or ?>
|
Chris@16
|
1752 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
1753
|
Chris@16
|
1754 // Parse declaration attributes
|
Chris@16
|
1755 parse_node_attributes<Flags>(text, declaration);
|
Chris@16
|
1756
|
Chris@16
|
1757 // Skip ?>
|
Chris@16
|
1758 if (text[0] != Ch('?') || text[1] != Ch('>'))
|
Chris@16
|
1759 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected ?>", text);
|
Chris@16
|
1760 text += 2;
|
Chris@16
|
1761
|
Chris@16
|
1762 return declaration;
|
Chris@16
|
1763 }
|
Chris@16
|
1764
|
Chris@16
|
1765 // Parse XML comment (<!--...)
|
Chris@16
|
1766 template<int Flags>
|
Chris@16
|
1767 xml_node<Ch> *parse_comment(Ch *&text)
|
Chris@16
|
1768 {
|
Chris@16
|
1769 // If parsing of comments is disabled
|
Chris@16
|
1770 if (!(Flags & parse_comment_nodes))
|
Chris@16
|
1771 {
|
Chris@16
|
1772 // Skip until end of comment
|
Chris@16
|
1773 while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>'))
|
Chris@16
|
1774 {
|
Chris@16
|
1775 if (!text[0])
|
Chris@16
|
1776 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1777 ++text;
|
Chris@16
|
1778 }
|
Chris@16
|
1779 text += 3; // Skip '-->'
|
Chris@16
|
1780 return 0; // Do not produce comment node
|
Chris@16
|
1781 }
|
Chris@16
|
1782
|
Chris@16
|
1783 // Remember value start
|
Chris@16
|
1784 Ch *val = text;
|
Chris@16
|
1785
|
Chris@16
|
1786 // Skip until end of comment
|
Chris@16
|
1787 while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>'))
|
Chris@16
|
1788 {
|
Chris@16
|
1789 if (!text[0])
|
Chris@16
|
1790 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1791 ++text;
|
Chris@16
|
1792 }
|
Chris@16
|
1793
|
Chris@16
|
1794 // Create comment node
|
Chris@16
|
1795 xml_node<Ch> *comment = this->allocate_node(node_comment);
|
Chris@16
|
1796 comment->value(val, text - val);
|
Chris@16
|
1797
|
Chris@16
|
1798 // Place zero terminator after comment value
|
Chris@16
|
1799 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
1800 *text = Ch('\0');
|
Chris@16
|
1801
|
Chris@16
|
1802 text += 3; // Skip '-->'
|
Chris@16
|
1803 return comment;
|
Chris@16
|
1804 }
|
Chris@16
|
1805
|
Chris@16
|
1806 // Parse DOCTYPE
|
Chris@16
|
1807 template<int Flags>
|
Chris@16
|
1808 xml_node<Ch> *parse_doctype(Ch *&text)
|
Chris@16
|
1809 {
|
Chris@16
|
1810 // Remember value start
|
Chris@16
|
1811 Ch *val = text;
|
Chris@16
|
1812
|
Chris@16
|
1813 // Skip to >
|
Chris@16
|
1814 while (*text != Ch('>'))
|
Chris@16
|
1815 {
|
Chris@16
|
1816 // Determine character type
|
Chris@16
|
1817 switch (*text)
|
Chris@16
|
1818 {
|
Chris@16
|
1819
|
Chris@16
|
1820 // If '[' encountered, scan for matching ending ']' using naive algorithm with depth
|
Chris@16
|
1821 // This works for all W3C test files except for 2 most wicked
|
Chris@16
|
1822 case Ch('['):
|
Chris@16
|
1823 {
|
Chris@16
|
1824 ++text; // Skip '['
|
Chris@16
|
1825 int depth = 1;
|
Chris@16
|
1826 while (depth > 0)
|
Chris@16
|
1827 {
|
Chris@16
|
1828 switch (*text)
|
Chris@16
|
1829 {
|
Chris@16
|
1830 case Ch('['): ++depth; break;
|
Chris@16
|
1831 case Ch(']'): --depth; break;
|
Chris@16
|
1832 case 0: BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1833 default: break;
|
Chris@16
|
1834 }
|
Chris@16
|
1835 ++text;
|
Chris@16
|
1836 }
|
Chris@16
|
1837 break;
|
Chris@16
|
1838 }
|
Chris@16
|
1839
|
Chris@16
|
1840 // Error on end of text
|
Chris@16
|
1841 case Ch('\0'):
|
Chris@16
|
1842 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1843
|
Chris@16
|
1844 // Other character, skip it
|
Chris@16
|
1845 default:
|
Chris@16
|
1846 ++text;
|
Chris@16
|
1847
|
Chris@16
|
1848 }
|
Chris@16
|
1849 }
|
Chris@16
|
1850
|
Chris@16
|
1851 // If DOCTYPE nodes enabled
|
Chris@16
|
1852 if (Flags & parse_doctype_node)
|
Chris@16
|
1853 {
|
Chris@16
|
1854 // Create a new doctype node
|
Chris@16
|
1855 xml_node<Ch> *doctype = this->allocate_node(node_doctype);
|
Chris@16
|
1856 doctype->value(val, text - val);
|
Chris@16
|
1857
|
Chris@16
|
1858 // Place zero terminator after value
|
Chris@16
|
1859 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
1860 *text = Ch('\0');
|
Chris@16
|
1861
|
Chris@16
|
1862 text += 1; // skip '>'
|
Chris@16
|
1863 return doctype;
|
Chris@16
|
1864 }
|
Chris@16
|
1865 else
|
Chris@16
|
1866 {
|
Chris@16
|
1867 text += 1; // skip '>'
|
Chris@16
|
1868 return 0;
|
Chris@16
|
1869 }
|
Chris@16
|
1870
|
Chris@16
|
1871 }
|
Chris@16
|
1872
|
Chris@16
|
1873 // Parse PI
|
Chris@16
|
1874 template<int Flags>
|
Chris@16
|
1875 xml_node<Ch> *parse_pi(Ch *&text)
|
Chris@16
|
1876 {
|
Chris@16
|
1877 // If creation of PI nodes is enabled
|
Chris@16
|
1878 if (Flags & parse_pi_nodes)
|
Chris@16
|
1879 {
|
Chris@16
|
1880 // Create pi node
|
Chris@16
|
1881 xml_node<Ch> *pi = this->allocate_node(node_pi);
|
Chris@16
|
1882
|
Chris@16
|
1883 // Extract PI target name
|
Chris@16
|
1884 Ch *n = text;
|
Chris@16
|
1885 skip<node_name_pred, Flags>(text);
|
Chris@16
|
1886 if (text == n)
|
Chris@16
|
1887 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected PI target", text);
|
Chris@16
|
1888 pi->name(n, text - n);
|
Chris@16
|
1889
|
Chris@16
|
1890 // Skip whitespace between pi target and pi
|
Chris@16
|
1891 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
1892
|
Chris@16
|
1893 // Remember start of pi
|
Chris@16
|
1894 Ch *val = text;
|
Chris@16
|
1895
|
Chris@16
|
1896 // Skip to '?>'
|
Chris@16
|
1897 while (text[0] != Ch('?') || text[1] != Ch('>'))
|
Chris@16
|
1898 {
|
Chris@16
|
1899 if (*text == Ch('\0'))
|
Chris@16
|
1900 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1901 ++text;
|
Chris@16
|
1902 }
|
Chris@16
|
1903
|
Chris@16
|
1904 // Set pi value (verbatim, no entity expansion or whitespace normalization)
|
Chris@16
|
1905 pi->value(val, text - val);
|
Chris@16
|
1906
|
Chris@16
|
1907 // Place zero terminator after name and value
|
Chris@16
|
1908 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
1909 {
|
Chris@16
|
1910 pi->name()[pi->name_size()] = Ch('\0');
|
Chris@16
|
1911 pi->value()[pi->value_size()] = Ch('\0');
|
Chris@16
|
1912 }
|
Chris@16
|
1913
|
Chris@16
|
1914 text += 2; // Skip '?>'
|
Chris@16
|
1915 return pi;
|
Chris@16
|
1916 }
|
Chris@16
|
1917 else
|
Chris@16
|
1918 {
|
Chris@16
|
1919 // Skip to '?>'
|
Chris@16
|
1920 while (text[0] != Ch('?') || text[1] != Ch('>'))
|
Chris@16
|
1921 {
|
Chris@16
|
1922 if (*text == Ch('\0'))
|
Chris@16
|
1923 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
1924 ++text;
|
Chris@16
|
1925 }
|
Chris@16
|
1926 text += 2; // Skip '?>'
|
Chris@16
|
1927 return 0;
|
Chris@16
|
1928 }
|
Chris@16
|
1929 }
|
Chris@16
|
1930
|
Chris@16
|
1931 // Parse and append data
|
Chris@16
|
1932 // Return character that ends data.
|
Chris@16
|
1933 // This is necessary because this character might have been overwritten by a terminating 0
|
Chris@16
|
1934 template<int Flags>
|
Chris@16
|
1935 Ch parse_and_append_data(xml_node<Ch> *node, Ch *&text, Ch *contents_start)
|
Chris@16
|
1936 {
|
Chris@16
|
1937 // Backup to contents start if whitespace trimming is disabled
|
Chris@16
|
1938 if (!(Flags & parse_trim_whitespace))
|
Chris@16
|
1939 text = contents_start;
|
Chris@16
|
1940
|
Chris@16
|
1941 // Skip until end of data
|
Chris@16
|
1942 Ch *val = text, *end;
|
Chris@16
|
1943 if (Flags & parse_normalize_whitespace)
|
Chris@16
|
1944 end = skip_and_expand_character_refs<text_pred, text_pure_with_ws_pred, Flags>(text);
|
Chris@16
|
1945 else
|
Chris@16
|
1946 end = skip_and_expand_character_refs<text_pred, text_pure_no_ws_pred, Flags>(text);
|
Chris@16
|
1947
|
Chris@16
|
1948 // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after >
|
Chris@16
|
1949 if (Flags & parse_trim_whitespace)
|
Chris@16
|
1950 {
|
Chris@16
|
1951 if (Flags & parse_normalize_whitespace)
|
Chris@16
|
1952 {
|
Chris@16
|
1953 // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end
|
Chris@16
|
1954 if (*(end - 1) == Ch(' '))
|
Chris@16
|
1955 --end;
|
Chris@16
|
1956 }
|
Chris@16
|
1957 else
|
Chris@16
|
1958 {
|
Chris@16
|
1959 // Backup until non-whitespace character is found
|
Chris@16
|
1960 while (whitespace_pred::test(*(end - 1)))
|
Chris@16
|
1961 --end;
|
Chris@16
|
1962 }
|
Chris@16
|
1963 }
|
Chris@16
|
1964
|
Chris@16
|
1965 // If characters are still left between end and value (this test is only necessary if normalization is enabled)
|
Chris@16
|
1966 // Create new data node
|
Chris@16
|
1967 if (!(Flags & parse_no_data_nodes))
|
Chris@16
|
1968 {
|
Chris@16
|
1969 xml_node<Ch> *data = this->allocate_node(node_data);
|
Chris@16
|
1970 data->value(val, end - val);
|
Chris@16
|
1971 node->append_node(data);
|
Chris@16
|
1972 }
|
Chris@16
|
1973
|
Chris@16
|
1974 // Add data to parent node if no data exists yet
|
Chris@16
|
1975 if (!(Flags & parse_no_element_values))
|
Chris@16
|
1976 if (*node->value() == Ch('\0'))
|
Chris@16
|
1977 node->value(val, end - val);
|
Chris@16
|
1978
|
Chris@16
|
1979 // Place zero terminator after value
|
Chris@16
|
1980 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
1981 {
|
Chris@16
|
1982 Ch ch = *text;
|
Chris@16
|
1983 *end = Ch('\0');
|
Chris@16
|
1984 return ch; // Return character that ends data; this is required because zero terminator overwritten it
|
Chris@16
|
1985 }
|
Chris@16
|
1986
|
Chris@16
|
1987 // Return character that ends data
|
Chris@16
|
1988 return *text;
|
Chris@16
|
1989 }
|
Chris@16
|
1990
|
Chris@16
|
1991 // Parse CDATA
|
Chris@16
|
1992 template<int Flags>
|
Chris@16
|
1993 xml_node<Ch> *parse_cdata(Ch *&text)
|
Chris@16
|
1994 {
|
Chris@16
|
1995 // If CDATA is disabled
|
Chris@16
|
1996 if (Flags & parse_no_data_nodes)
|
Chris@16
|
1997 {
|
Chris@16
|
1998 // Skip until end of cdata
|
Chris@16
|
1999 while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>'))
|
Chris@16
|
2000 {
|
Chris@16
|
2001 if (!text[0])
|
Chris@16
|
2002 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
2003 ++text;
|
Chris@16
|
2004 }
|
Chris@16
|
2005 text += 3; // Skip ]]>
|
Chris@16
|
2006 return 0; // Do not produce CDATA node
|
Chris@16
|
2007 }
|
Chris@16
|
2008
|
Chris@16
|
2009 // Skip until end of cdata
|
Chris@16
|
2010 Ch *val = text;
|
Chris@16
|
2011 while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>'))
|
Chris@16
|
2012 {
|
Chris@16
|
2013 if (!text[0])
|
Chris@16
|
2014 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
2015 ++text;
|
Chris@16
|
2016 }
|
Chris@16
|
2017
|
Chris@16
|
2018 // Create new cdata node
|
Chris@16
|
2019 xml_node<Ch> *cdata = this->allocate_node(node_cdata);
|
Chris@16
|
2020 cdata->value(val, text - val);
|
Chris@16
|
2021
|
Chris@16
|
2022 // Place zero terminator after value
|
Chris@16
|
2023 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
2024 *text = Ch('\0');
|
Chris@16
|
2025
|
Chris@16
|
2026 text += 3; // Skip ]]>
|
Chris@16
|
2027 return cdata;
|
Chris@16
|
2028 }
|
Chris@16
|
2029
|
Chris@16
|
2030 // Parse element node
|
Chris@16
|
2031 template<int Flags>
|
Chris@16
|
2032 xml_node<Ch> *parse_element(Ch *&text)
|
Chris@16
|
2033 {
|
Chris@16
|
2034 // Create element node
|
Chris@16
|
2035 xml_node<Ch> *element = this->allocate_node(node_element);
|
Chris@16
|
2036
|
Chris@16
|
2037 // Extract element name
|
Chris@16
|
2038 Ch *n = text;
|
Chris@16
|
2039 skip<node_name_pred, Flags>(text);
|
Chris@16
|
2040 if (text == n)
|
Chris@16
|
2041 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected element name", text);
|
Chris@16
|
2042 element->name(n, text - n);
|
Chris@16
|
2043
|
Chris@16
|
2044 // Skip whitespace between element name and attributes or >
|
Chris@16
|
2045 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2046
|
Chris@16
|
2047 // Parse attributes, if any
|
Chris@16
|
2048 parse_node_attributes<Flags>(text, element);
|
Chris@16
|
2049
|
Chris@16
|
2050 // Determine ending type
|
Chris@16
|
2051 if (*text == Ch('>'))
|
Chris@16
|
2052 {
|
Chris@16
|
2053 ++text;
|
Chris@16
|
2054 parse_node_contents<Flags>(text, element);
|
Chris@16
|
2055 }
|
Chris@16
|
2056 else if (*text == Ch('/'))
|
Chris@16
|
2057 {
|
Chris@16
|
2058 ++text;
|
Chris@16
|
2059 if (*text != Ch('>'))
|
Chris@16
|
2060 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected >", text);
|
Chris@16
|
2061 ++text;
|
Chris@16
|
2062 }
|
Chris@16
|
2063 else
|
Chris@16
|
2064 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected >", text);
|
Chris@16
|
2065
|
Chris@16
|
2066 // Place zero terminator after name
|
Chris@16
|
2067 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
2068 element->name()[element->name_size()] = Ch('\0');
|
Chris@16
|
2069
|
Chris@16
|
2070 // Return parsed element
|
Chris@16
|
2071 return element;
|
Chris@16
|
2072 }
|
Chris@16
|
2073
|
Chris@16
|
2074 // Determine node type, and parse it
|
Chris@16
|
2075 template<int Flags>
|
Chris@16
|
2076 xml_node<Ch> *parse_node(Ch *&text)
|
Chris@16
|
2077 {
|
Chris@16
|
2078 // Parse proper node type
|
Chris@16
|
2079 switch (text[0])
|
Chris@16
|
2080 {
|
Chris@16
|
2081
|
Chris@16
|
2082 // <...
|
Chris@16
|
2083 default:
|
Chris@16
|
2084 // Parse and append element node
|
Chris@16
|
2085 return parse_element<Flags>(text);
|
Chris@16
|
2086
|
Chris@16
|
2087 // <?...
|
Chris@16
|
2088 case Ch('?'):
|
Chris@16
|
2089 ++text; // Skip ?
|
Chris@16
|
2090 if ((text[0] == Ch('x') || text[0] == Ch('X')) &&
|
Chris@16
|
2091 (text[1] == Ch('m') || text[1] == Ch('M')) &&
|
Chris@16
|
2092 (text[2] == Ch('l') || text[2] == Ch('L')) &&
|
Chris@16
|
2093 whitespace_pred::test(text[3]))
|
Chris@16
|
2094 {
|
Chris@16
|
2095 // '<?xml ' - xml declaration
|
Chris@16
|
2096 text += 4; // Skip 'xml '
|
Chris@16
|
2097 return parse_xml_declaration<Flags>(text);
|
Chris@16
|
2098 }
|
Chris@16
|
2099 else
|
Chris@16
|
2100 {
|
Chris@16
|
2101 // Parse PI
|
Chris@16
|
2102 return parse_pi<Flags>(text);
|
Chris@16
|
2103 }
|
Chris@16
|
2104
|
Chris@16
|
2105 // <!...
|
Chris@16
|
2106 case Ch('!'):
|
Chris@16
|
2107
|
Chris@16
|
2108 // Parse proper subset of <! node
|
Chris@16
|
2109 switch (text[1])
|
Chris@16
|
2110 {
|
Chris@16
|
2111
|
Chris@16
|
2112 // <!-
|
Chris@16
|
2113 case Ch('-'):
|
Chris@16
|
2114 if (text[2] == Ch('-'))
|
Chris@16
|
2115 {
|
Chris@16
|
2116 // '<!--' - xml comment
|
Chris@16
|
2117 text += 3; // Skip '!--'
|
Chris@16
|
2118 return parse_comment<Flags>(text);
|
Chris@16
|
2119 }
|
Chris@16
|
2120 break;
|
Chris@16
|
2121
|
Chris@16
|
2122 // <![
|
Chris@16
|
2123 case Ch('['):
|
Chris@16
|
2124 if (text[2] == Ch('C') && text[3] == Ch('D') && text[4] == Ch('A') &&
|
Chris@16
|
2125 text[5] == Ch('T') && text[6] == Ch('A') && text[7] == Ch('['))
|
Chris@16
|
2126 {
|
Chris@16
|
2127 // '<![CDATA[' - cdata
|
Chris@16
|
2128 text += 8; // Skip '![CDATA['
|
Chris@16
|
2129 return parse_cdata<Flags>(text);
|
Chris@16
|
2130 }
|
Chris@16
|
2131 break;
|
Chris@16
|
2132
|
Chris@16
|
2133 // <!D
|
Chris@16
|
2134 case Ch('D'):
|
Chris@16
|
2135 if (text[2] == Ch('O') && text[3] == Ch('C') && text[4] == Ch('T') &&
|
Chris@16
|
2136 text[5] == Ch('Y') && text[6] == Ch('P') && text[7] == Ch('E') &&
|
Chris@16
|
2137 whitespace_pred::test(text[8]))
|
Chris@16
|
2138 {
|
Chris@16
|
2139 // '<!DOCTYPE ' - doctype
|
Chris@16
|
2140 text += 9; // skip '!DOCTYPE '
|
Chris@16
|
2141 return parse_doctype<Flags>(text);
|
Chris@16
|
2142 }
|
Chris@16
|
2143 break;
|
Chris@16
|
2144
|
Chris@16
|
2145 default: break;
|
Chris@16
|
2146
|
Chris@16
|
2147 } // switch
|
Chris@16
|
2148
|
Chris@16
|
2149 // Attempt to skip other, unrecognized node types starting with <!
|
Chris@16
|
2150 ++text; // Skip !
|
Chris@16
|
2151 while (*text != Ch('>'))
|
Chris@16
|
2152 {
|
Chris@16
|
2153 if (*text == 0)
|
Chris@16
|
2154 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
2155 ++text;
|
Chris@16
|
2156 }
|
Chris@16
|
2157 ++text; // Skip '>'
|
Chris@16
|
2158 return 0; // No node recognized
|
Chris@16
|
2159
|
Chris@16
|
2160 }
|
Chris@16
|
2161 }
|
Chris@16
|
2162
|
Chris@16
|
2163 // Parse contents of the node - children, data etc.
|
Chris@16
|
2164 template<int Flags>
|
Chris@16
|
2165 void parse_node_contents(Ch *&text, xml_node<Ch> *node)
|
Chris@16
|
2166 {
|
Chris@16
|
2167 // For all children and text
|
Chris@16
|
2168 while (1)
|
Chris@16
|
2169 {
|
Chris@16
|
2170 // Skip whitespace between > and node contents
|
Chris@16
|
2171 Ch *contents_start = text; // Store start of node contents before whitespace is skipped
|
Chris@16
|
2172 if (Flags & parse_trim_whitespace)
|
Chris@16
|
2173 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2174 Ch next_char = *text;
|
Chris@16
|
2175
|
Chris@16
|
2176 // After data nodes, instead of continuing the loop, control jumps here.
|
Chris@16
|
2177 // This is because zero termination inside parse_and_append_data() function
|
Chris@16
|
2178 // would wreak havoc with the above code.
|
Chris@16
|
2179 // Also, skipping whitespace after data nodes is unnecessary.
|
Chris@16
|
2180 after_data_node:
|
Chris@16
|
2181
|
Chris@16
|
2182 // Determine what comes next: node closing, child node, data node, or 0?
|
Chris@16
|
2183 switch (next_char)
|
Chris@16
|
2184 {
|
Chris@16
|
2185
|
Chris@16
|
2186 // Node closing or child node
|
Chris@16
|
2187 case Ch('<'):
|
Chris@16
|
2188 if (text[1] == Ch('/'))
|
Chris@16
|
2189 {
|
Chris@16
|
2190 // Node closing
|
Chris@16
|
2191 text += 2; // Skip '</'
|
Chris@16
|
2192 if (Flags & parse_validate_closing_tags)
|
Chris@16
|
2193 {
|
Chris@16
|
2194 // Skip and validate closing tag name
|
Chris@16
|
2195 Ch *closing_name = text;
|
Chris@16
|
2196 skip<node_name_pred, Flags>(text);
|
Chris@16
|
2197 if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true))
|
Chris@16
|
2198 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("invalid closing tag name", text);
|
Chris@16
|
2199 }
|
Chris@16
|
2200 else
|
Chris@16
|
2201 {
|
Chris@16
|
2202 // No validation, just skip name
|
Chris@16
|
2203 skip<node_name_pred, Flags>(text);
|
Chris@16
|
2204 }
|
Chris@16
|
2205 // Skip remaining whitespace after node name
|
Chris@16
|
2206 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2207 if (*text != Ch('>'))
|
Chris@16
|
2208 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected >", text);
|
Chris@16
|
2209 ++text; // Skip '>'
|
Chris@16
|
2210 return; // Node closed, finished parsing contents
|
Chris@16
|
2211 }
|
Chris@16
|
2212 else
|
Chris@16
|
2213 {
|
Chris@16
|
2214 // Child node
|
Chris@16
|
2215 ++text; // Skip '<'
|
Chris@16
|
2216 if (xml_node<Ch> *child = parse_node<Flags>(text))
|
Chris@16
|
2217 node->append_node(child);
|
Chris@16
|
2218 }
|
Chris@16
|
2219 break;
|
Chris@16
|
2220
|
Chris@16
|
2221 // End of data - error
|
Chris@16
|
2222 case Ch('\0'):
|
Chris@16
|
2223 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("unexpected end of data", text);
|
Chris@16
|
2224
|
Chris@16
|
2225 // Data node
|
Chris@16
|
2226 default:
|
Chris@16
|
2227 next_char = parse_and_append_data<Flags>(node, text, contents_start);
|
Chris@16
|
2228 goto after_data_node; // Bypass regular processing after data nodes
|
Chris@16
|
2229
|
Chris@16
|
2230 }
|
Chris@16
|
2231 }
|
Chris@16
|
2232 }
|
Chris@16
|
2233
|
Chris@16
|
2234 // Parse XML attributes of the node
|
Chris@16
|
2235 template<int Flags>
|
Chris@16
|
2236 void parse_node_attributes(Ch *&text, xml_node<Ch> *node)
|
Chris@16
|
2237 {
|
Chris@16
|
2238 // For all attributes
|
Chris@16
|
2239 while (attribute_name_pred::test(*text))
|
Chris@16
|
2240 {
|
Chris@16
|
2241 // Extract attribute name
|
Chris@16
|
2242 Ch *n = text;
|
Chris@16
|
2243 ++text; // Skip first character of attribute name
|
Chris@16
|
2244 skip<attribute_name_pred, Flags>(text);
|
Chris@16
|
2245 if (text == n)
|
Chris@16
|
2246 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected attribute name", n);
|
Chris@16
|
2247
|
Chris@16
|
2248 // Create new attribute
|
Chris@16
|
2249 xml_attribute<Ch> *attribute = this->allocate_attribute();
|
Chris@16
|
2250 attribute->name(n, text - n);
|
Chris@16
|
2251 node->append_attribute(attribute);
|
Chris@16
|
2252
|
Chris@16
|
2253 // Skip whitespace after attribute name
|
Chris@16
|
2254 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2255
|
Chris@16
|
2256 // Skip =
|
Chris@16
|
2257 if (*text != Ch('='))
|
Chris@16
|
2258 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected =", text);
|
Chris@16
|
2259 ++text;
|
Chris@16
|
2260
|
Chris@16
|
2261 // Add terminating zero after name
|
Chris@16
|
2262 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
2263 attribute->name()[attribute->name_size()] = 0;
|
Chris@16
|
2264
|
Chris@16
|
2265 // Skip whitespace after =
|
Chris@16
|
2266 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2267
|
Chris@16
|
2268 // Skip quote and remember if it was ' or "
|
Chris@16
|
2269 Ch quote = *text;
|
Chris@16
|
2270 if (quote != Ch('\'') && quote != Ch('"'))
|
Chris@16
|
2271 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected ' or \"", text);
|
Chris@16
|
2272 ++text;
|
Chris@16
|
2273
|
Chris@16
|
2274 // Extract attribute value and expand char refs in it
|
Chris@16
|
2275 Ch *val = text, *end;
|
Chris@16
|
2276 const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes
|
Chris@16
|
2277 if (quote == Ch('\''))
|
Chris@16
|
2278 end = skip_and_expand_character_refs<attribute_value_pred<Ch('\'')>, attribute_value_pure_pred<Ch('\'')>, AttFlags>(text);
|
Chris@16
|
2279 else
|
Chris@16
|
2280 end = skip_and_expand_character_refs<attribute_value_pred<Ch('"')>, attribute_value_pure_pred<Ch('"')>, AttFlags>(text);
|
Chris@16
|
2281
|
Chris@16
|
2282 // Set attribute value
|
Chris@16
|
2283 attribute->value(val, end - val);
|
Chris@16
|
2284
|
Chris@16
|
2285 // Make sure that end quote is present
|
Chris@16
|
2286 if (*text != quote)
|
Chris@16
|
2287 BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR("expected ' or \"", text);
|
Chris@16
|
2288 ++text; // Skip quote
|
Chris@16
|
2289
|
Chris@16
|
2290 // Add terminating zero after value
|
Chris@16
|
2291 if (!(Flags & parse_no_string_terminators))
|
Chris@16
|
2292 attribute->value()[attribute->value_size()] = 0;
|
Chris@16
|
2293
|
Chris@16
|
2294 // Skip whitespace after attribute value
|
Chris@16
|
2295 skip<whitespace_pred, Flags>(text);
|
Chris@16
|
2296 }
|
Chris@16
|
2297 }
|
Chris@16
|
2298
|
Chris@16
|
2299 };
|
Chris@16
|
2300
|
Chris@16
|
2301 //! \cond internal
|
Chris@16
|
2302 namespace internal
|
Chris@16
|
2303 {
|
Chris@16
|
2304
|
Chris@16
|
2305 // Whitespace (space \n \r \t)
|
Chris@16
|
2306 template<int Dummy>
|
Chris@16
|
2307 const unsigned char lookup_tables<Dummy>::lookup_whitespace[256] =
|
Chris@16
|
2308 {
|
Chris@16
|
2309 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2310 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0
|
Chris@16
|
2311 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
|
Chris@16
|
2312 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
|
Chris@16
|
2313 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
|
Chris@16
|
2314 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
|
Chris@16
|
2315 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
|
Chris@16
|
2316 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
|
Chris@16
|
2317 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
|
Chris@16
|
2318 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
|
Chris@16
|
2319 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
|
Chris@16
|
2320 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
|
Chris@16
|
2321 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
|
Chris@16
|
2322 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
|
Chris@16
|
2323 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
|
Chris@16
|
2324 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
|
Chris@16
|
2325 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
|
Chris@16
|
2326 };
|
Chris@16
|
2327
|
Chris@16
|
2328 // Node name (anything but space \n \r \t / > ? \0)
|
Chris@16
|
2329 template<int Dummy>
|
Chris@16
|
2330 const unsigned char lookup_tables<Dummy>::lookup_node_name[256] =
|
Chris@16
|
2331 {
|
Chris@16
|
2332 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2333 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0
|
Chris@16
|
2334 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2335 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2
|
Chris@16
|
2336 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3
|
Chris@16
|
2337 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2338 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2339 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2340 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2341 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2342 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2343 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2344 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2345 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2346 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2347 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2348 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2349 };
|
Chris@16
|
2350
|
Chris@16
|
2351 // Text (i.e. PCDATA) (anything but < \0)
|
Chris@16
|
2352 template<int Dummy>
|
Chris@16
|
2353 const unsigned char lookup_tables<Dummy>::lookup_text[256] =
|
Chris@16
|
2354 {
|
Chris@16
|
2355 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2356 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2357 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2358 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2359 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3
|
Chris@16
|
2360 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2361 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2362 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2363 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2364 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2365 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2366 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2367 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2368 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2369 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2370 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2371 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2372 };
|
Chris@16
|
2373
|
Chris@16
|
2374 // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled
|
Chris@16
|
2375 // (anything but < \0 &)
|
Chris@16
|
2376 template<int Dummy>
|
Chris@16
|
2377 const unsigned char lookup_tables<Dummy>::lookup_text_pure_no_ws[256] =
|
Chris@16
|
2378 {
|
Chris@16
|
2379 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2380 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2381 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2382 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2383 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3
|
Chris@16
|
2384 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2385 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2386 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2387 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2388 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2389 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2390 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2391 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2392 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2393 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2394 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2395 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2396 };
|
Chris@16
|
2397
|
Chris@16
|
2398 // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled
|
Chris@16
|
2399 // (anything but < \0 & space \n \r \t)
|
Chris@16
|
2400 template<int Dummy>
|
Chris@16
|
2401 const unsigned char lookup_tables<Dummy>::lookup_text_pure_with_ws[256] =
|
Chris@16
|
2402 {
|
Chris@16
|
2403 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2404 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0
|
Chris@16
|
2405 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2406 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2407 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3
|
Chris@16
|
2408 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2409 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2410 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2411 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2412 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2413 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2414 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2415 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2416 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2417 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2418 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2419 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2420 };
|
Chris@16
|
2421
|
Chris@16
|
2422 // Attribute name (anything but space \n \r \t / < > = ? ! \0)
|
Chris@16
|
2423 template<int Dummy>
|
Chris@16
|
2424 const unsigned char lookup_tables<Dummy>::lookup_attribute_name[256] =
|
Chris@16
|
2425 {
|
Chris@16
|
2426 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2427 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0
|
Chris@16
|
2428 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2429 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2
|
Chris@16
|
2430 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3
|
Chris@16
|
2431 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2432 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2433 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2434 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2435 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2436 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2437 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2438 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2439 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2440 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2441 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2442 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2443 };
|
Chris@16
|
2444
|
Chris@16
|
2445 // Attribute data with single quote (anything but ' \0)
|
Chris@16
|
2446 template<int Dummy>
|
Chris@16
|
2447 const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1[256] =
|
Chris@16
|
2448 {
|
Chris@16
|
2449 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2450 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2451 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2452 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2453 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
|
Chris@16
|
2454 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2455 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2456 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2457 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2458 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2459 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2460 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2461 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2462 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2463 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2464 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2465 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2466 };
|
Chris@16
|
2467
|
Chris@16
|
2468 // Attribute data with single quote that does not require processing (anything but ' \0 &)
|
Chris@16
|
2469 template<int Dummy>
|
Chris@16
|
2470 const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1_pure[256] =
|
Chris@16
|
2471 {
|
Chris@16
|
2472 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2473 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2474 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2475 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2476 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
|
Chris@16
|
2477 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2478 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2479 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2480 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2481 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2482 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2483 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2484 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2485 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2486 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2487 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2488 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2489 };
|
Chris@16
|
2490
|
Chris@16
|
2491 // Attribute data with double quote (anything but " \0)
|
Chris@16
|
2492 template<int Dummy>
|
Chris@16
|
2493 const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2[256] =
|
Chris@16
|
2494 {
|
Chris@16
|
2495 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2496 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2497 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2498 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2499 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
|
Chris@16
|
2500 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2501 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2502 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2503 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2504 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2505 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2506 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2507 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2508 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2509 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2510 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2511 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2512 };
|
Chris@16
|
2513
|
Chris@16
|
2514 // Attribute data with double quote that does not require processing (anything but " \0 &)
|
Chris@16
|
2515 template<int Dummy>
|
Chris@16
|
2516 const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2_pure[256] =
|
Chris@16
|
2517 {
|
Chris@16
|
2518 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2519 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
Chris@16
|
2520 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
Chris@16
|
2521 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
Chris@16
|
2522 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
|
Chris@16
|
2523 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
Chris@16
|
2524 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
Chris@16
|
2525 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
Chris@16
|
2526 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
Chris@16
|
2527 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8
|
Chris@16
|
2528 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9
|
Chris@16
|
2529 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A
|
Chris@16
|
2530 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B
|
Chris@16
|
2531 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
|
Chris@16
|
2532 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
|
Chris@16
|
2533 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E
|
Chris@16
|
2534 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F
|
Chris@16
|
2535 };
|
Chris@16
|
2536
|
Chris@16
|
2537 // Digits (dec and hex, 255 denotes end of numeric character reference)
|
Chris@16
|
2538 template<int Dummy>
|
Chris@16
|
2539 const unsigned char lookup_tables<Dummy>::lookup_digits[256] =
|
Chris@16
|
2540 {
|
Chris@16
|
2541 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
Chris@16
|
2542 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0
|
Chris@16
|
2543 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1
|
Chris@16
|
2544 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2
|
Chris@16
|
2545 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3
|
Chris@16
|
2546 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4
|
Chris@16
|
2547 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5
|
Chris@16
|
2548 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6
|
Chris@16
|
2549 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7
|
Chris@16
|
2550 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8
|
Chris@16
|
2551 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9
|
Chris@16
|
2552 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A
|
Chris@16
|
2553 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B
|
Chris@16
|
2554 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C
|
Chris@16
|
2555 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D
|
Chris@16
|
2556 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E
|
Chris@16
|
2557 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F
|
Chris@16
|
2558 };
|
Chris@16
|
2559
|
Chris@16
|
2560 // Upper case conversion
|
Chris@16
|
2561 template<int Dummy>
|
Chris@16
|
2562 const unsigned char lookup_tables<Dummy>::lookup_upcase[256] =
|
Chris@16
|
2563 {
|
Chris@16
|
2564 // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F
|
Chris@16
|
2565 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0
|
Chris@16
|
2566 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1
|
Chris@16
|
2567 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2
|
Chris@16
|
2568 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3
|
Chris@16
|
2569 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4
|
Chris@16
|
2570 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5
|
Chris@16
|
2571 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6
|
Chris@16
|
2572 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7
|
Chris@16
|
2573 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8
|
Chris@16
|
2574 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9
|
Chris@16
|
2575 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A
|
Chris@16
|
2576 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B
|
Chris@16
|
2577 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C
|
Chris@16
|
2578 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D
|
Chris@16
|
2579 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E
|
Chris@16
|
2580 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F
|
Chris@16
|
2581 };
|
Chris@16
|
2582 }
|
Chris@16
|
2583 //! \endcond
|
Chris@16
|
2584
|
Chris@16
|
2585 }}}}
|
Chris@16
|
2586
|
Chris@16
|
2587 // Undefine internal macros
|
Chris@16
|
2588 #undef BOOST_PROPERTY_TREE_RAPIDXML_PARSE_ERROR
|
Chris@16
|
2589
|
Chris@16
|
2590 // On MSVC, restore warnings state
|
Chris@16
|
2591 #ifdef _MSC_VER
|
Chris@16
|
2592 #pragma warning(pop)
|
Chris@16
|
2593 #endif
|
Chris@16
|
2594
|
Chris@16
|
2595 #endif
|