From 69cc3fcb3a28d4b7f69dfa5f4dcc025eb53332d8 Mon Sep 17 00:00:00 2001
From: "arseny.kapoulkine"
Date: Mon, 6 Nov 2006 18:31:28 +0000
Subject: pugixml 0.1: sources and documentation
git-svn-id: http://pugixml.googlecode.com/svn/trunk@2 99668b35-9821-0410-8761-19e4c4f06640
---
docs/index.html | 993 +++++++++++++++++++++++++++++
docs/tree.png | Bin 0 -> 16103 bytes
src/pugixml.cpp | 1868 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/pugixml.hpp | 619 ++++++++++++++++++
4 files changed, 3480 insertions(+)
create mode 100644 docs/index.html
create mode 100644 docs/tree.png
create mode 100644 src/pugixml.cpp
create mode 100644 src/pugixml.hpp
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..f56af74
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,993 @@
+
+
+
+pugixml documentation
+
+
+
+
+
+ pugixml documentation
+ |
+
+
+
+Contents
+
+ - Introduction
+ - Document Object Model
+ - Documentation
+
- Introduction
+ - xml_parser class
+ - xml_node class
+ - xml_attribute class
+ - Iterators
+ - Miscellaneous
+ - Lifetime issues and memory management
+
+ - Parsing process
+ - W3C compliance
+ - FAQ
+ - Bugs
+ - Future work
+ - Changelog
+ - Acknowledgements
+ - License
+
+
+
+
+
+Introduction
+pugixml is just another XML parser. This is a successor to
+pugxml (well, to be honest, the only part
+that is left as is is wildcard matching code, the rest was either heavily refactored or rewritten
+from scratch). The main features (call it USP) are:
+
+
+- low memory consumption and fragmentation (the win over pugxml is ~1.5 times, TinyXML
+- ~4.5 times, Xerces (DOM) - ~7 times 1)
+- extremely high parsing speed (the win over pugxml is ~11.8 times, TinyXML - ~13.6
+times, Xerces-DOM - ~20 times 1
+- extremely high parsing speed (well, I'm repeating myself, but it's so fast, that it outperforms
+expat by 2 times on test XML) 2
+- more or less standard-conformant (it will parse any standard-compliant file correctly in w3c-
+compliance mode, with the exception of DTD related issues and XML namespaces)
+- pretty much error-ignorant (it will not choke on something like <text>You & Me</text>,
+like expat will; it will try to recover the state even if meeting an error (like finding matching
+tags for closing ones); it will parse files with data in wrong encoding; and so on)
+- clean interface (a heavily refactored pugxml's one)
+- more or less unicode-aware (actually, it assumes UTF-8 encoding of the input data, though
+it will readily work with ANSI - no UTF-16 for now (see Future work), with
+helper conversion functions (UTF-8 <-> UTF-16/32 (whatever is the default for std::wstring & wchar_t))
+- fully standard compliant code (approved by Comeau
+strict mode), multiplatform (tested on win32 only ^_^)
+- high flexibility. You can control many aspects of file parsing and DOM tree building via parsing
+options.
+
+
+Okay, you might ask - what's the catch? Everything is so cute - it's small, fast, robust, clean solution
+for parsing XML. What is missing? Ok, we are fair developers - so here is a misfeature list:
+
+
+- memory consumption. It beats every DOM-based parser that I know of - but when SAX parser comes,
+there is no chance. You can't process a 2 Gb XML file with less than 4 Gb of memory - and do it fast.
+Though pugixml behaves better, than all other DOM-based parser, so if you're stuck with DOM,
+it's not a problem.
+- memory consumption. Ok, I'm repeating myself. Again. When other parsers will allow you to provide
+XML file in a constant storage (or even as a memory mapped area), pugixml will not. So you'll
+have to copy the entire data into a non-constant storage. Moreover, it should persist during the
+parser's lifetime (the reasons for that and more about lifetimes is written below). Again, if you're
+ok with DOM - it should not be a problem, because the overall memory consumption is less (well, though
+you'll need a contiguous chunk of memory, which can be a problem).
+- lack of validation, DTD processing, XML namespaces, proper handling of encoding. If you need those -
+go take MSXML or XercesC or anything like that.
+- lack of XPath & UTF-16/32 parsing. These are not implemented for now, but they are the features
+for the next release.
+- immutability of DOM tree. It's constant. You can't change it. There are good reasons for prohibiting
+that, though it is a thing that will likely be in the next release.
+
+
+
+
+1 The tests were done on a 1 mb XML file with a 4 levels deep tree
+with a small amount of text. The times are that of building DOM tree.
+2 Obviously, you can't estimate time of building DOM tree for a
+SAX parser, so the times of reading the data into storage that closely represented the structure of
+an XML file were measured.
+
+
+
+
+Document Object Model
+
+pugixml is a DOM-based parser. This means, that the XML document is converted to a tree.
+Each XML tag is converted to a node in DOM tree. If a tag is contained in some other tag, its node
+is a child to the outer tag's one. Comments, CDATA sections and PIs (Processing Instructions) also are
+transformed into tree nodes, as is the standalone text. Each node has its type.
+
+Here is an example of an XML document:
+
+
+<?xml version="1.0"?>
+<mesh name="mesh_root">
+ <!-- here is a mesh node -->
+ some text
+ <![CDATA[[someothertext]]>
+ some more text
+ <node attr1="value1" />
+ <node attr1="value2">
+ <?TARGET somedata?>
+ <innernode/>
+ </node>
+</mesh>
+
+
+It gets converted to the following tree (note, that with some parsing options comments, PIs and CDATA
+sections are not stored in the tree, and with some options there are also nodes with whitespaces
+and the contents of PCDATA sections is a bit different (with trailing/leading whitespaces). So generally
+the resulting DOM tree depends on the parsing options):
+
+
+
+The parent-children relations are shown with lines. Some nodes have previous and next siblings
+(for example, the next sibling for node_comment node is node_pcdata with value "some text", and the
+previous sibling for node_element with name "mesh" is node_pi with target "xml" (target for PI nodes
+is stored in the node name)).
+
+
+
+Documentation
+
+
+Introduction
+
+pugixml is a library for parsing XML files, which means that you give it XML data some way,
+and it gives you the DOM tree and the ways to traverse it and to get some useful information from it.
+The library source consist of two files, the header pugixml.hpp, and the source code pugixml.cpp.
+You can either compile cpp file in your project, or build a static library (or perhaps even a DLL),
+or make the whole code use inline linkage and make one big file (as it was done in pugxml).
+All library classes reside in namespace pugi, so you can either use fully qualified
+names (pugi::xml_node) or write a using declaration (using namespace pugi;, using
+pugi::xml_node) and use plain names. All classes have the xml_ prefix.
+
+By default it's supposed that you compile the source file with your project (add it into your
+project, or add relevant entry in your Makefile, or do whatever you need to do with your compilation
+environment). The library is written in standard-conformant C++ and was tested on win32 platform
+(MSVC 7.1 (2003), MSVC 8.0 (2005)).
+
+
+xml_parser class
+
+xml_parser class is the core of parsing process; you initiate parsing with it, you get DOM
+tree from it, the nodes and attributes are stored in it. You have two ways to load a file: either
+provide a string with XML-data (it has to be null-terminated, and it will be modified during parsing
+process, so it can not be a piece of read-only memory), or with an std::istream object (any input
+stream, like std::ifstream, std::istringstream, etc.) - in this case the parser will allocate
+the necessary amount of memory (equivalent to stream's size) and read everything from the stream.
+
+The functions for parsing are:
+
+-
+
+
- This function will create a buffer with the size equal to that of provided
stream
,
+read the chunk of data from the stream and parse it with provided options (optmsk
).
+The stream does not have to persist after the call to the function, the lifetime of internal buffer
+with stream's data is managed by pugixml.
+
+
+-
+-
+
+
- This function parses the provided string with provided options, and returns the position where the
+parsing stopped (do not expect, that parsing will stop on every error, or on most of them - as I've
+said, pugixml is error ignorant). The input string is modified. The string must persist for the
+lifetime of the parser.
+
+
-
+-
+
+
- Just a convenience ctor, that calls the corresponding parse() function.
+
+-
+-
+
+
- Just a convenience ctor, that calls the corresponding parse() function.
+
+
+
+If you want to provide XML data after the creation of the parser, use the default ctor. Otherwise
+you are free to use either parsing ctors or default ctor and later - parsing function.
+
+After parsing an XML file, you'll get a DOM tree. To get access to it (or, more precisely, to its
+root), call either document() function or cast xml_parser object to xml_node by
+using the following functions:
+
+
+
+Ok, easy part behind - now let's dive into parsing options. There is a variety of them, and you
+must choose them wisely to get the needed results and the best speed/least memory overhead. At first,
+there are flags that determine which parts of the document will be put into DOM tree, and which will
+be just skipped:
+
+
+- If parse_pi is on, then processing instructions (<? ... ?>) are put into DOM
+tree (with node type node_pi, otherwise they are discarded. Note that for now the prolog
+(<?xml ... ?>) is parsed as a processing instruction.
+
Default value: on
+
In W3C mode: on
+- If parse_comments is on, then comments (<!-- ... -->) are put into DOM
+tree (with node type node_comment, otherwise they are discarded.
+
Default value: on
+
In W3C mode: on
+- If parse_cdata is on, then the content of CDATA section (<![CDATA[[ ... ]]>)
+is put into DOM tree (with node type node_cdata, otherwise it is discarded.
+
Default value: on
+
In W3C mode: on
+- If parse_ws_pcdata is off, then the content of PCDATA section (it's the plain text
+in the node, like in <some_tag>Hello!</some_tag>) is discarded if it consists only
+of space-like characters (spaces, tabs and newlines).
+
Default value: off
+
In W3C mode: on
+- If parse_ext_pcdata is off, then the content of PCDATA section is discarded if it belongs
+to root (document) node, that is it does not have a parent tag.
+
Default value: on
+
In W3C mode: off
+
+
+Then there are flags that determine how the processing of the retrieved data is done. There are
+several reasons for these flags, mainly:
+
+- parsing speed. The less processing - the more speed.
+- data fetching comfort. Sometimes you're ok with messed linefeeds, sometimes you're not. Sometimes
+you want your PCDATA trimmed, sometimes you do not. Sometimes you want your attribute values normalized,
+sometimes you do not. Some of these are normally specified in DOCTYPE, though...
+
- ...parser is not DOCTYPE aware (and will never be), so you need a way to set those properties -
+if not on per-node basis, then on per-document
+
+So, these are the processing flags:
+
+
+
+- If parse_trim_pcdata is on, then the trimming of leading/trailing space-like characters
+is performed for PCDATA content
+
Default value: on
+
In W3C mode: off
+- If parse_trim_attribute is on, then the trimming of leading/trailing space-like characters
+is performed for attribute values
+
Default value: on
+
In W3C mode: off
+- If parse_escapes_pcdata is on, then the character reference expansion is done for PCDATA
+content (replacing <lt; with <, c; with L, etc.).
+
Default value: on
+
In W3C mode: on
+- If parse_escapes_attribute is on, then the character reference expansion is done for
+attribute values (replacing <lt; with <, c; with L, etc.).
+
Default value: on
+
In W3C mode: on
+- If parse_wnorm_pcdata is on, then the whitespace normalisation is done for PCDATA content
+(this includes replacing any space-like character by a space character and converting sequences of
+spaces into a single space)
+
Default value: on
+
In W3C mode: off
+- If parse_wnorm_attribute is on, then the whitespace normalisation is done for attribute
+values
+
Default value: on
+
In W3C mode: off
+- If parse_wconv_attribute is on, then the whitespace conversion is done for attribute
+values (this is a subset of whitespace normalization, and includes only replacing space-like characters
+with spaces). If parse_wnorm_attribute is on, this flag has no effect.
+
Default value: on
+
In W3C mode: on
+- If parse_eol_cdata is on, then the end-of-line handling is done for CDATA content (this
+includes converting any pair of 0x0d 0x0a characters to a single 0x0a and converting any standalone
+0x0d to 0x0a). Note, that end-of-line handling is done for all content (PCDATA, attribute values)
+except CDATA sections (if this flag is off).
+
Default value: on
+
In W3C mode: on
+
+
+Finally, there are two more flags, that indicate closing tag parsing. When pugixml meets a
+close tags, there are three ways:
+
+- check that the tag name matches the opening tag, return an error if it does not. This is a
+standard-compliant way, is controlled by parse_check_end_tags flag, which is on in W3C mode
+- try to find the corresponding tag name (so that <foo> <bar> </foo> will be parsed
+correctly). This is controlled by parse_match_end_tags, which is on by default
+- just treat the tag as a closing tag for the node (so that <foo> ... </bar> will
+be parsed as <foo> ... </foo>). This is the fastest way, and this is what pugxml
+is doing, but it can corrupt your DOM tree. This way is chosen if both parse_check_end_tags and
+parsse_match_end_tags are off.
+
+Note, that these 2 flags are mutually exclusive.
+
+
+Did I say finally? Ok, so finally there are some helper flags, or better groups of flags.
+These are:
+
+- parse_minimal - no flag is set (this also means the fastest parsing)
+- parse_default - default set of flags
+- parse_noset - use the current parser options (see below)
+- parse_w3c - use the W3C compliance mode
+
+
+
+A couple of words on flag usage. The parsing options are just a set of bits, with each bit corresponding
+to one flag. You can turn the flag on by OR-ing the options value with this flag's constant:
+
+ parse_w3c | parse_wnorm_pcdata
+
+or turn the flag off by AND-ing the options value with the NEGation of this flag's constant:
+
+ parse_w3c & ~parse_comments
+
+You can access the current options of parser by options() method:
+
+(the latter one returns previous options). These options are used when parse_noset flag set is
+passed to parse() functions (which is the default value of corresponding parameter).
+
+
+
+xml_node class
+
+If xml_parser is a heart of constructing a DOM tree from file, xml_node is a heart
+of processing the tree. This is a simple wrapper, so it's small (4/8 bytes, depending on the size of
+pointer), you're free to copy it and it does not own anything. I'll continue with a list of methods
+with their description, with one note in advance. Some functions, that do something according to a
+string-like parameter, have a pair with a suffix _w. The _w suffix tells, that this
+function is doing a wildcard matching, instead of simple string comparison. You're free to use wildcards
+* (that is equal to any sequence of characters (possibly empty)), ? (that is equal to
+any character) and character sets ([Abc] means 'any symbol of A, b and c', [A-Z4] means
+'any symbol from A to Z, or 4', [!0-9] means 'any symbol, that is not a digit'). So the wildcard
+?ell_[0-9][0-9]_* will match strings like 'cell_23_xref', 'hell_00_', but will not match the
+strings like 'ell_23_xref', 'cell_0_x' or 'cell_0a_x'.
+
+
+ /// Access iterators for this node's collection of child nodes.
+ iterator begin() const;
+ iterator end() const;
+
+ /// Access iterators for this node's collection of child nodes (same as begin/end).
+ iterator children_begin() const;
+ iterator children_end() const;
+
+ /// Access iterators for this node's collection of attributes.
+ attribute_iterator attributes_begin() const;
+ attribute_iterator attributes_end() const;
+
+ /// Access iterators for this node's collection of siblings.
+ iterator siblings_begin() const;
+ iterator siblings_end() const;
+ |
_Winnie C++ Colorizer |
+
+Functions, returning the iterators to walk through children/siblings/attributes. More on that in
+Iterators section.
+
+
+
+This is a safe bool-like conversion operator. You can check node's validity (if (xml_node),
+ if (!xml_node), if (node1 && node2 && !node3 && cond1 && ...) - you get the idea) with
+it.
+
+
+
+ bool operator==(const xml_node& r) const;
+ bool operator!=(const xml_node& r) const;
+ bool operator<(const xml_node& r) const;
+ bool operator>(const xml_node& r) const;
+ bool operator<=(const xml_node& r) const;
+ bool operator>=(const xml_node& r) const;
+ |
_Winnie C++ Colorizer |
+
+Comparison operators
+
+
+
+if (node.empty())
is equivalent to if (!node)
+
+
+ xml_node_type type() const;
+ const char* name() const;
+ const char* value() const;
+ |
_Winnie C++ Colorizer |
+
+Access node's properties (type, name and value). If there is no name/value, the corresponding functions
+return "" - they never return NULL.
+
+
+ xml_node child(const char* name) const;
+ xml_node child_w(const char* name) const;
+ |
_Winnie C++ Colorizer |
+
+Get a child node with specified name, or xml_node() (this is an invalid node) if nothing is
+found
+
+
+ xml_attribute attribute(const char* name) const;
+ xml_attribute attribute_w(const char* name) const;
+ |
_Winnie C++ Colorizer |
+
+Get an attribute with specified name, or xml_attribute() (this is an invalid attribute) if
+nothing is found
+
+
+ xml_node sibling(const char* name) const;
+ xml_node sibling_w(const char* name) const;
+ |
_Winnie C++ Colorizer |
+
+Get a node's sibling with specified name, or xml_node() if nothing is found.
+node.sibling(name)
is equivalent to node.parent().child(name)
.
+
+
+ xml_node next_sibling(const char* name) const;
+ xml_node next_sibling_w(const char* name) const;
+ xml_node next_sibling() const;
+ |
_Winnie C++ Colorizer |
+
+These functions get the next sibling, that is, one of the siblings of that node, that is to the
+right. next_sibling()
just returns the right brother of the node (or xml_node()),
+the two other functions are searching for the sibling with the given name
+
+
+ xml_node previous_sibling(const char* name) const;
+ xml_node previous_sibling_w(const char* name) const;
+ xml_node previous_sibling() const;
+ |
_Winnie C++ Colorizer |
+
+These functions do exactly the same as next_sibling
ones, with the exception that they
+search for the left siblings.
+
+
+
+Get a parent node. The parent node for the root one (the document) is considered to be the document
+itself.
+
+
+
+Look for the first node of type node_pcdata or node_cdata among the
+children of the current node and return its contents (or "" if nothing is found)
+
+
+
+These functions get the first and last attributes of the node (or xml_attribute() if the node
+has no attributes).
+
+
+
+These functions get the first and last children of the node (or xml_node() if the node has
+no children).
+
+
+ template <typename OutputIterator> void all_elements_by_name(const char* name, OutputIterator it) const;
+ template <typename OutputIterator> void all_elements_by_name_w(const char* name, OutputIterator it) const;
+ |
_Winnie C++ Colorizer |
+
+Get all elements with the specified name in the subtree (depth-first search) and return them with
+the help of output iterator (i.e. std::back_inserter)
+
+
+ template <typename Predicate> xml_attribute find_attribute(Predicate pred) const;
+ template <typename Predicate> xml_node find_child(Predicate pred) const;
+ template <typename Predicate> xml_node find_element(Predicate pred) const;
+ |
_Winnie C++ Colorizer |
+
+Find attribute, child or a node in the subtree (find_element - depth-first search) with the help
+of the given predicate. Predicate should behave like a function which accepts a xml_node or
+xml_attribute (for find_attribute) parameter and returns bool. The first entity for which
+the predicate returned true is returned. If predicate returned false for all entities, xml_node()
+or xml_attribute() is returned.
+
+
+ xml_node first_element(const char* name) const;
+ xml_node first_element_w(const char* name) const;
+
+ xml_node first_element_by_value(const char* name, const char* value) const;
+ xml_node first_element_by_value_w(const char* name, const char* value) const;
+
+ xml_node first_element_by_attribute(const char* name, const char* attr_name, const char* attr_value) const;
+ xml_node first_element_by_attribute_w(const char* name, const char* attr_name, const char* attr_value) const;
+
+ xml_node first_element_by_attribute(const char* attr_name, const char* attr_value) const;
+ xml_node first_element_by_attribute_w(const char* attr_name, const char* attr_value) const;
+ |
_Winnie C++ Colorizer |
+
+Find the first node (depth-first search), which corresponds to the given criteria (i.e. either has
+a matching name, or a matching value, or has an attribute with given name/value, or has an attribute
+and has a matching name). Note that _w versions treat all parameters as wildcards.
+
+
+
+Return a first node (depth-first search) with a given type, or xml_node().
+
+
+
+Get a path of the node (i.e. the string of names of the nodes on the path from the DOM tree root
+to the node, separated with delimiter (/ by default).
+
+
+
+Get the first element that has the following path. The path can be absolute (beginning with delimiter) or
+relative, '..' means 'up-level' (so if we are at the path mesh/fragment/geometry/stream, ../..
+will lead us to mesh/fragment, and /mesh will lead us to mesh).
+
+
+
+Traverse the subtree (beginning with current node) with the walker, return the result. See
+Miscellaneous section for details.
+
+
+xml_attribute class
+
+Like xml_node, xml_attribute is a simple wrapper of the node's attribute.
+
+
+ bool operator==(const xml_attribute& r) const;
+ bool operator!=(const xml_attribute& r) const;
+ bool operator<(const xml_attribute& r) const;
+ bool operator>(const xml_attribute& r) const;
+ bool operator<=(const xml_attribute& r) const;
+ bool operator>=(const xml_attribute& r) const;
+ |
_Winnie C++ Colorizer |
+
+Comparison operators.
+
+
+
+Safe bool conversion - like in xml_node, use this to check for validity.
+
+
+
+Like with xml_node, if (attr.empty())
is equivalent to if (!attr)
.
+
+
+
+ xml_attribute next_attribute() const;
+ xml_attribute previous_attribute() const;
+ |
_Winnie C++ Colorizer |
+
+Get the next/previous attribute of the node, that owns the current attribute. Return xml_attribute()
+if no such attribute is found.
+
+
+
+Get the name and value of the attribute. These methods never return NULL - they return "" instead.
+
+
+
+Convert the value of an attribute to the desired type. If the conversion is not successfull, return
+default value (0 for int, 0.0 for double, 0.0f for float). These functions rely on CRT functions ato*.
+
+
+
+Convert the value of an attribute to bool. This method returns true if the first character of the
+value is '1', 't', 'T', 'y' or 'Y'. Otherwise it returns false.
+
+
+Iterators
+
+Sometimes you have to cycle through the children or the attributes of the node. You can do it either
+by using next_sibling, previous_sibling, next_attribute and previous_attribute
+(along with first_child, last_child, first_attribute and last_attribute),
+or you can use an iterator-like interface. There are two iterator types, xml_node_iterator and
+xml_attribute_iterator. They are bidirectional constant iterators, which means that you can
+either increment or decrement them, and use dereferencing and member access operators to get constant
+access to node/attribute (the constness of iterators may change with the introducing of mutable trees).
+
+In order to get the iterators, use corresponding functions of xml_node. Note that _end()
+functions return past-the-end iterator, that is, in order to get the last attribute, you'll have to
+do something like:
+
+
+ if (node.attributes_begin() != node.attributes_end()) // we have at least one attribute
+ {
+ xml_attribute last_attrib = *(--node.attributes_end());
+ ...
+ }
+ |
_Winnie C++ Colorizer |
+
+
+
+Miscellaneous
+
+If you want to traverse a subtree, you can use traverse function. There is a class
+xml_tree_walker, which has some functions that you can override in order to get custom traversing
+(the default one just does nothing).
+
+
+
+These functions are called when the processing of the node starts/ends. First begin()
+is called, then all children of the node are processed recursively, then end() is called. If
+any of these functions returns false, the traversing is stopped and the traverse() function
+returns false.
+
+
+
+These functions are called before and after the processing of node's children. If node has no children,
+none of these is called. The default behavior is to increment/decrement current node depth.
+
+
+
+Get the current depth. You can use this function to do your own indentation, for example.
+
+Lets get to some minor notes. You can safely write something like:
+
+
+
+If node has a child with the name 'geometry', and this child has an attribute 'compress', than everything
+is ok. If node has a child with the name 'geometry' with no attribute 'compress', then attribute("compress")
+will return xml_attribute(), and the corresponding call to as_bool() will return default value (false).
+If there is no child node 'geometry', the child(...) call will return xml_node(), the subsequent call
+to attribute(...) will return xml_attribute() (because there are no attributes belonging to invalid
+node), and as_bool() will again return false, so this call sequence is perfectly safe.
+
+
+Lifetime issues and memory management
+
+As parsing is done in-situ, the XML data is to persist during the lifetime of xml_parser. If
+the parsing is called via a function of xml_parser, that accepts char*, you have to ensure
+yourself, that the string will outlive the xml_parser object.
+
+The memory for nodes and attributes is allocated in blocks of data (the blocks form a linked list;
+the default size of the block is 32 kb, though you can change it via changing a memory_block_size
+constant in pugixml.hpp file. Remember that the first block is allocated on stack (it resides
+inside xml_parser object), and all subsequent blocks are allocated on heap, so expect a stack overflow
+when setting too large memory block size), so the xml_parser object (which contains the blocks)
+should outlive all xml_node and xml_attribute objects (as well as iterators), which belong
+to the parser's tree. Again, you should ensure it yourself.
+
+
+
+
+Example
+
+Ok, so you are not much of documentation reader, are you? So am I. Let's assume that you're going
+to parse an xml file... something like this:
+
+
+<?xml version="1.0" encoding="UTF-8"?>
+<mesh name="Cathedral">
+ <fragment name="Cathedral">
+ <geometry>
+ <stream usage="main" source="StAnna.dmesh" compress="true" />
+ <stream usage="ao" source="StAnna.ao" />
+ </geometry>
+ </fragment>
+ <fragment name="Cathedral">
+ ...
+ </fragment>
+ ...
+</mesh>
+
+
+<mesh> is a root node, it has 0 or more <fragment>s, each of them has a <geometry>
+node, and there are <stream> nodes with the shown attributes. We'd like to parse the file and...
+well, and do something with it's contents. There are several methods of doing that; I'll show 2 of them
+(the remaining one is using iterators).
+
+Here we exploit the knowledge of the strict hierarchy of our XML document and read the nodes from
+DOM tree accordingly. When we have an xml_node object, we can get the desired information from
+it (name, value, attributes list, nearby nodes in a tree - siblings, parent and children).
+
+
+#include <fstream>
+#include <vector>
+#include <algorithm>
+#include <iterator>
+
+#include "pugixml.hpp"
+
+using namespace pugi;
+
+int main()
+{
+ std::ifstream in("mesh.xml");
+ in.unsetf(std::ios::skipws);
+
+ std::vector<char> buf;
+ std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(), std::back_inserter(buf));
+ buf.push_back(0); // zero-terminate
+
+ xml_parser parser(&buf[0], pugi::parse_w3c);
+
+ xml_node doc = parser.document();
+
+ if (xml_node mesh = doc.first_element("mesh"))
+ {
+ // store mesh.attribute("name").value()
+
+ for (xml_node fragment = mesh.first_element("fragment"); fragment; fragment = fragment.next_sibling())
+ {
+ // store fragment.attribute("name").value()
+
+ if (xml_node geometry = fragment.first_element("geometry"))
+ for (xml_node stream = geometry.first_element("stream"); stream; stream = stream.next_sibling())
+ {
+ // store stream.attribute("usage").value()
+ // store stream.attribute("source").value()
+
+ if (stream.attribute("compress"))
+ // store stream.attribute("compress").as_bool()
+
+ }
+ }
+ }
+}
+ |
_Winnie C++ Colorizer |
+
+We can also write a class that will traverse the DOM tree and store the information from nodes based
+on their names, depths, attributes, etc. This way is well known by the users of SAX parsers. To do that,
+we have to write an implementation of xml_tree_walker interface
+
+
+#include <fstream>
+#include <vector>
+#include <algorithm>
+#include <iterator>
+
+#include "pugixml.hpp"
+
+using namespace pugi;
+
+struct mesh_parser: public xml_tree_walker
+{
+ virtual bool begin(const xml_node& node)
+ {
+ if (strcmp(node.name(), "mesh") == 0)
+ {
+ // store node.attribute("name").value()
+ }
+ else if (strcmp(node.name(), "fragment") == 0)
+ {
+ // store node.attribute("name").value()
+ }
+ else if (strcmp(node.name(), "geometry") == 0)
+ {
+ // ...
+ }
+ else if (strcmp(node.name(), "stream") == 0)
+ {
+ // store node.attribute("usage").value()
+ // store node.attribute("source").value()
+
+ if (node.attribute("compress"))
+ // store stream.attribute("compress").as_bool()
+ }
+ else return false;
+
+ return true;
+ }
+};
+
+int main()
+{
+ std::ifstream in("mesh.xml");
+ in.unsetf(std::ios::skipws);
+
+ std::vector<char> buf;
+ std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(), std::back_inserter(buf));
+ buf.push_back(0); // zero-terminate
+
+ xml_parser parser(&buf[0], pugi::parse_w3c);
+
+ mesh_parser mp;
+
+ if (!parser.document().traverse(mp))
+ // generate an error
+}
+ |
_Winnie C++ Colorizer |
+
+
+
+
+Parsing process
+
+So, let's talk a bit about parsing process, and about the reason for providing XML data as a contiguous
+writeable block of memory. Parsing is done in-situ. This means, that the strings, representing the
+parts of DOM tree (node names, attribute names and values, CDATA content, etc.) are not separately
+allocated on heap, but instead are parts of the original data. This is the keypoint to parsing speed,
+because it helps achieve the minimal amount of memory allocations (more on that below) and minimal
+amount of copying data.
+
+In-situ parsing can be done in two ways, with zero-segmenting the string (that is, set the past-the-end
+character for the part of XML string to 0, see
+this image for further details), and storing pointer + size of the string instead of pointer to
+the beginning of ASCIIZ string.
+
+Originally, pugxml had only the first way, but then authors added the second method, 'non-segmenting'
+or non-destructive parsing. The advantages of this method are: you no longer need non-constant storage;
+you can even read data from memory-mapped files directly. Well, there are disadvantages.
+For one thing, you can not do any of the transformations in-situ. The transformations that are required
+by XML standard are:
+
+- End of line handling (replacing 0x0d 0x0a with 0x0a and any standalone 0x0d with 0x0a) (for the whole
+document)
+- White space normalization for attribute values (converting space-like characters to spaces (0x20),
+sometimes trimming leading/trailing spaces and converting sequences of spaces to a single space
+- Character reference expansion (< and alike, <#0a; and alike, <40; and alike)
+- Entity reference expansion (&entityname;)
+
+
+None of these can be done in-situ. pugxml did neither character nor entity reference expansion,
+and allocated new memory when normalizing white spaces when in non-destructive mode. I chose complete
+in-situ parsing (the good thing about it is that any transformation, except entity reference, can be
+done in-situ because it does not increase the amount of characters - even converting a character
+reference to UTF-8). There is no entity reference expansion because of this and because I do not want
+to parse DOCTYPE and, moreover, use DOCTYPE in following parsing (performing selective whitespace
+normalization in attributes and CDATA sections and so on).
+
+In order to be able to modify the tree (change attribute/node names & values) with in-situ parsing,
+one needs to implement two ways of storing data (both in-situ and not). The DOM tree is now mutable,
+but it will change in the future releases (without introducing speed/memory overhead, except on clean-
+up stage).
+
+The parsing process itself is more or less straightforward, when you see it - but the impression
+is fake, because the explicit jumps are made (i.e. we know, that if we come to a closing brace (>),
+we should expect CDATA after it (or a new tag), so let's just jump to the corresponding code), and,
+well, there can be bugs (see Bugs section).
+
+And, to make things worse, memory allocation (which is done only for node and attribute structures)
+is done in pools. The pools are single-linked lists with predefined block size (32 kb by default), and
+well, it increases speed a lot (allocations are slow, and the memory gets fragmented when allocating
+a bunch of 16-byte (attribute) or 40-byte (node) structures)
+
+
+
+
+W3C compliance
+
+pugixml is not a compliant XML parser. The main reason for that is that it does not reject
+most malformed XML files. The more or less complete list of incompatibilities follows (I will be talking
+of ones when using parse_w3c mode):
+
+
+- The parser is completely DOCTYPE-ignorant, that is, it does not even skip all possible DOCTYPEs
+correctly, let alone use them for parsing
+
- It accepts multiple attributes with the same name in one node
+
- It is charset-ignorant
+
- It accepts invalid names of tags
+
- It accepts invalid attribute values (those with < in them) and does not reject invalid entity
+references or character references (in fact, it does not do DOCTYPE parsing, so it does not perform
+entity reference expansion)
+
- It does not reject comments with -- inside
+
- It does not reject PI with the names of 'xml' and alike; in fact, it parses prolog as a PI, which
+is not conformant
+
- All characters from #x1 to #x20 are considered to be whitespaces
+
- And some other things that I forgot to mention
+
+
+In short, it accepts most malformed XML files and does not do anything that is related to DOCTYPE.
+This is because the main goal was developing fast, easy-to-use and error ignorant (so you can always
+get something even from a malformed document) parser, there are some good validating and conformant
+parsers already.
+
+
+
+
+FAQ
+
+I'm always open for questions; feel free to write them to zeux@mathcentre.com.
+
+
+
+
+
+Bugs
+
+I'm always open for bug reports; feel free to write them to zeux@mathcentre.com.
+Please provide as much information as possible - version of pugixml, compiling and OS environment
+(compiler and it's version, STL version, OS version, etc.), the description of the situation in which
+the bug arises, the code and data files that show the bug, etc. - the more, the better. Though, please,
+do not send executable files.
+
+
+
+
+Future work
+
+Here are some improvements that will be done in future versions (they are sorted by priority, the
+upper ones will get there sooner).
+
+
+- Support for altering the tree (both changing nodes'/attributes' names and values and adding/deleting
+attributes/nodes) and writing the tree to stream
+
- Support for UTF-16 files (parsing BOM to get file's type and converting UTF-16 file to UTF-8 buffer
+if necessary)
+
- Improved API (I'm going to look at SelectNode from MS XML and perhaps there will be some other
+changes)
+
- Externally provided entity reference table (or perhaps even taken from DOCTYPE?)
+
- More intelligent parsing of DOCTYPE (it does not always skip DOCTYPE for now)
+
- XML 1.1 changes (changed EOL handling, normalization issues,
+
- XPath support
+
- Name your own?
+
+
+
+
+
+Changelog
+
+
+- 15.07.2006 - v0.1
+
- First private release for testing purposes
+
+
+
+
+
+
+Acknowledgements
+
+
+
+
+
+
+License
+
+The pugixml parser is released into the public domain (though this may change).
+
+
+
+Revised 15 July, 2006
+© Copyright Zeux 2006. All Rights Reserved.
+
+
diff --git a/docs/tree.png b/docs/tree.png
new file mode 100644
index 0000000..14d48d6
Binary files /dev/null and b/docs/tree.png differ
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
new file mode 100644
index 0000000..1018bda
--- /dev/null
+++ b/src/pugixml.cpp
@@ -0,0 +1,1868 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Pug Improved XML Parser - Version 0.1
+// --------------------------------------------------------
+// Copyright (C) 2006, by Zeux (zeux@mathcentre.com)
+// This work is based on the pugxml parser, which is:
+// Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+// Released into the Public Domain. Use at your own risk.
+// See pugxml.xml for further information, history, etc.
+// Contributions by Neville Franks (readonly@getsoft.com).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include "pugixml.hpp"
+
+namespace pugi
+{
+ /// A 'name=value' XML attribute structure.
+ struct xml_attribute_struct
+ {
+ /// Default ctor
+ xml_attribute_struct();
+
+ char* name; ///< Pointer to attribute name.
+ char* value; ///< Pointer to attribute value.
+
+ xml_attribute_struct* prev_attribute; ///< Previous attribute
+ xml_attribute_struct* next_attribute; ///< Next attribute
+ };
+
+ /// An XML document tree node.
+ struct xml_node_struct
+ {
+ /// Default ctor
+ /// \param type - node type
+ xml_node_struct(xml_node_type type = node_element);
+
+ xml_node_type type; ///< Node type; see xml_node_type.
+ xml_node_struct* parent; ///< Pointer to parent
+
+ char* name; ///< Pointer to element name.
+ char* value; ///< Pointer to any associated string data.
+
+ xml_node_struct* first_child; ///< First child
+ xml_node_struct* last_child; ///< Last child
+
+ xml_node_struct* prev_sibling; ///< Left brother
+ xml_node_struct* next_sibling; ///< Right brother
+
+ xml_attribute_struct* first_attribute; ///< First attribute
+ xml_attribute_struct* last_attribute; ///< Last attribute
+ };
+
+ class xml_allocator
+ {
+ public:
+ xml_allocator(xml_memory_block* root): _root(root), _count(0)
+ {
+ }
+
+ template T* allocate()
+ {
+ void* buf = memalloc(sizeof(T));
+ return new (buf) T();
+ }
+
+ template T* allocate(U val)
+ {
+ void* buf = memalloc(sizeof(T));
+ return new (buf) T(val);
+ }
+
+ private:
+ xml_memory_block* _root;
+ unsigned int _count;
+
+ void* memalloc(size_t size)
+ {
+ if (_root->size + size <= memory_block_size)
+ {
+ void* buf = _root->data + _root->size;
+ _root->size += size;
+ return buf;
+ }
+ else
+ {
+ _root->next = new xml_memory_block();
+ _root = _root->next;
+
+ _root->size = size;
+
+ ++_count;
+
+ return _root->data;
+ }
+ }
+ };
+}
+
+namespace
+{
+ using namespace pugi;
+
+ namespace utf8
+ {
+ const unsigned char BYTE_MASK = 0xBF;
+ const unsigned char BYTE_MARK = 0x80;
+ const unsigned char BYTE_MASK_READ = 0x3F;
+ const unsigned char FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+ }
+
+ // Get the size that is needed for strutf16_utf8 applied to all s characters
+ size_t strutf16_utf8_size(const wchar_t* s)
+ {
+ size_t length = 0;
+
+ for (; *s; ++s)
+ {
+ if (*s < 0x80) length += 1;
+ else if (*s < 0x800) length += 2;
+ else if (*s < 0x10000) length += 3;
+ else if (*s < 0x200000) length += 4;
+ }
+
+ return length;
+ }
+
+ // Write utf16 char to stream, return position after the last written char
+ // \param s - pointer to string
+ // \param ch - char
+ // \return position after the last char
+ // \rem yes, this is trom TinyXML. How would you write it the other way, without switch trick?..
+ char* strutf16_utf8(char* s, unsigned int ch)
+ {
+ unsigned int length;
+
+ if (ch < 0x80) length = 1;
+ else if (ch < 0x800) length = 2;
+ else if (ch < 0x10000) length = 3;
+ else if (ch < 0x200000) length = 4;
+ else return s;
+
+ s += length;
+
+ // Scary scary fall throughs.
+ switch (length)
+ {
+ case 4:
+ *--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK);
+ ch >>= 6;
+ case 3:
+ *--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK);
+ ch >>= 6;
+ case 2:
+ *--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK);
+ ch >>= 6;
+ case 1:
+ *--s = (char)(ch | utf8::FIRST_BYTE_MARK[length]);
+ }
+
+ return s + length;
+ }
+
+ // Get the size that is needed for strutf8_utf16 applied to all s characters
+ size_t strutf8_utf16_size(const char* s)
+ {
+ size_t length = 0;
+
+ for (; *s; ++s)
+ {
+ if (*s < 0x80 || (*s >= 0xC0 && *s < 0xFC)) ++length;
+ }
+
+ return length;
+ }
+
+ // Read utf16 char from utf8 stream, return position after the last read char
+ // \param s - pointer to string
+ // \param ch - char
+ // \return position after the last char
+ const char* strutf8_utf16(const char* s, unsigned int& ch)
+ {
+ unsigned int length;
+
+ const unsigned char* str = reinterpret_cast(s);
+
+ if (*str < utf8::BYTE_MARK)
+ {
+ ch = *str;
+ return s + 1;
+ }
+ else if (*str < 0xC0)
+ {
+ ch = ' ';
+ return s + 1;
+ }
+ else if (*str < 0xE0) length = 2;
+ else if (*str < 0xF0) length = 3;
+ else if (*str < 0xF8) length = 4;
+ else if (*str < 0xFC) length = 5;
+ else
+ {
+ ch = ' ';
+ return s + 1;
+ }
+
+ ch = (*str++ & ~utf8::FIRST_BYTE_MARK[length]);
+
+ // Scary scary fall throughs.
+ switch (length)
+ {
+ case 5:
+ ch <<= 6;
+ ch += (*str++ & utf8::BYTE_MASK_READ);
+ case 4:
+ ch <<= 6;
+ ch += (*str++ & utf8::BYTE_MASK_READ);
+ case 3:
+ ch <<= 6;
+ ch += (*str++ & utf8::BYTE_MASK_READ);
+ case 2:
+ ch <<= 6;
+ ch += (*str++ & utf8::BYTE_MASK_READ);
+ }
+
+ return reinterpret_cast(str);
+ }
+
+ struct xml_parser_impl
+ {
+ xml_allocator& alloc;
+ bool chartype_symbol_table[256];
+
+ bool chartype_symbol(char c) { return chartype_symbol_table[(unsigned char)c]; }
+
+ static bool chartype_space(char c) { return c < '!' && c > 0; }
+ static bool chartype_enter(char c) { return c == '<'; }
+ static bool chartype_leave(char c) { return c == '>'; }
+ static bool chartype_close(char c) { return c == '/'; }
+ static bool chartype_equals(char c) { return c == '='; }
+ static bool chartype_special(char c) { return c == '!'; }
+ static bool chartype_pi(char c) { return c == '?'; }
+ static bool chartype_dash(char c) { return c == '-'; }
+ static bool chartype_quote(char c) { return c == '"' || c == '\''; }
+ static bool chartype_lbracket(char c) { return c == '['; }
+ static bool chartype_rbracket(char c) { return c == ']'; }
+
+ template static void strconv_t(char** s)
+ {
+ if (!s || !*s) return;
+
+ // Trim whitespaces
+ if (opt_trim) while (chartype_space(**s)) ++(*s);
+
+ char* str = *s;
+ char* lastpos = str;
+
+ if (!*str) return;
+
+ while (*str)
+ {
+ if (*str == '&' && opt_escape) // &
+ {
+ char* stre = str + 1;
+
+ switch (*stre)
+ {
+ case '#': // ...
+ {
+ unsigned int ucsc = 0;
+
+ ++stre;
+
+ if (*stre == 'x') // ... (hex code)
+ {
+ ++stre;
+
+ while (*stre)
+ {
+ if (*stre >= '0' && *stre <= '9')
+ ucsc = 16 * ucsc + (*stre++ - '0');
+ else if (*stre >= 'A' && *stre <= 'F')
+ ucsc = 16 * ucsc + (*stre++ - 'A' + 10);
+ else if (*stre >= 'a' && *stre <= 'f')
+ ucsc = 16 * ucsc + (*stre++ - 'a' + 10);
+ else break;
+ }
+
+ if (*stre == ';') ++stre;
+ }
+ else // ... (dec code)
+ {
+ while (*stre >= '0' && *stre <= '9')
+ ucsc = 10 * ucsc + (*stre++ - '0');
+
+ if (*stre == ';') ++stre;
+ }
+
+ str = stre;
+ lastpos = strutf16_utf8(lastpos, ucsc);
+ continue;
+ }
+ case 'a': // &a
+ {
+ ++stre;
+
+ if (*stre == 'm') // &am
+ {
+ if (*++stre == 'p' && *++stre == ';') // &
+ {
+ *lastpos++ = '&';
+ str = ++stre;
+ continue;
+ }
+ }
+ else if (*stre == 'p') // &ap
+ {
+ if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // '
+ {
+ *lastpos++ = '\'';
+ str = ++stre;
+ continue;
+ }
+ }
+ break;
+ }
+ case 'g': // &g
+ {
+ if (*++stre == 't' && *++stre == ';') // >
+ {
+ *lastpos++ = '>';
+ str = ++stre;
+ continue;
+ }
+ break;
+ }
+ case 'l': // &l
+ {
+ if (*++stre == 't' && *++stre == ';') // <
+ {
+ *lastpos++ = '<';
+ str = ++stre;
+ continue;
+ }
+ break;
+ }
+ case 'q': // &q
+ {
+ if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // "
+ {
+ *lastpos++ = '"';
+ str = ++stre;
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ else if (chartype_space(*str) && opt_wconv)
+ {
+ if (*str == 0x0d && *(str + 1) == 0x0a) ++str;
+
+ ++str;
+ *lastpos++ = ' ';
+
+ continue;
+ }
+ else if (*str == 0x0d && !opt_wnorm)
+ {
+ if (*(str + 1) == 0x0a) ++str;
+ ++str;
+ *lastpos++ = 0x0a;
+
+ continue;
+ }
+ else if (chartype_space(*str) && opt_wnorm)
+ {
+ *lastpos++ = ' ';
+
+ while (chartype_space(*str)) ++str;
+
+ continue;
+ }
+
+ *lastpos++ = *str++;
+ }
+
+ if (opt_trim)
+ {
+ do *lastpos-- = 0;
+ while (chartype_space(*lastpos));
+ }
+ else *lastpos = 0;
+ }
+
+ static void strconv_setup(void (*&func)(char**), unsigned int opt_trim, unsigned int opt_escape, unsigned int opt_wnorm, unsigned int opt_wconv)
+ {
+ if (opt_wconv)
+ {
+ if (opt_trim)
+ {
+ if (opt_escape)
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ else
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ }
+ else
+ {
+ if (opt_escape)
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ else
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ }
+ }
+ else
+ {
+ if (opt_trim)
+ {
+ if (opt_escape)
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ else
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ }
+ else
+ {
+ if (opt_escape)
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ else
+ {
+ if (opt_wnorm) func = &strconv_t;
+ else func = &strconv_t;
+ }
+ }
+ }
+ }
+ // Allocate & append a new xml_node_struct onto the given parent.
+ // \param parent - pointer to parent node.
+ // \param type - desired node type.
+ // \return pointer to the new node
+ xml_node_struct* append_node(xml_node_struct* parent, xml_node_type type = node_element)
+ {
+ if(!parent) return NULL; // Must have a parent.
+
+ xml_node_struct* child = alloc.allocate(type); // Allocate a new child.
+ child->parent = parent; // Set it's parent pointer.
+ if (parent->last_child)
+ {
+ parent->last_child->next_sibling = child;
+ child->prev_sibling = parent->last_child;
+ parent->last_child = child;
+ }
+ else parent->first_child = parent->last_child = child;
+ return child;
+ }
+
+ // Allocate & append a new attribute to the given xml_node_struct.
+ // \param node - pointer to parent node.
+ // \return pointer to appended xml_attribute_struct.
+ xml_attribute_struct* append_attribute(xml_node_struct* node)
+ {
+ if(!node) return NULL;
+ xml_attribute_struct* a = alloc.allocate();
+
+ if (node->last_attribute)
+ {
+ node->last_attribute->next_attribute = a;
+ a->prev_attribute = node->last_attribute;
+ node->last_attribute = a;
+ }
+ else node->first_attribute = node->last_attribute = a;
+
+ return a;
+ }
+
+ // Parser utilities.
+ #define SKIPWS() { while(chartype_space(*s)) ++s; if(*s==0) return s; }
+ #define OPTSET(OPT) ( optmsk & OPT )
+ #define PUSHNODE(TYPE) { cursor = append_node(cursor,TYPE); }
+ #define POPNODE() { cursor = cursor->parent; }
+ #define SCANFOR(X) { while(*s!=0 && !(X)) ++s; if(*s==0) return s; }
+ #define SCANWHILE(X) { while((X)) ++s; if(*s==0) return s; }
+ #define ENDSEG() { ch = *s; *s = 0; ++s; if(*s==0) return s; }
+
+ xml_parser_impl(xml_allocator& alloc): alloc(alloc)
+ {
+ for (unsigned int c = 0; c < 256; ++c)
+ {
+ chartype_symbol_table[c] = c > 127 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || c == '_' || c == ':' || c == '-' || c == '.';
+ }
+ }
+
+ // Static single-pass in-situ parse the given xml string.
+ // \param s - pointer to XML-formatted string.
+ // \param xmldoc - pointer to root.
+ // \param optmsk - parse options mask.
+ // \return last string position or null.
+ char* parse(register char* s,xml_node_struct* xmldoc,unsigned int optmsk = parse_default)
+ {
+ if(!s || !xmldoc) return s;
+
+ void (*strconv_pcdata)(char**);
+ void (*strconv_attribute)(char**);
+
+ strconv_setup(strconv_attribute, OPTSET(parse_trim_attribute), OPTSET(parse_escapes_attribute), OPTSET(parse_wnorm_attribute), OPTSET(parse_wconv_attribute));
+ strconv_setup(strconv_pcdata, OPTSET(parse_trim_pcdata), OPTSET(parse_escapes_pcdata), OPTSET(parse_wnorm_pcdata), false);
+
+ char ch = 0; // Current char, in cases where we must null-terminate before we test.
+ xml_node_struct* cursor = xmldoc; // Tree node cursor.
+ char* mark = s; // Marked string position for temporary look-ahead.
+ while(*s!=0)
+ {
+ LOC_SEARCH: // Obliviously search for next element.
+ SCANFOR(chartype_enter(*s)); // Find the next '<'.
+ if(chartype_enter(*s))
+ {
+ ++s;
+ LOC_CLASSIFY: // What kind of element?
+ if(chartype_pi(*s)) // '...'
+ {
+ ++s;
+ if(chartype_symbol(*s) && OPTSET(parse_pi))
+ {
+ mark = s;
+ SCANWHILE(chartype_symbol(*s)); // Read PI target
+ ENDSEG();
+
+ PUSHNODE(node_pi); // Append a new node on the tree.
+
+ cursor->name = mark;
+
+ if (chartype_space(ch))
+ {
+ SKIPWS();
+
+ mark = s;
+ }
+ else mark = 0;
+
+ SCANFOR(chartype_pi(*s) && chartype_leave(*(s+1))); // Look for '?>'.
+ ENDSEG();
+
+ cursor->value = mark;
+
+ POPNODE();
+
+ goto LOC_LEAVE;
+ }
+ else // Bad PI or parse_pi not set.
+ {
+ SCANFOR(chartype_pi(*s) && chartype_leave(*(s+1))); // Look for '?>'.
+ ++s;
+ goto LOC_LEAVE;
+ }
+ }
+ else if(chartype_special(*s)) // 'value = s; // Save the offset.
+ }
+
+ // Scan for terminating '-->'.
+ SCANFOR(chartype_dash(*s) && chartype_dash(*(s+1)) && chartype_leave(*(s+2)));
+
+ if (OPTSET(parse_comments))
+ {
+ *s = 0; // Zero-terminate this segment at the first terminating '-'.
+ POPNODE(); // Pop since this is a standalone.
+ }
+
+ s += 2; // Step over the '\0-'.
+ goto LOC_LEAVE; // Look for any following PCDATA.
+ }
+ }
+ else if(chartype_lbracket(*s))
+ {
+ // 'value = s; // Save the offset.
+ // Scan for terminating ']]>'.
+ SCANFOR(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)));
+ ENDSEG(); // Zero-terminate this segment.
+
+ if (OPTSET(parse_eol_cdata))
+ {
+ strconv_t(&cursor->value);
+ }
+
+ POPNODE(); // Pop since this is a standalone.
+ }
+ else // Flagged for discard, but we still have to scan for the terminator.
+ {
+ // Scan for terminating ']]>'.
+ SCANFOR(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)));
+ ++s;
+ }
+ ++s; // Step over the last ']'.
+ goto LOC_LEAVE; // Look for any following PCDATA.
+ }
+ continue; // Probably a corrupted CDATA section, so just eat it.
+ }
+ else if(*s=='D' && *++s=='O' && *++s=='C' && *++s=='T' && *++s=='Y' && *++s=='P' && *++s=='E')
+ {
+ ++s;
+ SKIPWS(); // Eat any whitespace.
+ LOC_DOCTYPE:
+ SCANWHILE(chartype_quote(*s) || chartype_lbracket(*s) || chartype_leave(*s));
+ if(chartype_quote(*s)) // '...SYSTEM "..."
+ {
+ ch = *s++;
+ SCANFOR(*s == ch);
+ ++s;
+ goto LOC_DOCTYPE;
+ }
+ if(chartype_lbracket(*s)) // '...[...'
+ {
+ ++s;
+ unsigned int bd = 1; // Bracket depth counter.
+ while(*s!=0) // Loop till we're out of all brackets.
+ {
+ if(chartype_rbracket(*s)) --bd;
+ else if(chartype_lbracket(*s)) ++bd;
+ if(bd == 0) break;
+ ++s;
+ }
+ // Note: 's' now points to end of DTD, i.e.: ']'.
+ SCANFOR(chartype_leave(*s));
+ continue;
+ }
+ // Fall-through
+ continue;
+ }
+ }
+ else if(chartype_symbol(*s)) // '<#...'
+ {
+ cursor = append_node(cursor); // Append a new node to the tree.
+
+ cursor->name = s;
+ SCANWHILE(chartype_symbol(*s)); // Scan for a terminator.
+ ENDSEG(); // Save char in 'ch', terminate & step over.
+ if (*s!=0 && chartype_close(ch)) // ''
+ POPNODE(); // Pop.
+ goto LOC_LEAVE;
+ }
+ else if(*s!=0 && !chartype_space(ch))
+ {
+ if (!chartype_leave(ch)) SCANWHILE(!chartype_leave(*s));
+ if (!*s) return s;
+ goto LOC_PCDATA; // No attributes, so scan for PCDATA.
+ }
+ else if(*s!=0 && chartype_space(ch))
+ {
+ SKIPWS(); // Eat any whitespace.
+ LOC_ATTRIBUTE:
+ if(chartype_symbol(*s)) // <... #...
+ {
+ xml_attribute_struct* a = append_attribute(cursor); // Make space for this attribute.
+ a->name = s; // Save the offset.
+ SCANWHILE(chartype_symbol(*s)); // Scan for a terminator.
+ ENDSEG(); // Save char in 'ch', terminate & step over.
+ if(*s!=0 && chartype_space(ch)) SKIPWS(); // Eat any whitespace.
+ if(*s!=0 && (chartype_equals(ch) || chartype_equals(*s))) // '<... #=...'
+ {
+ if(chartype_equals(*s)) ++s;
+ SKIPWS(); // Eat any whitespace.
+ if(chartype_quote(*s)) // '<... #="...'
+ {
+ ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
+ ++s; // Step over the quote.
+ a->value = s; // Save the offset.
+ SCANFOR(*s == ch); // Scan for the terminating quote
+ ENDSEG(); // Save char in 'ch', terminate & step over.
+
+ strconv_attribute(&a->value);
+
+ if(chartype_leave(*s))
+ {
+ ++s;
+ goto LOC_PCDATA;
+ }
+ else if(chartype_close(*s))
+ {
+ ++s;
+ POPNODE();
+ SKIPWS(); // Eat any whitespace.
+ goto LOC_LEAVE;
+ }
+ else if(chartype_space(*s)) // This may indicate a following attribute.
+ {
+ SKIPWS(); // Eat any whitespace.
+ goto LOC_ATTRIBUTE; // Go scan for additional attributes.
+ }
+ }
+ }
+ goto LOC_ATTRIBUTE;
+ }
+
+ SCANWHILE(!chartype_leave(*s) && !chartype_close(*s));
+ }
+ LOC_LEAVE:
+ if(chartype_leave(*s)) // '...>'
+ {
+ ++s; // Step over the '>'.
+ LOC_PCDATA: // '>...<'
+ mark = s; // Save this offset while searching for a terminator.
+ SKIPWS(); // Eat whitespace if no genuine PCDATA here.
+ // We hit a '<...', with only whitespace, so don't bother storing anything.
+ if((mark == s || !OPTSET(parse_ws_pcdata)) && chartype_enter(*s))
+ {
+ if(chartype_close(*(s+1))) // 'type != node_document;
+
+ if (preserve)
+ {
+ PUSHNODE(node_pcdata); // Append a new node on the tree.
+ cursor->value = s; // Save the offset.
+ }
+
+ while (*s && !chartype_enter(*s)) ++s; // '...<'
+
+ if (preserve)
+ {
+ if (*s) ENDSEG(); // Save char in 'ch', terminate & step over.
+
+ strconv_pcdata(&cursor->value);
+
+ POPNODE(); // Pop since this is a standalone.
+ }
+
+ if (!*s) return s;
+
+ if(chartype_enter(ch)) // Did we hit a '<...'?
+ {
+ if(chartype_close(*s)) goto LOC_CLOSE;
+ else if(chartype_special(*s)) goto LOC_CLASSIFY; // We hit a ''
+ {
+ POPNODE(); // Pop.
+ goto LOC_LEAVE;
+ }
+ }
+ }
+ // Fall-through B.
+ else if(chartype_close(*s)) // '.../'
+ {
+ LOC_CLOSE:
+ ++s;
+
+ if (OPTSET(parse_match_end_tags))
+ {
+ while (cursor && cursor->type != node_document)
+ {
+ char* tagname = s;
+
+ // is it the matching tag?
+ char* name = cursor->name;
+
+ if (name)
+ {
+ while (*tagname && chartype_symbol(*tagname))
+ {
+ if (*tagname++ != *name++) goto TAG_NEXTMATCH;
+ }
+
+ if (!*name)
+ {
+ // we've found matching tag
+ POPNODE();
+ s = tagname;
+ break;
+ }
+ }
+
+ TAG_NEXTMATCH:
+ POPNODE();
+ }
+ }
+ else if (OPTSET(parse_check_end_tags))
+ {
+ char* name = cursor->name;
+ if (!name) return s;
+
+ while (*s && chartype_symbol(*s))
+ {
+ if (*s++ != *name++) return s;
+ }
+ if (*name) return s;
+
+ POPNODE(); // Pop.
+ }
+ else
+ {
+ SCANFOR(chartype_leave(*s)); // '...>'
+ POPNODE(); // Pop.
+ }
+
+ SKIPWS();
+
+ goto LOC_LEAVE;
+ }
+ }
+ }
+ return s;
+ }
+ };
+
+ // Compare lhs with [rhs_begin, rhs_end)
+ int strcmprange(const char* lhs, const char* rhs_begin, const char* rhs_end)
+ {
+ while (*lhs && rhs_begin != rhs_end && *lhs == *rhs_begin)
+ {
+ ++lhs;
+ ++rhs_begin;
+ }
+
+ if (rhs_begin == rhs_end && *lhs == 0) return 0;
+ else return 1;
+ }
+
+ // Character set pattern match.
+ int strcmpwild_cset(const char** src, const char** dst)
+ {
+ int find = 0, excl = 0, star = 0;
+
+ if (**src == '!')
+ {
+ excl = 1;
+ ++(*src);
+ }
+
+ while (**src != ']' || star == 1)
+ {
+ if (find == 0)
+ {
+ if (**src == '-' && *(*src-1) < *(*src+1) && *(*src+1) != ']' && star == 0)
+ {
+ if (**dst >= *(*src-1) && **dst <= *(*src+1))
+ {
+ find = 1;
+ ++(*src);
+ }
+ }
+ else if (**src == **dst) find = 1;
+ }
+ ++(*src);
+ star = 0;
+ }
+
+ if (excl == 1) find = (1 - find);
+ if (find == 1) ++(*dst);
+
+ return find;
+ }
+}
+
+namespace pugi
+{
+ namespace impl
+ {
+ int strcmpwild(const char* src, const char* dst);
+ }
+}
+
+namespace
+{
+ using namespace pugi;
+
+ // Wildcard pattern match.
+ int strcmpwild_astr(const char** src, const char** dst)
+ {
+ int find = 1;
+ ++(*src);
+ while ((**dst != 0 && **src == '?') || **src == '*')
+ {
+ if(**src == '?') ++(*dst);
+ ++(*src);
+ }
+ while (**src == '*') ++(*src);
+ if (**dst == 0 && **src != 0) return 0;
+ if (**dst == 0 && **src == 0) return 1;
+ else
+ {
+ if (impl::strcmpwild(*src,*dst))
+ {
+ do
+ {
+ ++(*dst);
+ while(**src != **dst && **src != '[' && **dst != 0)
+ ++(*dst);
+ }
+ while ((**dst != 0) ? impl::strcmpwild(*src,*dst) : 0 != (find=0));
+ }
+ if (**dst == 0 && **src == 0) find = 1;
+ return find;
+ }
+ }
+}
+
+namespace pugi
+{
+ namespace impl
+ {
+ // Compare two strings, with globbing, and character sets.
+ int strcmpwild(const char* src, const char* dst)
+ {
+ int find = 1;
+ for(; *src != 0 && find == 1 && *dst != 0; ++src)
+ {
+ switch (*src)
+ {
+ case '?': ++dst; break;
+ case '[': ++src; find = strcmpwild_cset(&src,&dst); break;
+ case '*': find = strcmpwild_astr(&src,&dst); --src; break;
+ default : find = (int) (*src == *dst); ++dst;
+ }
+ }
+ while (*src == '*' && find == 1) ++src;
+ return (find == 1 && *dst == 0 && *src == 0) ? 0 : 1;
+ }
+ }
+
+ extern "C"
+ {
+ int strcmpwildimpl(const char* src, const char* dst)
+ {
+ return impl::strcmpwild(src, dst);
+ }
+
+ typedef int (*strcmpfunc)(const char*, const char*);
+ }
+
+ xml_attribute_struct::xml_attribute_struct(): name(0), value(0), prev_attribute(0), next_attribute(0)
+ {
+ }
+
+ xml_node_struct::xml_node_struct(xml_node_type type): name(0), value(0), prev_sibling(0), next_sibling(0), first_child(0), last_child(0), first_attribute(0), last_attribute(0), type(type)
+ {
+ }
+
+ xml_tree_walker::xml_tree_walker() : _deep(0)
+ {
+ }
+
+ xml_tree_walker::~xml_tree_walker()
+ {
+ }
+
+ void xml_tree_walker::push()
+ {
+ ++_deep;
+ }
+
+ void xml_tree_walker::pop()
+ {
+ --_deep;
+ }
+
+ int xml_tree_walker::depth() const
+ {
+ return (_deep > 0) ? _deep : 0;
+ }
+
+ bool xml_tree_walker::begin(const xml_node&)
+ {
+ return true;
+ }
+
+ bool xml_tree_walker::end(const xml_node&)
+ {
+ return true;
+ }
+
+ xml_attribute::xml_attribute(): _attr(NULL)
+ {
+ }
+
+ xml_attribute::xml_attribute(const xml_attribute_struct* attr): _attr(attr)
+ {
+ }
+
+ xml_attribute::operator xml_attribute::unspecified_bool_type() const
+ {
+ return empty() ? 0 : &xml_attribute::_attr;
+ }
+
+ bool xml_attribute::operator==(const xml_attribute& r) const
+ {
+ return (_attr == r._attr);
+ }
+
+ bool xml_attribute::operator!=(const xml_attribute& r) const
+ {
+ return (_attr != r._attr);
+ }
+
+ bool xml_attribute::operator<(const xml_attribute& r) const
+ {
+ return (_attr < r._attr);
+ }
+
+ bool xml_attribute::operator>(const xml_attribute& r) const
+ {
+ return (_attr > r._attr);
+ }
+
+ bool xml_attribute::operator<=(const xml_attribute& r) const
+ {
+ return (_attr <= r._attr);
+ }
+
+ bool xml_attribute::operator>=(const xml_attribute& r) const
+ {
+ return (_attr >= r._attr);
+ }
+
+ xml_attribute xml_attribute::next_attribute() const
+ {
+ return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
+ }
+
+ xml_attribute xml_attribute::previous_attribute() const
+ {
+ return _attr ? xml_attribute(_attr->prev_attribute) : xml_attribute();
+ }
+
+ int xml_attribute::as_int() const
+ {
+ if(empty() || !_attr->value) return 0;
+ return atoi(_attr->value);
+ }
+
+ double xml_attribute::as_double() const
+ {
+ if(empty() || !_attr->value) return 0.0;
+ return atof(_attr->value);
+ }
+
+ float xml_attribute::as_float() const
+ {
+ if(empty() || !_attr->value) return 0.0f;
+ return (float)atof(_attr->value);
+ }
+
+ bool xml_attribute::as_bool() const
+ {
+ if(empty() || !_attr->value) return false;
+ if(*(_attr->value))
+ {
+ return // Only look at first char:
+ (
+ *(_attr->value) == '1' || // 1*
+ *(_attr->value) == 't' || // t* (true)
+ *(_attr->value) == 'T' || // T* (true|true)
+ *(_attr->value) == 'y' || // y* (yes)
+ *(_attr->value) == 'Y' // Y* (Yes|YES)
+ )
+ ? true : false; // Return true if matches above, else false.
+ }
+ else return false;
+ }
+
+ bool xml_attribute::empty() const
+ {
+ return (_attr == NULL);
+ }
+
+ const char* xml_attribute::name() const
+ {
+ return (!empty() && _attr->name) ? _attr->name : "";
+ }
+
+ const char* xml_attribute::value() const
+ {
+ return (!empty() && _attr->value) ? _attr->value : "";
+ }
+
+ xml_node::xml_node(): _root(0)
+ {
+ }
+
+ xml_node::xml_node(const xml_node_struct* p): _root(p)
+ {
+ }
+
+ xml_node::operator xml_node::unspecified_bool_type() const
+ {
+ return empty() ? 0 : &xml_node::_root;
+ }
+
+ xml_node::iterator xml_node::begin() const
+ {
+ return iterator(_root->first_child);
+ }
+
+ xml_node::iterator xml_node::end() const
+ {
+ return iterator(0, _root->last_child);
+ }
+
+ xml_node::iterator xml_node::children_begin() const
+ {
+ return iterator(_root->first_child);
+ }
+
+ xml_node::iterator xml_node::children_end() const
+ {
+ return iterator(0, _root->last_child);
+ }
+
+ xml_node::attribute_iterator xml_node::attributes_begin() const
+ {
+ return attribute_iterator(_root->first_attribute);
+ }
+
+ xml_node::attribute_iterator xml_node::attributes_end() const
+ {
+ return attribute_iterator(0, _root->last_attribute);
+ }
+
+ xml_node::iterator xml_node::siblings_begin() const
+ {
+ return parent().children_begin();
+ }
+
+ xml_node::iterator xml_node::siblings_end() const
+ {
+ return parent().children_end();
+ }
+
+ bool xml_node::operator==(const xml_node& r) const
+ {
+ return (_root == r._root);
+ }
+
+ bool xml_node::operator!=(const xml_node& r) const
+ {
+ return (_root != r._root);
+ }
+
+ bool xml_node::operator<(const xml_node& r) const
+ {
+ return (_root < r._root);
+ }
+
+ bool xml_node::operator>(const xml_node& r) const
+ {
+ return (_root > r._root);
+ }
+
+ bool xml_node::operator<=(const xml_node& r) const
+ {
+ return (_root <= r._root);
+ }
+
+ bool xml_node::operator>=(const xml_node& r) const
+ {
+ return (_root >= r._root);
+ }
+
+ bool xml_node::empty() const
+ {
+ return (_root == 0 || _root->type == node_null);
+ }
+
+ bool xml_node::type_document() const
+ {
+ return (_root && _root == _root->parent && _root->type == node_document);
+ }
+
+ const char* xml_node::name() const
+ {
+ return (!empty() && _root->name) ? _root->name : "";
+ }
+
+ xml_node_type xml_node::type() const
+ {
+ return (_root) ? (xml_node_type)_root->type : node_null;
+ }
+
+ const char* xml_node::value() const
+ {
+ return (!empty() && _root->value) ? _root->value : "";
+ }
+
+ xml_node xml_node::child(const char* name) const
+ {
+ if (!empty())
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if (i->name && !strcmp(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_node xml_node::child_w(const char* name) const
+ {
+ if (!empty())
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_attribute xml_node::attribute(const char* name) const
+ {
+ if (!_root) return xml_attribute();
+
+ for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
+ if (i->name && !strcmp(name, i->name))
+ return xml_attribute(i);
+
+ return xml_attribute();
+ }
+
+ xml_attribute xml_node::attribute_w(const char* name) const
+ {
+ if (!_root) return xml_attribute();
+
+ for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
+ if (i->name && !impl::strcmpwild(name, i->name))
+ return xml_attribute(i);
+
+ return xml_attribute();
+ }
+
+ xml_node xml_node::sibling(const char* name) const
+ {
+ if (!empty() && !type_document()) return parent().child(name);
+ else return xml_node();
+ }
+
+ xml_node xml_node::sibling_w(const char* name) const
+ {
+ if (!empty() && !type_document()) return parent().child_w(name);
+ else return xml_node();
+ }
+
+ xml_node xml_node::next_sibling(const char* name) const
+ {
+ if(empty()) return xml_node();
+
+ for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
+ if (i->name && !strcmp(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_node xml_node::next_sibling_w(const char* name) const
+ {
+ if(empty()) return xml_node();
+
+ for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
+ if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_node xml_node::next_sibling() const
+ {
+ if(empty()) return xml_node();
+
+ if (_root->next_sibling) return xml_node(_root->next_sibling);
+ else return xml_node();
+ }
+
+ xml_node xml_node::previous_sibling(const char* name) const
+ {
+ if (empty()) return xml_node();
+
+ for (xml_node_struct* i = _root->prev_sibling; i; i = i->prev_sibling)
+ if (i->name && !strcmp(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_node xml_node::previous_sibling_w(const char* name) const
+ {
+ if (empty()) return xml_node();
+
+ for (xml_node_struct* i = _root->prev_sibling; i; i = i->prev_sibling)
+ if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ xml_node xml_node::previous_sibling() const
+ {
+ if(empty()) return xml_node();
+
+ if (_root->prev_sibling) return xml_node(_root->prev_sibling);
+ else return xml_node();
+ }
+
+ xml_node xml_node::parent() const
+ {
+ return (!type_document()) ? xml_node(_root->parent) : xml_node();
+ }
+
+ const char* xml_node::child_value() const
+ {
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if ((i->type == node_pcdata || i->type == node_cdata) && i->value)
+ return i->value;
+ return "";
+ }
+
+ xml_attribute xml_node::first_attribute() const
+ {
+ return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
+ }
+
+ xml_attribute xml_node::last_attribute() const
+ {
+ return _root ? xml_attribute(_root->last_attribute) : xml_attribute();
+ }
+
+ xml_node xml_node::first_child() const
+ {
+ if (_root) return xml_node(_root->first_child);
+ else return xml_node();
+ }
+
+ xml_node xml_node::last_child() const
+ {
+ if (_root) return xml_node(_root->last_child);
+ else return xml_node();
+ }
+
+ namespace impl
+ {
+ xml_node first_element(const xml_node_struct* node, const char* name, strcmpfunc pred)
+ {
+ for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
+ {
+ if (i->name && !pred(name, i->name)) return xml_node(i);
+ else if (i->first_child)
+ {
+ xml_node found = first_element(i, name, pred);
+ if (found) return found; // Found.
+ }
+ }
+ return xml_node(); // Not found.
+ }
+ }
+
+ xml_node xml_node::first_element(const char* name) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element(_root, name, &strcmp);
+ }
+
+ xml_node xml_node::first_element_w(const char* name) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element(_root, name, &strcmpwildimpl);
+ }
+
+ namespace impl
+ {
+ xml_node first_element_by_value(const xml_node_struct* node, const char* name, const char* value, strcmpfunc pred)
+ {
+ for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
+ {
+ if (i->name && !pred(name,i->name))
+ {
+ for (xml_node_struct* j = i->first_child; j; j = j->next_sibling)
+ if (j->type == node_pcdata && j->value && !pred(value, j->value))
+ return xml_node(i);
+ }
+ else if (i->first_child)
+ {
+ xml_node found = first_element_by_value(i, name, value, pred);
+ if(!found.empty()) return found; // Found.
+ }
+ }
+ return xml_node(); // Not found.
+ }
+ }
+
+ xml_node xml_node::first_element_by_value(const char* name,const char* value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_value(_root, name, value, &strcmp);
+ }
+
+ xml_node xml_node::first_element_by_value_w(const char* name,const char* value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_value(_root, name, value, &strcmpwildimpl);
+ }
+
+ namespace impl
+ {
+ xml_node first_element_by_attribute(const xml_node_struct* node, const char* name, const char* attr_name, const char* attr_value, strcmpfunc pred)
+ {
+ for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
+ {
+ if (i->name && !pred(name, i->name))
+ {
+ for (xml_attribute_struct* j = i->first_attribute; j; j = j->next_attribute)
+ {
+ if (j->name && j->value && !pred(attr_name, j->name) && !pred(attr_value, j->value))
+ return xml_node(i); // Wrap it up and return.
+ }
+ }
+ else if (i->first_child)
+ {
+ xml_node found = first_element_by_attribute(i, name, attr_name, attr_value, pred);
+ if(!found.empty()) return found; // Found.
+ }
+ }
+ return xml_node(); // Not found.
+ }
+ }
+
+ xml_node xml_node::first_element_by_attribute(const char* name,const char* attr_name,const char* attr_value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_attribute(_root, name, attr_name, attr_value, &strcmp);
+ }
+
+ xml_node xml_node::first_element_by_attribute_w(const char* name,const char* attr_name,const char* attr_value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_attribute(_root, name, attr_name, attr_value, &strcmpwildimpl);
+ }
+
+ namespace impl
+ {
+ xml_node first_element_by_attribute(const xml_node_struct* node, const char* attr_name,const char* attr_value, strcmpfunc pred)
+ {
+ for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
+ {
+ for (xml_attribute_struct* j = i->first_attribute; j; j = j->next_attribute)
+ {
+ if (j->name && j->value && !pred(attr_name, j->name) && !pred(attr_value, j->value))
+ return xml_node(i); // Wrap it up and return.
+ }
+
+ if (i->first_child)
+ {
+ xml_node found = first_element_by_attribute(i->first_child, attr_name, attr_value, pred);
+ if (!found.empty()) return found; // Found.
+ }
+ }
+ return xml_node(); // Not found.
+ }
+ }
+
+ xml_node xml_node::first_element_by_attribute(const char* attr_name,const char* attr_value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_attribute(_root, attr_name, attr_value, &strcmp);
+ }
+
+ xml_node xml_node::first_element_by_attribute_w(const char* attr_name,const char* attr_value) const
+ {
+ if (empty()) return xml_node();
+
+ return impl::first_element_by_attribute(_root, attr_name, attr_value, &strcmpwildimpl);
+ }
+
+ xml_node xml_node::first_node(xml_node_type type) const
+ {
+ if(!_root) return xml_node();
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ {
+ if (i->type == type) return xml_node(i);
+ else if (i->first_child)
+ {
+ xml_node subsearch(i);
+ xml_node found = subsearch.first_node(type);
+ if(!found.empty()) return found; // Found.
+ }
+ }
+ return xml_node(); // Not found.
+ }
+
+ std::string xml_node::path(char delimiter) const
+ {
+ std::string path;
+
+ xml_node cursor = *this; // Make a copy.
+
+ path = cursor.name();
+
+ while (cursor.parent() && !cursor.type_document()) // Loop to parent (stopping on actual root because it has no name).
+ {
+ cursor = cursor.parent();
+
+ std::string temp = cursor.name();
+ temp += delimiter;
+ temp += path;
+ path.swap(temp);
+ }
+
+ return path;
+ }
+
+ xml_node xml_node::first_element_by_path(const char* path, char delimiter) const
+ {
+ xml_node found = *this; // Current search context.
+
+ if (empty() || !path || !path[0]) return found;
+
+ if (path[0] == delimiter)
+ {
+ // Absolute path; e.g. '/foo/bar'
+ while (found.parent()) found = found.parent();
+ ++path;
+ }
+
+ const char* path_segment = path;
+
+ while (*path_segment == delimiter) ++path_segment;
+
+ const char* path_segment_end = path_segment;
+
+ while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;
+
+ if (path_segment == path_segment_end) return found;
+
+ const char* next_segment = path_segment_end;
+
+ while (*next_segment == delimiter) ++next_segment;
+
+ if (*path_segment == '.' && path_segment + 1 == path_segment_end)
+ return found.first_element_by_path(next_segment, delimiter);
+ else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)
+ return found.parent().first_element_by_path(next_segment, delimiter);
+ else
+ {
+ for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)
+ {
+ if (j->name && !strcmprange(j->name, path_segment, path_segment_end))
+ {
+ xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
+
+ if (subsearch) return subsearch;
+ }
+ }
+
+ return xml_node();
+ }
+ }
+
+ bool xml_node::traverse(xml_tree_walker& walker) const
+ {
+ if (!walker.begin(*this)) return false; // Send the callback for begin traverse if depth is zero.
+ if(!empty()) // Don't traverse if this is a null node.
+ {
+ walker.push(); // Increment the walker depth counter.
+
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ {
+ xml_node subsearch(i);
+ if (!subsearch.traverse(walker)) return false;
+ }
+ walker.pop(); // Decrement the walker depth counter.
+ }
+ if (!walker.end(*this)) return false; // Send the callback for end traverse if depth is zero.
+
+ return true;
+ }
+
+ xml_node_iterator::xml_node_iterator()
+ {
+ }
+
+ xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node)
+ {
+ }
+
+ xml_node_iterator::xml_node_iterator(const xml_node_struct* ref): _wrap(ref)
+ {
+ }
+
+ xml_node_iterator::xml_node_iterator(const xml_node_struct* ref, const xml_node_struct* prev): _wrap(ref), _prev(prev)
+ {
+ }
+
+ bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
+ {
+ return (_wrap == rhs._wrap);
+ }
+
+ bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
+ {
+ return (_wrap != rhs._wrap);
+ }
+
+ const xml_node& xml_node_iterator::operator*() const
+ {
+ return _wrap;
+ }
+
+ const xml_node* xml_node_iterator::operator->() const
+ {
+ return &_wrap;
+ }
+
+ const xml_node_iterator& xml_node_iterator::operator++()
+ {
+ _prev = _wrap;
+ _wrap = xml_node(_wrap._root->next_sibling);
+ return *this;
+ }
+
+ xml_node_iterator xml_node_iterator::operator++(int)
+ {
+ xml_node_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ const xml_node_iterator& xml_node_iterator::operator--()
+ {
+ if (_wrap._root) _wrap = xml_node(_wrap._root->prev_sibling);
+ else _wrap = _prev;
+ return *this;
+ }
+
+ xml_node_iterator xml_node_iterator::operator--(int)
+ {
+ xml_node_iterator temp = *this;
+ --*this;
+ return temp;
+ }
+
+ xml_attribute_iterator::xml_attribute_iterator()
+ {
+ }
+
+ xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr): _wrap(attr)
+ {
+ }
+
+ xml_attribute_iterator::xml_attribute_iterator(const xml_attribute_struct* ref): _wrap(ref)
+ {
+ }
+
+ xml_attribute_iterator::xml_attribute_iterator(const xml_attribute_struct* ref, const xml_attribute_struct* prev): _wrap(ref), _prev(prev)
+ {
+ }
+
+ bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
+ {
+ return (_wrap == rhs._wrap);
+ }
+
+ bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
+ {
+ return (_wrap != rhs._wrap);
+ }
+
+ const xml_attribute& xml_attribute_iterator::operator*() const
+ {
+ return _wrap;
+ }
+
+ const xml_attribute* xml_attribute_iterator::operator->() const
+ {
+ return &_wrap;
+ }
+
+ const xml_attribute_iterator& xml_attribute_iterator::operator++()
+ {
+ _prev = _wrap;
+ _wrap = xml_attribute(_wrap._attr->next_attribute);
+ return *this;
+ }
+
+ xml_attribute_iterator xml_attribute_iterator::operator++(int)
+ {
+ xml_attribute_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ const xml_attribute_iterator& xml_attribute_iterator::operator--()
+ {
+ if (_wrap._attr) _wrap = xml_attribute(_wrap._attr->prev_attribute);
+ else _wrap = _prev;
+ return *this;
+ }
+
+ xml_attribute_iterator xml_attribute_iterator::operator--(int)
+ {
+ xml_attribute_iterator temp = *this;
+ --*this;
+ return temp;
+ }
+
+ xml_memory_block::xml_memory_block(): next(0), size(0)
+ {
+ }
+
+ xml_parser::xml_parser(unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
+ {
+ }
+
+ xml_parser::xml_parser(std::istream& stream,unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
+ {
+ parse(stream, _optmsk);
+ }
+
+ xml_parser::xml_parser(char* xmlstr,unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
+ {
+ parse(xmlstr, _optmsk);
+ }
+
+ xml_parser::~xml_parser()
+ {
+ xml_memory_block* current = _memory.next;
+
+ while (current)
+ {
+ xml_memory_block* next = current->next;
+ delete current;
+ current = next;
+ }
+ }
+
+ xml_parser::operator xml_node() const
+ {
+ return xml_node(_xmldoc);
+ }
+
+ xml_node xml_parser::document() const
+ {
+ return xml_node(_xmldoc);
+ }
+
+ unsigned int xml_parser::options() const
+ {
+ return _optmsk;
+ }
+
+ unsigned int xml_parser::options(unsigned int optmsk)
+ {
+ unsigned int prev = _optmsk;
+ _optmsk = optmsk;
+ return prev;
+ }
+
+ void xml_parser::parse(std::istream& stream,unsigned int optmsk)
+ {
+ int length = 0, pos = stream.tellg();
+ stream.seekg(0, std::ios_base::end);
+ length = stream.tellg();
+ stream.seekg(pos, std::ios_base::beg);
+
+ _buffer.resize(length + 1);
+ stream.read(&_buffer[0], length);
+ _buffer[length] = 0;
+
+ parse(&_buffer[0],optmsk); // Parse the input string.
+ }
+
+ char* xml_parser::parse(char* xmlstr,unsigned int optmsk)
+ {
+ if(!xmlstr) return NULL;
+
+ xml_allocator alloc(&_memory);
+
+ _xmldoc = alloc.allocate(node_document); // Allocate a new root.
+ _xmldoc->parent = _xmldoc; // Point to self.
+ if(optmsk != parse_noset) _optmsk = optmsk;
+
+ xml_parser_impl parser(alloc);
+
+ return parser.parse(xmlstr,_xmldoc,_optmsk); // Parse the input string.
+ }
+
+ std::string utf8(const wchar_t* str)
+ {
+ std::string result;
+ result.reserve(strutf16_utf8_size(str));
+
+ for (; *str; ++str)
+ {
+ char buffer[6];
+
+ result.append(buffer, strutf16_utf8(buffer, *str));
+ }
+
+ return result;
+ }
+
+ std::wstring utf16(const char* str)
+ {
+ std::wstring result;
+ result.reserve(strutf8_utf16_size(str));
+
+ for (; *str;)
+ {
+ unsigned int ch;
+ str = strutf8_utf16(str, ch);
+ result += (wchar_t)ch;
+ }
+
+ return result;
+ }
+}
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
new file mode 100644
index 0000000..026e6ba
--- /dev/null
+++ b/src/pugixml.hpp
@@ -0,0 +1,619 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Pug Improved XML Parser - Version 0.1
+// --------------------------------------------------------
+// Copyright (C) 2006, by Zeux (zeux@mathcentre.com)
+// This work is based on the pugxml parser, which is:
+// Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+// Released into the Public Domain. Use at your own risk.
+// See pugxml.xml for further information, history, etc.
+// Contributions by Neville Franks (readonly@getsoft.com).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef HEADER_PUGIXML_HPP
+#define HEADER_PUGIXML_HPP
+
+/// The PugiXML Parser namespace.
+namespace pugi
+{
+ /// Tree node classification.
+ /// See 'xml_node_struct::type'
+ enum xml_node_type
+ {
+ node_null, ///< An undifferentiated entity.
+ node_document, ///< A document tree's absolute root.
+ node_element, ///< E.g. '<...>'
+ node_pcdata, ///< E.g. '<...>'
+ node_cdata, ///< E.g. ''
+ node_comment, ///< E.g. ''
+ node_pi ///< E.g. '...?>'
+ };
+
+ /// Parser Options
+ const size_t memory_block_size = 32768; ///< Memory block size, 32 kb
+
+ const unsigned int parse_minimal = 0x00000000; ///< Unset the following flags.
+ const unsigned int parse_pi = 0x00000001; ///< Parse '...?>'
+ const unsigned int parse_comments = 0x00000002; ///< Parse ''
+ const unsigned int parse_cdata = 0x00000004; ///< Parse ''
+ const unsigned int parse_ws_pcdata = 0x00000008; ///< Skip PCDATA that consists only of whitespaces
+ const unsigned int parse_ext_pcdata = 0x00000010; ///< Skip PCDATA that is outside all tags (i.e. root)
+ const unsigned int parse_trim_pcdata = 0x00000020; ///< Trim '>...<'
+ const unsigned int parse_trim_attribute = 0x00000040; ///< Trim 'foo="..."'.
+ const unsigned int parse_escapes_pcdata = 0x00000080; ///< Parse <, >, &, ", ', .. sequences
+ const unsigned int parse_escapes_attribute = 0x00000100; ///< Parse <, >, &, ", ', .. sequences
+ const unsigned int parse_wnorm_pcdata = 0x00000200; ///< Normalize spaces in pcdata
+ const unsigned int parse_wnorm_attribute = 0x00000400; ///< Normalize spaces in attributes
+ const unsigned int parse_wconv_attribute = 0x00000800; ///< Convert space-like characters to spaces in attributes (only if wnorm is not set)
+ const unsigned int parse_eol_cdata = 0x00001000; ///< Perform EOL handling in CDATA sections
+ const unsigned int parse_check_end_tags = 0x00002000; ///< Check start and end tag names and return error if names mismatch
+ const unsigned int parse_match_end_tags = 0x00004000; ///< Try to find corresponding start tag for an end tag
+ const unsigned int parse_default = 0x0000FFFF & ~parse_ws_pcdata; ///< Set all flags, except parse_ws_pcdata
+ const unsigned int parse_noset = 0x80000000; ///< Parse with flags in xml_parser
+
+ const unsigned int parse_w3c = parse_pi | parse_comments | parse_cdata |
+ parse_escapes_pcdata | parse_escapes_attribute |
+ parse_wconv_attribute | parse_check_end_tags |
+ parse_ws_pcdata | parse_eol_cdata;
+
+ /// Forward declarations
+ struct xml_attribute_struct;
+ struct xml_node_struct;
+
+ class xml_node_iterator;
+ class xml_attribute_iterator;
+
+ class xml_tree_walker;
+
+ /// Provides a light-weight wrapper for manipulating xml_attribute_struct structures.
+ /// Note: xml_attribute does not create any memory for the attribute it wraps;
+ /// it only wraps a pointer to an existing xml_attribute_struct.
+ class xml_attribute
+ {
+ friend class xml_attribute_iterator;
+ friend class xml_node;
+
+ private:
+ const xml_attribute_struct* _attr; ///< The internal attribute pointer.
+
+ /// Safe bool type
+ typedef const xml_attribute_struct* xml_attribute::*unspecified_bool_type;
+
+ /// Initializing ctor
+ explicit xml_attribute(const xml_attribute_struct* attr);
+
+ public:
+ /// Default ctor
+ xml_attribute();
+
+ public:
+ /// Comparison operators
+ bool operator==(const xml_attribute& r) const;
+ bool operator!=(const xml_attribute& r) const;
+ bool operator<(const xml_attribute& r) const;
+ bool operator>(const xml_attribute& r) const;
+ bool operator<=(const xml_attribute& r) const;
+ bool operator>=(const xml_attribute& r) const;
+
+ /// Safe bool conversion
+ operator unspecified_bool_type() const;
+
+ /// Get next attribute if any, else xml_attribute()
+ xml_attribute next_attribute() const;
+
+ /// Get previous attribute if any, else xml_attribute()
+ xml_attribute previous_attribute() const;
+
+ /// Cast attribute value as int. If not found, return 0.
+ /// \return Attribute value as int, or 0.
+ int as_int() const;
+
+ /// Cast attribute value as double. If not found, return 0.0.
+ /// \return Attribute value as double, or 0.0.
+ double as_double() const;
+
+ /// Cast attribute value as float. If not found, return 0.0.
+ /// \return Attribute value as float, or 0.0.
+ float as_float() const;
+
+ /// Cast attribute value as bool. If not found, return false.
+ /// \return Attribute value as bool, or false.
+ bool as_bool() const;
+
+ public:
+ /// True if internal pointer is valid
+ bool empty() const;
+
+ public:
+ /// Access the attribute name.
+ const char* name() const;
+
+ /// Access the attribute value.
+ const char* value() const;
+ };
+
+ /// Provides a light-weight wrapper for manipulating xml_node_struct structures.
+ class xml_node
+ {
+ friend class xml_node_iterator;
+ friend class xml_parser;
+
+ private:
+ const xml_node_struct* _root; ///< Pointer to node root.
+
+ /// Safe bool type
+ typedef const xml_node_struct* xml_node::*unspecified_bool_type;
+
+ private:
+ /// Node is tree root.
+ bool type_document() const;
+
+ public:
+ /// Default constructor.
+ /// Node root points to a dummy 'xml_node_struct' structure. Test for this
+ /// with 'empty'.
+ xml_node();
+
+ /// Construct, wrapping the given 'xml_node_struct' pointer.
+ explicit xml_node(const xml_node_struct* p);
+
+ public:
+ /// Base iterator type (for child nodes). Same as 'child_iterator'.
+ typedef xml_node_iterator iterator;
+
+ /// Attribute iterator type.
+ typedef xml_attribute_iterator attribute_iterator;
+
+ /// Access the begin iterator for this node's collection of child nodes.
+ /// Same as 'children_begin'.
+ iterator begin() const;
+
+ /// Access the end iterator for this node's collection of child nodes.
+ /// Same as 'children_end'.
+ iterator end() const;
+
+ /// Access the begin iterator for this node's collection of child nodes.
+ /// Same as 'begin'.
+ iterator children_begin() const;
+
+ /// Access the end iterator for this node's collection of child nodes.
+ /// Same as 'end'.
+ iterator children_end() const;
+
+ /// Access the begin iterator for this node's collection of attributes.
+ attribute_iterator attributes_begin() const;
+
+ /// Access the end iterator for this node's collection of attributes.
+ attribute_iterator attributes_end() const;
+
+ /// Access the begin iterator for this node's collection of siblings.
+ iterator siblings_begin() const;
+
+ /// Access the end iterator for this node's collection of siblings.
+ iterator siblings_end() const;
+
+ public:
+ /// Safe bool conversion
+ operator unspecified_bool_type() const;
+
+ /// Comparison operators
+ bool operator==(const xml_node& r) const;
+ bool operator!=(const xml_node& r) const;
+ bool operator<(const xml_node& r) const;
+ bool operator>(const xml_node& r) const;
+ bool operator<=(const xml_node& r) const;
+ bool operator>=(const xml_node& r) const;
+
+ public:
+ /// Node pointer is null, or type is node_null. Same as type_null.
+ bool empty() const;
+
+ public:
+ /// Access node entity type.
+ xml_node_type type() const;
+
+ /// Access pointer to node name if any, else empty string.
+ const char* name() const;
+
+ /// Access pointer to data if any, else empty string.
+ const char* value() const;
+
+ /// Access child node at name as xml_node or xml_node(NULL) if bad name.
+ xml_node child(const char* name) const;
+
+ /// Access child node at name as xml_node or xml_node(NULL) if bad name.
+ /// Enable wildcard matching.
+ xml_node child_w(const char* name) const;
+
+ /// Access the attribute having 'name'.
+ xml_attribute attribute(const char* name) const;
+
+ /// Access the attribute having 'name'.
+ /// Enable wildcard matching.
+ xml_attribute attribute_w(const char* name) const;
+
+ /// Access sibling node at name as xml_node or xml_node(NULL) if bad name.
+ xml_node sibling(const char* name) const;
+
+ /// Access sibling node at name as xml_node or xml_node(NULL) if bad name.
+ /// Enable wildcard matching.
+ xml_node sibling_w(const char* name) const;
+
+ /// Access current node's next sibling by position and name.
+ xml_node next_sibling(const char* name) const;
+
+ /// Access current node's next sibling by position and name.
+ /// Enable wildcard matching.
+ xml_node next_sibling_w(const char* name) const;
+
+ /// Access current node's next sibling by position.
+ xml_node next_sibling() const;
+
+ /// Access current node's previous sibling by position and name.
+ xml_node previous_sibling(const char* name) const;
+
+ /// Access current node's previous sibling by position and name.
+ /// Enable wildcard matching.
+ xml_node previous_sibling_w(const char* name) const;
+
+ /// Access current node's previous sibling by position.
+ xml_node previous_sibling() const;
+
+ /// Access node's parent if any, else xml_node(NULL)
+ xml_node parent() const;
+
+ /// Return PCDATA/CDATA that is child of current node. If none, return empty string.
+ const char* child_value() const;
+
+ public:
+ /// Access node's first attribute if any, else xml_attribute()
+ xml_attribute first_attribute() const;
+
+ /// Access node's last attribute if any, else xml_attribute()
+ xml_attribute last_attribute() const;
+
+ /// Find all elements having the given name.
+ template void all_elements_by_name(const char* name, OutputIterator it) const;
+
+ /// Find all elements having the given name.
+ /// Enable wildcard matching.
+ template void all_elements_by_name_w(const char* name, OutputIterator it) const;
+
+ /// Access node's first child if any, else xml_node()
+ xml_node first_child() const;
+
+ /// Access node's last child if any, else xml_node()
+ xml_node last_child() const;
+
+ /// Find attribute using the predicate
+ /// Predicate should take xml_attribute and return bool.
+ template xml_attribute find_attribute(Predicate pred) const;
+
+ /// Find child using the predicate
+ /// Predicate should take xml_node and return bool.
+ template xml_node find_child(Predicate pred) const;
+
+ /// Recursively-implemented depth-first find element using the predicate
+ /// Predicate should take xml_node and return bool.
+ template xml_node find_element(Predicate pred) const;
+
+ /// Recursively-implemented depth-first find the first matching element.
+ /// Use for shallow drill-downs.
+ xml_node first_element(const char* name) const;
+
+ /// Recursively-implemented depth-first find the first matching element.
+ /// Use for shallow drill-downs.
+ /// Enable wildcard matching.
+ xml_node first_element_w(const char* name) const;
+
+ /// Recursively-implemented depth-first find the first matching element
+ /// also having matching PCDATA.
+ xml_node first_element_by_value(const char* name, const char* value) const;
+
+ /// Recursively-implemented depth-first find the first matching element
+ /// also having matching PCDATA.
+ /// Enable wildcard matching.
+ xml_node first_element_by_value_w(const char* name, const char* value) const;
+
+ /// Recursively-implemented depth-first find the first matching element
+ /// also having matching attribute.
+ xml_node first_element_by_attribute(const char* name, const char* attr_name, const char* attr_value) const;
+
+ /// Recursively-implemented depth-first find the first matching element
+ /// also having matching attribute.
+ /// Enable wildcard matching.
+ xml_node first_element_by_attribute_w(const char* name, const char* attr_name, const char* attr_value) const;
+
+ /// Recursively-implemented depth-first find the first element
+ /// having matching attribute.
+ xml_node first_element_by_attribute(const char* attr_name, const char* attr_value) const;
+
+ /// Recursively-implemented depth-first find the first element
+ /// having matching attribute.
+ /// Enable wildcard matching.
+ xml_node first_element_by_attribute_w(const char* attr_name, const char* attr_value) const;
+
+ /// Recursively-implemented depth-first find the first matching entity.
+ /// Use for shallow drill-downs.
+ xml_node first_node(xml_node_type type) const;
+
+ /// Compile the absolute node path from root as a text string.
+ /// \param delimiter - Delimiter character to insert between element names.
+ /// \return path string (e.g. with '/' as delimiter, '/document/.../this'.
+ std::string path(char delimiter = '/') const;
+
+ /// Search for a node by path.
+ /// \param path - Path string; e.g. './foo/bar' (relative to node), '/foo/bar' (relative
+ /// to root), '../foo/bar' (pop relative position).
+ /// \param delimiter - Delimiter character to use in tokenizing path.
+ /// \return Matching node, or xml_node() if not found.
+ xml_node first_element_by_path(const char* path, char delimiter = '/') const;
+
+ /// Recursively traverse the tree.
+ bool traverse(xml_tree_walker& walker) const;
+ };
+
+ /// Child node iterator.
+ class xml_node_iterator: public std::iterator
+ {
+ friend class xml_node;
+
+ private:
+ xml_node _prev;
+ xml_node _wrap;
+
+ /// Initializing ctor
+ explicit xml_node_iterator(const xml_node_struct* ref);
+ public:
+ /// Default ctor
+ xml_node_iterator();
+
+ /// Initializing ctor
+ xml_node_iterator(const xml_node& node);
+
+ /// Initializing ctor (for past-the-end)
+ xml_node_iterator(const xml_node_struct* ref, const xml_node_struct* prev);
+
+ bool operator==(const xml_node_iterator& rhs) const;
+ bool operator!=(const xml_node_iterator& rhs) const;
+
+ const xml_node& operator*() const;
+ const xml_node* operator->() const;
+
+ const xml_node_iterator& operator++();
+ xml_node_iterator operator++(int);
+
+ const xml_node_iterator& operator--();
+ xml_node_iterator operator--(int);
+ };
+
+ /// Attribute iterator.
+ class xml_attribute_iterator: public std::iterator
+ {
+ friend class xml_node;
+
+ private:
+ xml_attribute _prev;
+ xml_attribute _wrap;
+
+ /// Initializing ctor
+ explicit xml_attribute_iterator(const xml_attribute_struct* ref);
+ public:
+ /// Default ctor
+ xml_attribute_iterator();
+
+ /// Initializing ctor
+ xml_attribute_iterator(const xml_attribute& attr);
+
+ /// Initializing ctor (for past-the-end)
+ xml_attribute_iterator(const xml_attribute_struct* ref, const xml_attribute_struct* prev);
+
+ bool operator==(const xml_attribute_iterator& rhs) const;
+ bool operator!=(const xml_attribute_iterator& rhs) const;
+
+ const xml_attribute& operator*() const;
+ const xml_attribute* operator->() const;
+
+ const xml_attribute_iterator& operator++();
+ xml_attribute_iterator operator++(int);
+
+ const xml_attribute_iterator& operator--();
+ xml_attribute_iterator operator--(int);
+ };
+
+ /// Abstract tree walker class for xml_node::traverse().
+ class xml_tree_walker
+ {
+ private:
+ int _deep; ///< Current node depth.
+ public:
+ /// Default ctor
+ xml_tree_walker();
+
+ /// Virtual dtor
+ virtual ~xml_tree_walker();
+
+ public:
+ /// Increment node depth.
+ virtual void push();
+
+ /// Decrement node depth
+ virtual void pop();
+
+ /// Access node depth
+ virtual int depth() const;
+
+ public:
+ /// Callback when traverse on a node begins.
+ /// \return returning false will abort the traversal.
+ virtual bool begin(const xml_node&);
+
+ /// Callback when traverse on a node ends.
+ /// \return Returning false will abort the traversal.
+ virtual bool end(const xml_node&);
+ };
+
+ /// Memory block (internal)
+ struct xml_memory_block
+ {
+ xml_memory_block();
+
+ xml_memory_block* next;
+ size_t size;
+
+ char data[memory_block_size];
+ };
+
+ /// Provides a high-level interface to the XML parser.
+ class xml_parser
+ {
+ private:
+ std::vector _buffer; ///< character buffer
+
+ xml_memory_block _memory; ///< Memory block
+
+ xml_node_struct* _xmldoc; ///< Pointer to current XML document tree root.
+ unsigned int _optmsk; ///< Parser options.
+
+ xml_parser(const xml_parser&);
+ const xml_parser& operator=(const xml_parser&);
+
+ public:
+ /// Constructor.
+ /// \param optmsk - Options mask.
+ xml_parser(unsigned int optmsk = parse_default);
+
+ /// Parse constructor.
+ /// \param xmlstr - readwrite string with xml data
+ /// \param optmsk - Options mask.
+ /// \see parse
+ xml_parser(char* xmlstr, unsigned int optmsk = parse_default);
+
+ /// Parse constructor.
+ /// \param stream - stream with xml data
+ /// \param optmsk - Options mask.
+ /// \see parse
+ xml_parser(std::istream& stream, unsigned int optmsk = parse_default);
+
+ /// Dtor
+ ~xml_parser();
+
+ public:
+ /// Cast as xml_node (same as document).
+ operator xml_node() const;
+
+ /// Returns the root wrapped by an xml_node.
+ xml_node document() const;
+
+ public:
+ /// Get parser options mask.
+ unsigned int options() const;
+
+ /// Set parser options mask.
+ unsigned int options(unsigned int optmsk);
+
+ public:
+ /// Parse the given XML stream
+ /// \param stream - stream with xml data
+ /// \param optmsk - Options mask.
+ void parse(std::istream& stream, unsigned int optmsk = parse_noset);
+
+ /// Parse the given XML string in-situ.
+ /// \param xmlstr - readwrite string with xml data
+ /// \param optmsk - Options mask.
+ /// \return last position or NULL
+ /// \rem input string is zero-segmented
+ char* parse(char* xmlstr, unsigned int optmsk = parse_noset);
+ };
+
+ /// Utility functions for xml
+
+ /// Convert utf16 to utf8
+ std::string utf8(const wchar_t* str);
+
+ /// Convert utf8 to utf16
+ std::wstring utf16(const char* str);
+}
+
+/// Inline implementation
+
+namespace pugi
+{
+ namespace impl
+ {
+ int strcmpwild(const char*, const char*);
+ }
+
+ template void xml_node::all_elements_by_name(const char* name, OutputIterator it) const
+ {
+ if (empty()) return;
+
+ for (xml_node node = first_child(); node; node = node.next_sibling())
+ {
+ if (!strcmp(name, node.name()))
+ {
+ *it = node;
+ ++it;
+ }
+
+ if (node.first_child()) node.all_elements_by_name(name, it);
+ }
+ }
+
+ template void xml_node::all_elements_by_name_w(const char* name, OutputIterator it) const
+ {
+ if (empty()) return;
+
+ for (xml_node node = first_child(); node; node = node.next_sibling())
+ {
+ if (!impl::strcmpwild(name, node.name()))
+ {
+ *it = node;
+ ++it;
+ }
+
+ if (node.first_child()) node.all_elements_by_name_w(name, it);
+ }
+ }
+
+ template inline xml_attribute xml_node::find_attribute(Predicate pred) const
+ {
+ if (!empty())
+ for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
+ if (pred(attrib))
+ return attrib;
+
+ return xml_attribute();
+ }
+
+ template inline xml_node xml_node::find_child(Predicate pred) const
+ {
+ if (!empty())
+ for (xml_node node = first_child(); node; node = node.next_sibling())
+ if (pred(node))
+ return node;
+
+ return xml_node();
+ }
+
+ template inline xml_node xml_node::find_element(Predicate pred) const
+ {
+ if (!empty())
+ for (xml_node node = first_child(); node; node = node.next_sibling())
+ {
+ if (pred(node))
+ return node;
+
+ if (node.first_child())
+ {
+ xml_node found = node.find_element(pred);
+ if (found) return found;
+ }
+ }
+
+ return xml_node();
+ }
+}
+
+#endif
--
cgit v1.2.3