2017-12-18 14:09:20 +08:00
# ifndef RAPIDXML_HPP_INCLUDED
# define RAPIDXML_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation
// If standard library is disabled, user must provide implementations of required functions and typedefs
# if !defined(RAPIDXML_NO_STDLIB)
# include <cstdlib> // For std::size_t
# include <cassert> // For assert
# include <new> // For placement new
# endif
// On MSVC, disable "conditional expression is constant" warning (level 4).
// This warning is almost impossible to avoid with certain types of templated code
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4127) // Conditional expression is constant
# endif
///////////////////////////////////////////////////////////////////////////
// RAPIDXML_PARSE_ERROR
# if defined(RAPIDXML_NO_EXCEPTIONS)
# define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); }
namespace rapidxml
{
//! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS,
//! this function is called to notify user about the error.
//! It must be defined by the user.
//! <br><br>
//! This function cannot return. If it does, the results are undefined.
//! <br><br>
//! A very simple definition might look like that:
//! <pre>
//! void %rapidxml::%parse_error_handler(const char *what, void *where)
//! {
//! std::cout << "Parse error: " << what << "\n";
//! std::abort();
//! }
//! </pre>
//! \param what Human readable description of the error.
//! \param where Pointer to character data where error was detected.
void parse_error_handler ( const char * what , void * where ) ;
}
# else
# include <exception> // For std::exception
# define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where)
namespace rapidxml
{
//! Parse error exception.
//! This exception is thrown by the parser when an error occurs.
//! Use what() function to get human-readable error message.
//! Use where() function to get a pointer to position within source text where error was detected.
//! <br><br>
//! If throwing exceptions by the parser is undesirable,
//! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included.
//! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception.
//! This function must be defined by the user.
//! <br><br>
//! This class derives from <code>std::exception</code> class.
class parse_error : public std : : exception
{
public :
//! Constructs parse error
parse_error ( const char * what , void * where )
: m_what ( what )
, m_where ( where )
{
}
//! Gets human readable description of error.
//! \return Pointer to null terminated description of the error.
virtual const char * what ( ) const throw ( )
{
return m_what ;
}
//! Gets pointer to character data where error happened.
//! Ch should be the same as char type of xml_document that produced the error.
//! \return Pointer to location within the parsed string where error occured.
template < class Ch >
Ch * where ( ) const
{
return reinterpret_cast < Ch * > ( m_where ) ;
}
private :
const char * m_what ;
void * m_where ;
} ;
}
# endif
///////////////////////////////////////////////////////////////////////////
// Pool sizes
# ifndef RAPIDXML_STATIC_POOL_SIZE
// Size of static memory block of memory_pool.
// Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
// No dynamic memory allocations are performed by memory_pool until static memory is exhausted.
# define RAPIDXML_STATIC_POOL_SIZE (64 * 1024)
# endif
# ifndef RAPIDXML_DYNAMIC_POOL_SIZE
// Size of dynamic memory block of memory_pool.
// Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
// After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool.
# define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024)
# endif
# ifndef RAPIDXML_ALIGNMENT
// Memory allocation alignment.
// Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer.
// All memory allocations for nodes, attributes and strings will be aligned to this value.
// This must be a power of 2 and at least 1, otherwise memory_pool will not work.
# define RAPIDXML_ALIGNMENT sizeof(void *)
# endif
namespace rapidxml
{
// Forward declarations
template < class Ch > class xml_node ;
template < class Ch > class xml_attribute ;
template < class Ch > class xml_document ;
//! Enumeration listing all node types produced by the parser.
//! Use xml_node::type() function to query node type.
enum node_type
{
node_document , //!< A document node. Name and value are empty.
node_element , //!< An element node. Name contains element name. Value contains text of first data node.
node_data , //!< A data node. Name is empty. Value contains data text.
node_cdata , //!< A CDATA node. Name is empty. Value contains data text.
node_comment , //!< A comment node. Name is empty. Value contains comment text.
node_declaration , //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
node_doctype , //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
node_pi //!< A PI node. Name contains target. Value contains instructions.
} ;
///////////////////////////////////////////////////////////////////////
// Parsing flags
//! Parse flag instructing the parser to not create data nodes.
//! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_no_data_nodes = 0x1 ;
//! Parse flag instructing the parser to not use text of first data node as a value of parent element.
//! Can be combined with other flags by use of | operator.
//! Note that child data nodes of element node take precendence over its value when printing.
//! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored.
//! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.
//! <br><br>
//! See xml_document::parse() function.
const int parse_no_element_values = 0x2 ;
//! Parse flag instructing the parser to not place zero terminators after strings in the source text.
//! By default zero terminators are placed, modifying source text.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_no_string_terminators = 0x4 ;
//! Parse flag instructing the parser to not translate entities in the source text.
//! By default entities are translated, modifying source text.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_no_entity_translation = 0x8 ;
//! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters.
//! By default, UTF-8 handling is enabled.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_no_utf8 = 0x10 ;
//! Parse flag instructing the parser to create XML declaration node.
//! By default, declaration node is not created.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_declaration_node = 0x20 ;
//! Parse flag instructing the parser to create comments nodes.
//! By default, comment nodes are not created.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_comment_nodes = 0x40 ;
//! Parse flag instructing the parser to create DOCTYPE node.
//! By default, doctype node is not created.
//! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_doctype_node = 0x80 ;
//! Parse flag instructing the parser to create PI nodes.
//! By default, PI nodes are not created.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_pi_nodes = 0x100 ;
//! Parse flag instructing the parser to validate closing tag names.
//! If not set, name inside closing tag is irrelevant to the parser.
//! By default, closing tags are not validated.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_validate_closing_tags = 0x200 ;
//! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes.
//! By default, whitespace is not trimmed.
//! This flag does not cause the parser to modify source text.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_trim_whitespace = 0x400 ;
//! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character.
//! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag.
//! By default, whitespace is not normalized.
//! If this flag is specified, source text will be modified.
//! Can be combined with other flags by use of | operator.
//! <br><br>
//! See xml_document::parse() function.
const int parse_normalize_whitespace = 0x800 ;
// Compound flags
//! Parse flags which represent default behaviour of the parser.
//! This is always equal to 0, so that all other flags can be simply ored together.
//! Normally there is no need to inconveniently disable flags by anding with their negated (~) values.
//! This also means that meaning of each flag is a <i>negation</i> of the default setting.
//! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default,
//! and using the flag will disable it.
//! <br><br>
//! See xml_document::parse() function.
const int parse_default = 0 ;
//! A combination of parse flags that forbids any modifications of the source text.
//! This also results in faster parsing. However, note that the following will occur:
//! <ul>
//! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li>
//! <li>entities will not be translated</li>
//! <li>whitespace will not be normalized</li>
//! </ul>
//! See xml_document::parse() function.
const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation ;
//! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.
//! <br><br>
//! See xml_document::parse() function.
const int parse_fastest = parse_non_destructive | parse_no_data_nodes ;
//! A combination of parse flags resulting in largest amount of data being extracted.
//! This usually results in slowest parsing.
//! <br><br>
//! See xml_document::parse() function.
const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags ;
///////////////////////////////////////////////////////////////////////
// Internals
//! \cond internal
namespace internal
{
// Struct that contains lookup tables for the parser
// It must be a template to allow correct linking (because it has static data members, which are defined in a header file).
template < int Dummy >
struct lookup_tables
{
static const unsigned char lookup_whitespace [ 256 ] ; // Whitespace table
static const unsigned char lookup_node_name [ 256 ] ; // Node name table
static const unsigned char lookup_text [ 256 ] ; // Text table
static const unsigned char lookup_text_pure_no_ws [ 256 ] ; // Text table
static const unsigned char lookup_text_pure_with_ws [ 256 ] ; // Text table
static const unsigned char lookup_attribute_name [ 256 ] ; // Attribute name table
static const unsigned char lookup_attribute_data_1 [ 256 ] ; // Attribute data table with single quote
static const unsigned char lookup_attribute_data_1_pure [ 256 ] ; // Attribute data table with single quote
static const unsigned char lookup_attribute_data_2 [ 256 ] ; // Attribute data table with double quotes
static const unsigned char lookup_attribute_data_2_pure [ 256 ] ; // Attribute data table with double quotes
static const unsigned char lookup_digits [ 256 ] ; // Digits
static const unsigned char lookup_upcase [ 256 ] ; // To uppercase conversion table for ASCII characters
} ;
// Find length of the string
template < class Ch >
inline std : : size_t measure ( const Ch * p )
{
const Ch * tmp = p ;
while ( * tmp )
+ + tmp ;
return tmp - p ;
}
// Compare strings for equality
template < class Ch >
inline bool compare ( const Ch * p1 , std : : size_t size1 , const Ch * p2 , std : : size_t size2 , bool case_sensitive )
{
if ( size1 ! = size2 )
return false ;
if ( case_sensitive )
{
for ( const Ch * end = p1 + size1 ; p1 < end ; + + p1 , + + p2 )
if ( * p1 ! = * p2 )
return false ;
}
else
{
for ( const Ch * end = p1 + size1 ; p1 < end ; + + p1 , + + p2 )
if ( lookup_tables < 0 > : : lookup_upcase [ static_cast < unsigned char > ( * p1 ) ] ! = lookup_tables < 0 > : : lookup_upcase [ static_cast < unsigned char > ( * p2 ) ] )
return false ;
}
return true ;
}
}
//! \endcond
///////////////////////////////////////////////////////////////////////
// Memory pool
//! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation.
//! In most cases, you will not need to use this class directly.
//! However, if you need to create nodes manually or modify names/values of nodes,
//! you are encouraged to use memory_pool of relevant xml_document to allocate the memory.
//! Not only is this faster than allocating them by using <code>new</code> operator,
//! but also their lifetime will be tied to the lifetime of document,
//! possibly simplyfing memory management.
//! <br><br>
//! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool.
//! You can also call allocate_string() function to allocate strings.
//! Such strings can then be used as names or values of nodes without worrying about their lifetime.
//! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called,
//! or when the pool is destroyed.
//! <br><br>
//! It is also possible to create a standalone memory_pool, and use it
//! to allocate nodes, whose lifetime will not be tied to any document.
//! <br><br>
//! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory.
//! Until static memory is exhausted, no dynamic memory allocations are done.
//! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each,
//! by using global <code>new[]</code> and <code>delete[]</code> operators.
//! This behaviour can be changed by setting custom allocation routines.
//! Use set_allocator() function to set them.
//! <br><br>
//! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes.
//! This value defaults to the size of pointer on target architecture.
//! <br><br>
//! To obtain absolutely top performance from the parser,
//! it is important that all nodes are allocated from a single, contiguous block of memory.
//! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably.
//! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code>
//! to obtain best wasted memory to performance compromise.
//! To do it, define their values before rapidxml.hpp file is included.
//! \param Ch Character type of created nodes.
template < class Ch = char >
class memory_pool
{
public :
//! \cond internal
typedef void * ( alloc_func ) ( std : : size_t ) ; // Type of user-defined function used to allocate memory
typedef void ( free_func ) ( void * ) ; // Type of user-defined function used to free memory
//! \endcond
//! Constructs empty pool with default allocator functions.
memory_pool ( )
: m_alloc_func ( 0 )
, m_free_func ( 0 )
{
init ( ) ;
}
//! Destroys pool and frees all the memory.
//! This causes memory occupied by nodes allocated by the pool to be freed.
//! Nodes allocated from the pool are no longer valid.
~ memory_pool ( )
{
clear ( ) ;
}
//! Allocates a new node from the pool, and optionally assigns name and value to it.
//! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
//! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
//! will call rapidxml::parse_error_handler() function.
//! \param type Type of node to create.
//! \param name Name to assign to the node, or 0 to assign no name.
//! \param value Value to assign to the node, or 0 to assign no value.
//! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
//! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
//! \return Pointer to allocated node. This pointer will never be NULL.
xml_node < Ch > * allocate_node ( node_type type ,
const Ch * name = 0 , const Ch * value = 0 ,
std : : size_t name_size = 0 , std : : size_t value_size = 0 )
{
void * memory = allocate_aligned ( sizeof ( xml_node < Ch > ) ) ;
xml_node < Ch > * node = new ( memory ) xml_node < Ch > ( type ) ;
if ( name )
{
if ( name_size > 0 )
node - > name ( name , name_size ) ;
else
node - > name ( name ) ;
}
if ( value )
{
if ( value_size > 0 )
node - > value ( value , value_size ) ;
else
node - > value ( value ) ;
}
return node ;
}
//! Allocates a new attribute from the pool, and optionally assigns name and value to it.
//! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
//! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
//! will call rapidxml::parse_error_handler() function.
//! \param name Name to assign to the attribute, or 0 to assign no name.
//! \param value Value to assign to the attribute, or 0 to assign no value.
//! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
//! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
//! \return Pointer to allocated attribute. This pointer will never be NULL.
xml_attribute < Ch > * allocate_attribute ( const Ch * name = 0 , const Ch * value = 0 ,
std : : size_t name_size = 0 , std : : size_t value_size = 0 )
{
void * memory = allocate_aligned ( sizeof ( xml_attribute < Ch > ) ) ;
xml_attribute < Ch > * attribute = new ( memory ) xml_attribute < Ch > ;
if ( name )
{
if ( name_size > 0 )
attribute - > name ( name , name_size ) ;
else
attribute - > name ( name ) ;
}
if ( value )
{
if ( value_size > 0 )
attribute - > value ( value , value_size ) ;
else
attribute - > value ( value ) ;
}
return attribute ;
}
//! Allocates a char array of given size from the pool, and optionally copies a given string to it.
//! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
//! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
//! will call rapidxml::parse_error_handler() function.
//! \param source String to initialize the allocated memory with, or 0 to not initialize it.
//! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.
//! \return Pointer to allocated char array. This pointer will never be NULL.
Ch * allocate_string ( const Ch * source = 0 , std : : size_t size = 0 )
{
assert ( source | | size ) ; // Either source or size (or both) must be specified
if ( size = = 0 )
size = internal : : measure ( source ) + 1 ;
Ch * result = static_cast < Ch * > ( allocate_aligned ( size * sizeof ( Ch ) ) ) ;
if ( source )
for ( std : : size_t i = 0 ; i < size ; + + i )
result [ i ] = source [ i ] ;
return result ;
}
//! Clones an xml_node and its hierarchy of child nodes and attributes.
//! Nodes and attributes are allocated from this memory pool.
//! Names and values are not cloned, they are shared between the clone and the source.
//! Result node can be optionally specified as a second parameter,
//! in which case its contents will be replaced with cloned source node.
//! This is useful when you want to clone entire document.
//! \param source Node to clone.
//! \param result Node to put results in, or 0 to automatically allocate result node
//! \return Pointer to cloned node. This pointer will never be NULL.
xml_node < Ch > * clone_node ( const xml_node < Ch > * source , xml_node < Ch > * result = 0 )
{
// Prepare result node
if ( result )
{
result - > remove_all_attributes ( ) ;
result - > remove_all_nodes ( ) ;
result - > type ( source - > type ( ) ) ;
}
else
result = allocate_node ( source - > type ( ) ) ;
// Clone name and value
result - > name ( source - > name ( ) , source - > name_size ( ) ) ;
result - > value ( source - > value ( ) , source - > value_size ( ) ) ;
// Clone child nodes and attributes
for ( xml_node < Ch > * child = source - > first_node ( ) ; child ; child = child - > next_sibling ( ) )
result - > append_node ( clone_node ( child ) ) ;
for ( xml_attribute < Ch > * attr = source - > first_attribute ( ) ; attr ; attr = attr - > next_attribute ( ) )
result - > append_attribute ( allocate_attribute ( attr - > name ( ) , attr - > value ( ) , attr - > name_size ( ) , attr - > value_size ( ) ) ) ;
return result ;
}
//! Clears the pool.
//! This causes memory occupied by nodes allocated by the pool to be freed.
//! Any nodes or strings allocated from the pool will no longer be valid.
void clear ( )
{
while ( m_begin ! = m_static_memory )
{
char * previous_begin = reinterpret_cast < header * > ( align ( m_begin ) ) - > previous_begin ;
if ( m_free_func )
m_free_func ( m_begin ) ;
else
delete [ ] m_begin ;
m_begin = previous_begin ;
}
init ( ) ;
}
//! Sets or resets the user-defined memory allocation functions for the pool.
//! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined.
//! Allocation function must not return invalid pointer on failure. It should either throw,
//! stop the program, or use <code>longjmp()</code> function to pass control to other place of program.
//! If it returns invalid pointer, results are undefined.
//! <br><br>
//! User defined allocation functions must have the following forms:
//! <br><code>
//! <br>void *allocate(std::size_t size);
//! <br>void free(void *pointer);
//! </code><br>
//! \param af Allocation function, or 0 to restore default function
//! \param ff Free function, or 0 to restore default function
void set_allocator ( alloc_func * af , free_func * ff )
{
assert ( m_begin = = m_static_memory & & m_ptr = = align ( m_begin ) ) ; // Verify that no memory is allocated yet
m_alloc_func = af ;
m_free_func = ff ;
}
private :
struct header
{
char * previous_begin ;
} ;
void init ( )
{
m_begin = m_static_memory ;
m_ptr = align ( m_begin ) ;
m_end = m_static_memory + sizeof ( m_static_memory ) ;
}
char * align ( char * ptr )
{
std : : size_t alignment = ( ( RAPIDXML_ALIGNMENT - ( std : : size_t ( ptr ) & ( RAPIDXML_ALIGNMENT - 1 ) ) ) & ( RAPIDXML_ALIGNMENT - 1 ) ) ;
return ptr + alignment ;
}
char * allocate_raw ( std : : size_t size )
{
// Allocate
void * memory ;
if ( m_alloc_func ) // Allocate memory using either user-specified allocation function or global operator new[]
{
memory = m_alloc_func ( size ) ;
assert ( memory ) ; // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp
}
else
{
memory = new char [ size ] ;
# ifdef RAPIDXML_NO_EXCEPTIONS
if ( ! memory ) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc
RAPIDXML_PARSE_ERROR ( " out of memory " , 0 ) ;
# endif
}
return static_cast < char * > ( memory ) ;
}
void * allocate_aligned ( std : : size_t size )
{
// Calculate aligned pointer
char * result = align ( m_ptr ) ;
// If not enough memory left in current pool, allocate a new pool
if ( result + size > m_end )
{
// Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE)
std : : size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE ;
if ( pool_size < size )
pool_size = size ;
// Allocate
std : : size_t alloc_size = sizeof ( header ) + ( 2 * RAPIDXML_ALIGNMENT - 2 ) + pool_size ; // 2 alignments required in worst case: one for header, one for actual allocation
char * raw_memory = allocate_raw ( alloc_size ) ;
// Setup new pool in allocated memory
char * pool = align ( raw_memory ) ;
header * new_header = reinterpret_cast < header * > ( pool ) ;
new_header - > previous_begin = m_begin ;
m_begin = raw_memory ;
m_ptr = pool + sizeof ( header ) ;
m_end = raw_memory + alloc_size ;
// Calculate aligned pointer again using new pool
result = align ( m_ptr ) ;
}
// Update pool and return aligned pointer
m_ptr = result + size ;
return result ;
}
char * m_begin ; // Start of raw memory making up current pool
char * m_ptr ; // First free byte in current pool
char * m_end ; // One past last available byte in current pool
char m_static_memory [ RAPIDXML_STATIC_POOL_SIZE ] ; // Static raw memory
alloc_func * m_alloc_func ; // Allocator function, or 0 if default is to be used
free_func * m_free_func ; // Free function, or 0 if default is to be used
} ;
///////////////////////////////////////////////////////////////////////////
// XML base
//! Base class for xml_node and xml_attribute implementing common functions:
//! name(), name_size(), value(), value_size() and parent().
//! \param Ch Character type to use
template < class Ch = char >
class xml_base
{
public :
///////////////////////////////////////////////////////////////////////////
// Construction & destruction
// Construct a base with empty name, value and parent
xml_base ( )
: m_name ( 0 )
, m_value ( 0 )
, m_parent ( 0 )
{
}
///////////////////////////////////////////////////////////////////////////
// Node data access
//! Gets name of the node.
//! Interpretation of name depends on type of node.
//! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
//! <br><br>
//! Use name_size() function to determine length of the name.
//! \return Name of node, or empty string if node has no name.
Ch * name ( ) const
{
return m_name ? m_name : nullstr ( ) ;
}
//! Gets size of node name, not including terminator character.
//! This function works correctly irrespective of whether name is or is not zero terminated.
//! \return Size of node name, in characters.
std : : size_t name_size ( ) const
{
return m_name ? m_name_size : 0 ;
}
//! Gets value of node.
//! Interpretation of value depends on type of node.
//! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
//! <br><br>
//! Use value_size() function to determine length of the value.
//! \return Value of node, or empty string if node has no value.
Ch * value ( ) const
{
return m_value ? m_value : nullstr ( ) ;
}
//! Gets size of node value, not including terminator character.
//! This function works correctly irrespective of whether value is or is not zero terminated.
//! \return Size of node value, in characters.
std : : size_t value_size ( ) const
{
return m_value ? m_value_size : 0 ;
}
///////////////////////////////////////////////////////////////////////////
// Node modification
//! Sets name of node to a non zero-terminated string.
//! See \ref ownership_of_strings.
//! <br><br>
//! Note that node does not own its name or value, it only stores a pointer to it.
//! It will not delete or otherwise free the pointer on destruction.
//! It is reponsibility of the user to properly manage lifetime of the string.
//! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
//! on destruction of the document the string will be automatically freed.
//! <br><br>
//! Size of name must be specified separately, because name does not have to be zero terminated.
//! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).
//! \param name Name of node to set. Does not have to be zero terminated.
//! \param size Size of name, in characters. This does not include zero terminator, if one is present.
void name ( const Ch * name , std : : size_t size )
{
m_name = const_cast < Ch * > ( name ) ;
m_name_size = size ;
}
//! Sets name of node to a zero-terminated string.
//! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t).
//! \param name Name of node to set. Must be zero terminated.
void name ( const Ch * name )
{
this - > name ( name , internal : : measure ( name ) ) ;
}
//! Sets value of node to a non zero-terminated string.
//! See \ref ownership_of_strings.
//! <br><br>
//! Note that node does not own its name or value, it only stores a pointer to it.
//! It will not delete or otherwise free the pointer on destruction.
//! It is reponsibility of the user to properly manage lifetime of the string.
//! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
//! on destruction of the document the string will be automatically freed.
//! <br><br>
//! Size of value must be specified separately, because it does not have to be zero terminated.
//! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).
//! <br><br>
//! If an element has a child node of type node_data, it will take precedence over element value when printing.
//! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.
//! \param value value of node to set. Does not have to be zero terminated.
//! \param size Size of value, in characters. This does not include zero terminator, if one is present.
void value ( const Ch * value , std : : size_t size )
{
m_value = const_cast < Ch * > ( value ) ;
m_value_size = size ;
}
//! Sets value of node to a zero-terminated string.
//! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t).
//! \param value Vame of node to set. Must be zero terminated.
void value ( const Ch * value )
{
this - > value ( value , internal : : measure ( value ) ) ;
}
///////////////////////////////////////////////////////////////////////////
// Related nodes access
//! Gets node parent.
//! \return Pointer to parent node, or 0 if there is no parent.
xml_node < Ch > * parent ( ) const
{
return m_parent ;
}
protected :
// Return empty string
static Ch * nullstr ( )
{
static Ch zero = Ch ( ' \0 ' ) ;
return & zero ;
}
Ch * m_name ; // Name of node, or 0 if no name
Ch * m_value ; // Value of node, or 0 if no value
std : : size_t m_name_size ; // Length of node name, or undefined of no name
std : : size_t m_value_size ; // Length of node value, or undefined if no value
xml_node < Ch > * m_parent ; // Pointer to parent node, or 0 if none
} ;
//! Class representing attribute node of XML document.
//! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base).
//! Note that after parse, both name and value of attribute will point to interior of source text used for parsing.
//! Thus, this text must persist in memory for the lifetime of attribute.
//! \param Ch Character type to use.
template < class Ch = char >
class xml_attribute : public xml_base < Ch >
{
friend class xml_node < Ch > ;
public :
///////////////////////////////////////////////////////////////////////////
// Construction & destruction
//! Constructs an empty attribute with the specified type.
//! Consider using memory_pool of appropriate xml_document if allocating attributes manually.
xml_attribute ( )
{
}
///////////////////////////////////////////////////////////////////////////
// Related nodes access
//! Gets document of which attribute is a child.
//! \return Pointer to document that contains this attribute, or 0 if there is no parent document.
xml_document < Ch > * document ( ) const
{
if ( xml_node < Ch > * node = this - > parent ( ) )
{
while ( node - > parent ( ) )
node = node - > parent ( ) ;
return node - > type ( ) = = node_document ? static_cast < xml_document < Ch > * > ( node ) : 0 ;
}
else
return 0 ;
}
//! Gets previous attribute, optionally matching attribute name.
//! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found attribute, or 0 if not found.
xml_attribute < Ch > * previous_attribute ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_attribute < Ch > * attribute = m_prev_attribute ; attribute ; attribute = attribute - > m_prev_attribute )
if ( internal : : compare ( attribute - > name ( ) , attribute - > name_size ( ) , name , name_size , case_sensitive ) )
return attribute ;
return 0 ;
}
else
return this - > m_parent ? m_prev_attribute : 0 ;
}
//! Gets next attribute, optionally matching attribute name.
//! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found attribute, or 0 if not found.
xml_attribute < Ch > * next_attribute ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_attribute < Ch > * attribute = m_next_attribute ; attribute ; attribute = attribute - > m_next_attribute )
if ( internal : : compare ( attribute - > name ( ) , attribute - > name_size ( ) , name , name_size , case_sensitive ) )
return attribute ;
return 0 ;
}
else
return this - > m_parent ? m_next_attribute : 0 ;
}
private :
xml_attribute < Ch > * m_prev_attribute ; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero
xml_attribute < Ch > * m_next_attribute ; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero
} ;
///////////////////////////////////////////////////////////////////////////
// XML node
//! Class representing a node of XML document.
//! Each node may have associated name and value strings, which are available through name() and value() functions.
//! Interpretation of name and value depends on type of the node.
//! Type of node can be determined by using type() function.
//! <br><br>
//! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing.
//! Thus, this text must persist in the memory for the lifetime of node.
//! \param Ch Character type to use.
template < class Ch = char >
class xml_node : public xml_base < Ch >
{
public :
///////////////////////////////////////////////////////////////////////////
// Construction & destruction
//! Constructs an empty node with the specified type.
//! Consider using memory_pool of appropriate document to allocate nodes manually.
//! \param type Type of node to construct.
xml_node ( node_type type )
: m_type ( type )
, m_first_node ( 0 )
, m_first_attribute ( 0 )
{
}
///////////////////////////////////////////////////////////////////////////
// Node data access
//! Gets type of node.
//! \return Type of node.
node_type type ( ) const
{
return m_type ;
}
///////////////////////////////////////////////////////////////////////////
// Related nodes access
//! Gets document of which node is a child.
//! \return Pointer to document that contains this node, or 0 if there is no parent document.
xml_document < Ch > * document ( ) const
{
xml_node < Ch > * node = const_cast < xml_node < Ch > * > ( this ) ;
while ( node - > parent ( ) )
node = node - > parent ( ) ;
return node - > type ( ) = = node_document ? static_cast < xml_document < Ch > * > ( node ) : 0 ;
}
//! Gets first child node, optionally matching node name.
//! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found child, or 0 if not found.
xml_node < Ch > * first_node ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_node < Ch > * child = m_first_node ; child ; child = child - > next_sibling ( ) )
if ( internal : : compare ( child - > name ( ) , child - > name_size ( ) , name , name_size , case_sensitive ) )
return child ;
return 0 ;
}
else
return m_first_node ;
}
//! Gets last child node, optionally matching node name.
//! Behaviour is undefined if node has no children.
//! Use first_node() to test if node has children.
//! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found child, or 0 if not found.
xml_node < Ch > * last_node ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
assert ( m_first_node ) ; // Cannot query for last child if node has no children
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_node < Ch > * child = m_last_node ; child ; child = child - > previous_sibling ( ) )
if ( internal : : compare ( child - > name ( ) , child - > name_size ( ) , name , name_size , case_sensitive ) )
return child ;
return 0 ;
}
else
return m_last_node ;
}
//! Gets previous sibling node, optionally matching node name.
//! Behaviour is undefined if node has no parent.
//! Use parent() to test if node has a parent.
//! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found sibling, or 0 if not found.
xml_node < Ch > * previous_sibling ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
assert ( this - > m_parent ) ; // Cannot query for siblings if node has no parent
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_node < Ch > * sibling = m_prev_sibling ; sibling ; sibling = sibling - > m_prev_sibling )
if ( internal : : compare ( sibling - > name ( ) , sibling - > name_size ( ) , name , name_size , case_sensitive ) )
return sibling ;
return 0 ;
}
else
return m_prev_sibling ;
}
//! Gets next sibling node, optionally matching node name.
//! Behaviour is undefined if node has no parent.
//! Use parent() to test if node has a parent.
//! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found sibling, or 0 if not found.
xml_node < Ch > * next_sibling ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
assert ( this - > m_parent ) ; // Cannot query for siblings if node has no parent
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_node < Ch > * sibling = m_next_sibling ; sibling ; sibling = sibling - > m_next_sibling )
if ( internal : : compare ( sibling - > name ( ) , sibling - > name_size ( ) , name , name_size , case_sensitive ) )
return sibling ;
return 0 ;
}
else
return m_next_sibling ;
}
//! Gets first attribute of node, optionally matching attribute name.
//! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found attribute, or 0 if not found.
xml_attribute < Ch > * first_attribute ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_attribute < Ch > * attribute = m_first_attribute ; attribute ; attribute = attribute - > m_next_attribute )
if ( internal : : compare ( attribute - > name ( ) , attribute - > name_size ( ) , name , name_size , case_sensitive ) )
return attribute ;
return 0 ;
}
else
return m_first_attribute ;
}
//! Gets last attribute of node, optionally matching attribute name.
//! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
//! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
//! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
//! \return Pointer to found attribute, or 0 if not found.
xml_attribute < Ch > * last_attribute ( const Ch * name = 0 , std : : size_t name_size = 0 , bool case_sensitive = true ) const
{
if ( name )
{
if ( name_size = = 0 )
name_size = internal : : measure ( name ) ;
for ( xml_attribute < Ch > * attribute = m_last_attribute ; attribute ; attribute = attribute - > m_prev_attribute )
if ( internal : : compare ( attribute - > name ( ) , attribute - > name_size ( ) , name , name_size , case_sensitive ) )
return attribute ;
return 0 ;
}
else
return m_first_attribute ? m_last_attribute : 0 ;
}
///////////////////////////////////////////////////////////////////////////
// Node modification
//! Sets type of node.
//! \param type Type of node to set.
void type ( node_type type )
{
m_type = type ;
}
///////////////////////////////////////////////////////////////////////////
// Node manipulation
//! Prepends a new child node.
//! The prepended child becomes the first child, and all existing children are moved one position back.
//! \param child Node to prepend.
void prepend_node ( xml_node < Ch > * child )
{
assert ( child & & ! child - > parent ( ) & & child - > type ( ) ! = node_document ) ;
if ( first_node ( ) )
{
child - > m_next_sibling = m_first_node ;
m_first_node - > m_prev_sibling = child ;
}
else
{
child - > m_next_sibling = 0 ;
m_last_node = child ;
}
m_first_node = child ;
child - > m_parent = this ;
child - > m_prev_sibling = 0 ;
}
//! Appends a new child node.
//! The appended child becomes the last child.
//! \param child Node to append.
void append_node ( xml_node < Ch > * child )
{
assert ( child & & ! child - > parent ( ) & & child - > type ( ) ! = node_document ) ;
if ( first_node ( ) )
{
child - > m_prev_sibling = m_last_node ;
m_last_node - > m_next_sibling = child ;
}
else
{
child - > m_prev_sibling = 0 ;
m_first_node = child ;
}
m_last_node = child ;
child - > m_parent = this ;
child - > m_next_sibling = 0 ;
}
//! Inserts a new child node at specified place inside the node.
//! All children after and including the specified node are moved one position back.
//! \param where Place where to insert the child, or 0 to insert at the back.
//! \param child Node to insert.
void insert_node ( xml_node < Ch > * where , xml_node < Ch > * child )
{
assert ( ! where | | where - > parent ( ) = = this ) ;
assert ( child & & ! child - > parent ( ) & & child - > type ( ) ! = node_document ) ;
if ( where = = m_first_node )
prepend_node ( child ) ;
else if ( where = = 0 )
append_node ( child ) ;
else
{
child - > m_prev_sibling = where - > m_prev_sibling ;
child - > m_next_sibling = where ;
where - > m_prev_sibling - > m_next_sibling = child ;
where - > m_prev_sibling = child ;
child - > m_parent = this ;
}
}
//! Removes first child node.
//! If node has no children, behaviour is undefined.
//! Use first_node() to test if node has children.
void remove_first_node ( )
{
assert ( first_node ( ) ) ;
xml_node < Ch > * child = m_first_node ;
m_first_node = child - > m_next_sibling ;
if ( child - > m_next_sibling )
child - > m_next_sibling - > m_prev_sibling = 0 ;
else
m_last_node = 0 ;
child - > m_parent = 0 ;
}
//! Removes last child of the node.
//! If node has no children, behaviour is undefined.
//! Use first_node() to test if node has children.
void remove_last_node ( )
{
assert ( first_node ( ) ) ;
xml_node < Ch > * child = m_last_node ;
if ( child - > m_prev_sibling )
{
m_last_node = child - > m_prev_sibling ;
child - > m_prev_sibling - > m_next_sibling = 0 ;
}
else
m_first_node = 0 ;
child - > m_parent = 0 ;
}
//! Removes specified child from the node
// \param where Pointer to child to be removed.
void remove_node ( xml_node < Ch > * where )
{
assert ( where & & where - > parent ( ) = = this ) ;
assert ( first_node ( ) ) ;
if ( where = = m_first_node )
remove_first_node ( ) ;
else if ( where = = m_last_node )
remove_last_node ( ) ;
else
{
where - > m_prev_sibling - > m_next_sibling = where - > m_next_sibling ;
where - > m_next_sibling - > m_prev_sibling = where - > m_prev_sibling ;
where - > m_parent = 0 ;
}
}
//! Removes all child nodes (but not attributes).
void remove_all_nodes ( )
{
for ( xml_node < Ch > * node = first_node ( ) ; node ; node = node - > m_next_sibling )
node - > m_parent = 0 ;
m_first_node = 0 ;
}
//! Prepends a new attribute to the node.
//! \param attribute Attribute to prepend.
void prepend_attribute ( xml_attribute < Ch > * attribute )
{
assert ( attribute & & ! attribute - > parent ( ) ) ;
if ( first_attribute ( ) )
{
attribute - > m_next_attribute = m_first_attribute ;
m_first_attribute - > m_prev_attribute = attribute ;
}
else
{
attribute - > m_next_attribute = 0 ;
m_last_attribute = attribute ;
}
m_first_attribute = attribute ;
attribute - > m_parent = this ;
attribute - > m_prev_attribute = 0 ;
}
//! Appends a new attribute to the node.
//! \param attribute Attribute to append.
void append_attribute ( xml_attribute < Ch > * attribute )
{
assert ( attribute & & ! attribute - > parent ( ) ) ;
if ( first_attribute ( ) )
{
attribute - > m_prev_attribute = m_last_attribute ;
m_last_attribute - > m_next_attribute = attribute ;
}
else
{
attribute - > m_prev_attribute = 0 ;
m_first_attribute = attribute ;
}
m_last_attribute = attribute ;
attribute - > m_parent = this ;
attribute - > m_next_attribute = 0 ;
}
//! Inserts a new attribute at specified place inside the node.
//! All attributes after and including the specified attribute are moved one position back.
//! \param where Place where to insert the attribute, or 0 to insert at the back.
//! \param attribute Attribute to insert.
void insert_attribute ( xml_attribute < Ch > * where , xml_attribute < Ch > * attribute )
{
assert ( ! where | | where - > parent ( ) = = this ) ;
assert ( attribute & & ! attribute - > parent ( ) ) ;
if ( where = = m_first_attribute )
prepend_attribute ( attribute ) ;
else if ( where = = 0 )
append_attribute ( attribute ) ;
else
{
attribute - > m_prev_attribute = where - > m_prev_attribute ;
attribute - > m_next_attribute = where ;
where - > m_prev_attribute - > m_next_attribute = attribute ;
where - > m_prev_attribute = attribute ;
attribute - > m_parent = this ;
}
}
//! Removes first attribute of the node.
//! If node has no attributes, behaviour is undefined.
//! Use first_attribute() to test if node has attributes.
void remove_first_attribute ( )
{
assert ( first_attribute ( ) ) ;
xml_attribute < Ch > * attribute = m_first_attribute ;
if ( attribute - > m_next_attribute )
{
attribute - > m_next_attribute - > m_prev_attribute = 0 ;
}
else
m_last_attribute = 0 ;
attribute - > m_parent = 0 ;
m_first_attribute = attribute - > m_next_attribute ;
}
//! Removes last attribute of the node.
//! If node has no attributes, behaviour is undefined.
//! Use first_attribute() to test if node has attributes.
void remove_last_attribute ( )
{
assert ( first_attribute ( ) ) ;
xml_attribute < Ch > * attribute = m_last_attribute ;
if ( attribute - > m_prev_attribute )
{
attribute - > m_prev_attribute - > m_next_attribute = 0 ;
m_last_attribute = attribute - > m_prev_attribute ;
}
else
m_first_attribute = 0 ;
attribute - > m_parent = 0 ;
}
//! Removes specified attribute from node.
//! \param where Pointer to attribute to be removed.
void remove_attribute ( xml_attribute < Ch > * where )
{
assert ( first_attribute ( ) & & where - > parent ( ) = = this ) ;
if ( where = = m_first_attribute )
remove_first_attribute ( ) ;
else if ( where = = m_last_attribute )
remove_last_attribute ( ) ;
else
{
where - > m_prev_attribute - > m_next_attribute = where - > m_next_attribute ;
where - > m_next_attribute - > m_prev_attribute = where - > m_prev_attribute ;
where - > m_parent = 0 ;
}
}
//! Removes all attributes of node.
void remove_all_attributes ( )
{
for ( xml_attribute < Ch > * attribute = first_attribute ( ) ; attribute ; attribute = attribute - > m_next_attribute )
attribute - > m_parent = 0 ;
m_first_attribute = 0 ;
}
private :
///////////////////////////////////////////////////////////////////////////
// Restrictions
// No copying
xml_node ( const xml_node & ) ;
void operator = ( const xml_node & ) ;
///////////////////////////////////////////////////////////////////////////
// Data members
// Note that some of the pointers below have UNDEFINED values if certain other pointers are 0.
// This is required for maximum performance, as it allows the parser to omit initialization of
// unneded/redundant values.
//
// The rules are as follows:
// 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively
// 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage
// 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage
node_type m_type ; // Type of node; always valid
xml_node < Ch > * m_first_node ; // Pointer to first child node, or 0 if none; always valid
xml_node < Ch > * m_last_node ; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero
xml_attribute < Ch > * m_first_attribute ; // Pointer to first attribute of node, or 0 if none; always valid
xml_attribute < Ch > * m_last_attribute ; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero
xml_node < Ch > * m_prev_sibling ; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero
xml_node < Ch > * m_next_sibling ; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero
} ;
///////////////////////////////////////////////////////////////////////////
// XML document
//! This class represents root of the DOM hierarchy.
//! It is also an xml_node and a memory_pool through public inheritance.
//! Use parse() function to build a DOM tree from a zero-terminated XML text string.
//! parse() function allocates memory for nodes and attributes by using functions of xml_document,
//! which are inherited from memory_pool.
//! To access root node of the document, use the document itself, as if it was an xml_node.
//! \param Ch Character type to use.
template < class Ch = char >
class xml_document : public xml_node < Ch > , public memory_pool < Ch >
{
public :
//! Constructs empty XML document
xml_document ( )
: xml_node < Ch > ( node_document )
{
}
//! Parses zero-terminated XML string according to given flags.
//! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used.
//! The string must persist for the lifetime of the document.
//! In case of error, rapidxml::parse_error exception will be thrown.
//! <br><br>
//! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning.
//! Make sure that data is zero-terminated.
//! <br><br>
//! Document can be parsed into multiple times.
//! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.
//! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.
template < int Flags >
void parse ( Ch * text )
{
assert ( text ) ;
// Remove current contents
this - > remove_all_nodes ( ) ;
this - > remove_all_attributes ( ) ;
// Parse BOM, if any
parse_bom < Flags > ( text ) ;
// Parse children
while ( 1 )
{
// Skip whitespace before node
skip < whitespace_pred , Flags > ( text ) ;
if ( * text = = 0 )
break ;
// Parse and append new child
if ( * text = = Ch ( ' < ' ) )
{
+ + text ; // Skip '<'
if ( xml_node < Ch > * node = parse_node < Flags > ( text ) )
this - > append_node ( node ) ;
}
else
RAPIDXML_PARSE_ERROR ( " expected < " , text ) ;
}
}
//! Clears the document by deleting all nodes and clearing the memory pool.
//! All nodes owned by document pool are destroyed.
void clear ( )
{
this - > remove_all_nodes ( ) ;
this - > remove_all_attributes ( ) ;
memory_pool < Ch > : : clear ( ) ;
}
private :
///////////////////////////////////////////////////////////////////////
// Internal character utility functions
// Detect whitespace character
struct whitespace_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_whitespace [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect node name character
struct node_name_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_node_name [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect attribute name character
struct attribute_name_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_attribute_name [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect text character (PCDATA)
struct text_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_text [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect text character (PCDATA) that does not require processing
struct text_pure_no_ws_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_text_pure_no_ws [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect text character (PCDATA) that does not require processing
struct text_pure_with_ws_pred
{
static unsigned char test ( Ch ch )
{
return internal : : lookup_tables < 0 > : : lookup_text_pure_with_ws [ static_cast < unsigned char > ( ch ) ] ;
}
} ;
// Detect attribute value character
template < Ch Quote >
struct attribute_value_pred
{
static unsigned char test ( Ch ch )
{
if ( Quote = = Ch ( ' \' ' ) )
return internal : : lookup_tables < 0 > : : lookup_attribute_data_1 [ static_cast < unsigned char > ( ch ) ] ;
if ( Quote = = Ch ( ' \" ' ) )
return internal : : lookup_tables < 0 > : : lookup_attribute_data_2 [ static_cast < unsigned char > ( ch ) ] ;
return 0 ; // Should never be executed, to avoid warnings on Comeau
}
} ;
// Detect attribute value character
template < Ch Quote >
struct attribute_value_pure_pred
{
static unsigned char test ( Ch ch )
{
if ( Quote = = Ch ( ' \' ' ) )
return internal : : lookup_tables < 0 > : : lookup_attribute_data_1_pure [ static_cast < unsigned char > ( ch ) ] ;
if ( Quote = = Ch ( ' \" ' ) )
return internal : : lookup_tables < 0 > : : lookup_attribute_data_2_pure [ static_cast < unsigned char > ( ch ) ] ;
return 0 ; // Should never be executed, to avoid warnings on Comeau
}
} ;
// Insert coded character, using UTF8 or 8-bit ASCII
template < int Flags >
static void insert_coded_character ( Ch * & text , unsigned long code )
{
if ( Flags & parse_no_utf8 )
{
// Insert 8-bit ASCII character
// Todo: possibly verify that code is less than 256 and use replacement char otherwise?
text [ 0 ] = static_cast < unsigned char > ( code ) ;
text + = 1 ;
}
else
{
// Insert UTF8 sequence
if ( code < 0x80 ) // 1 byte sequence
{
text [ 0 ] = static_cast < unsigned char > ( code ) ;
text + = 1 ;
}
else if ( code < 0x800 ) // 2 byte sequence
{
text [ 1 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 0 ] = static_cast < unsigned char > ( code | 0xC0 ) ;
text + = 2 ;
}
else if ( code < 0x10000 ) // 3 byte sequence
{
text [ 2 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 1 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 0 ] = static_cast < unsigned char > ( code | 0xE0 ) ;
text + = 3 ;
}
else if ( code < 0x110000 ) // 4 byte sequence
{
text [ 3 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 2 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 1 ] = static_cast < unsigned char > ( ( code | 0x80 ) & 0xBF ) ; code > > = 6 ;
text [ 0 ] = static_cast < unsigned char > ( code | 0xF0 ) ;
text + = 4 ;
}
else // Invalid, only codes up to 0x10FFFF are allowed in Unicode
{
RAPIDXML_PARSE_ERROR ( " invalid numeric character entity " , text ) ;
}
}
}
// Skip characters until predicate evaluates to true
template < class StopPred , int Flags >
static void skip ( Ch * & text )
{
Ch * tmp = text ;
while ( StopPred : : test ( * tmp ) )
+ + tmp ;
text = tmp ;
}
// Skip characters until predicate evaluates to true while doing the following:
// - replacing XML character entity references with proper characters (' & " < > &#...;)
// - condensing whitespace sequences to single space character
template < class StopPred , class StopPredPure , int Flags >
static Ch * skip_and_expand_character_refs ( Ch * & text )
{
// If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip
if ( Flags & parse_no_entity_translation & &
! ( Flags & parse_normalize_whitespace ) & &
! ( Flags & parse_trim_whitespace ) )
{
skip < StopPred , Flags > ( text ) ;
return text ;
}
// Use simple skip until first modification is detected
skip < StopPredPure , Flags > ( text ) ;
// Use translation skip
Ch * src = text ;
Ch * dest = src ;
while ( StopPred : : test ( * src ) )
{
// If entity translation is enabled
if ( ! ( Flags & parse_no_entity_translation ) )
{
// Test if replacement is needed
if ( src [ 0 ] = = Ch ( ' & ' ) )
{
switch ( src [ 1 ] )
{
// & '
case Ch ( ' a ' ) :
if ( src [ 2 ] = = Ch ( ' m ' ) & & src [ 3 ] = = Ch ( ' p ' ) & & src [ 4 ] = = Ch ( ' ; ' ) )
{
* dest = Ch ( ' & ' ) ;
+ + dest ;
src + = 5 ;
continue ;
}
if ( src [ 2 ] = = Ch ( ' p ' ) & & src [ 3 ] = = Ch ( ' o ' ) & & src [ 4 ] = = Ch ( ' s ' ) & & src [ 5 ] = = Ch ( ' ; ' ) )
{
* dest = Ch ( ' \' ' ) ;
+ + dest ;
src + = 6 ;
continue ;
}
break ;
// "
case Ch ( ' q ' ) :
if ( src [ 2 ] = = Ch ( ' u ' ) & & src [ 3 ] = = Ch ( ' o ' ) & & src [ 4 ] = = Ch ( ' t ' ) & & src [ 5 ] = = Ch ( ' ; ' ) )
{
* dest = Ch ( ' " ' ) ;
+ + dest ;
src + = 6 ;
continue ;
}
break ;
// >
case Ch ( ' g ' ) :
if ( src [ 2 ] = = Ch ( ' t ' ) & & src [ 3 ] = = Ch ( ' ; ' ) )
{
* dest = Ch ( ' > ' ) ;
+ + dest ;
src + = 4 ;
continue ;
}
break ;
// <
case Ch ( ' l ' ) :
if ( src [ 2 ] = = Ch ( ' t ' ) & & src [ 3 ] = = Ch ( ' ; ' ) )
{
* dest = Ch ( ' < ' ) ;
+ + dest ;
src + = 4 ;
continue ;
}
break ;
// &#...; - assumes ASCII
case Ch ( ' # ' ) :
if ( src [ 2 ] = = Ch ( ' x ' ) )
{
unsigned long code = 0 ;
src + = 3 ; // Skip &#x
while ( 1 )
{
unsigned char digit = internal : : lookup_tables < 0 > : : lookup_digits [ static_cast < unsigned char > ( * src ) ] ;
if ( digit = = 0xFF )
break ;
code = code * 16 + digit ;
+ + src ;
}
insert_coded_character < Flags > ( dest , code ) ; // Put character in output
}
else
{
unsigned long code = 0 ;
src + = 2 ; // Skip &#
while ( 1 )
{
unsigned char digit = internal : : lookup_tables < 0 > : : lookup_digits [ static_cast < unsigned char > ( * src ) ] ;
if ( digit = = 0xFF )
break ;
code = code * 10 + digit ;
+ + src ;
}
insert_coded_character < Flags > ( dest , code ) ; // Put character in output
}
if ( * src = = Ch ( ' ; ' ) )
+ + src ;
else
RAPIDXML_PARSE_ERROR ( " expected ; " , src ) ;
continue ;
// Something else
default :
// Ignore, just copy '&' verbatim
break ;
}
}
}
// If whitespace condensing is enabled
if ( Flags & parse_normalize_whitespace )
{
// Test if condensing is needed
if ( whitespace_pred : : test ( * src ) )
{
* dest = Ch ( ' ' ) ; + + dest ; // Put single space in dest
+ + src ; // Skip first whitespace char
// Skip remaining whitespace chars
while ( whitespace_pred : : test ( * src ) )
+ + src ;
continue ;
}
}
// No replacement, only copy character
* dest + + = * src + + ;
}
// Return new end
text = src ;
return dest ;
}
///////////////////////////////////////////////////////////////////////
// Internal parsing functions
// Parse BOM, if any
template < int Flags >
void parse_bom ( Ch * & text )
{
// UTF-8?
if ( static_cast < unsigned char > ( text [ 0 ] ) = = 0xEF & &
static_cast < unsigned char > ( text [ 1 ] ) = = 0xBB & &
static_cast < unsigned char > ( text [ 2 ] ) = = 0xBF )
{
text + = 3 ; // Skup utf-8 bom
}
}
// Parse XML declaration (<?xml...)
template < int Flags >
xml_node < Ch > * parse_xml_declaration ( Ch * & text )
{
// If parsing of declaration is disabled
if ( ! ( Flags & parse_declaration_node ) )
{
// Skip until end of declaration
while ( text [ 0 ] ! = Ch ( ' ? ' ) | | text [ 1 ] ! = Ch ( ' > ' ) )
{
if ( ! text [ 0 ] )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
text + = 2 ; // Skip '?>'
return 0 ;
}
// Create declaration
xml_node < Ch > * declaration = this - > allocate_node ( node_declaration ) ;
// Skip whitespace before attributes or ?>
skip < whitespace_pred , Flags > ( text ) ;
// Parse declaration attributes
parse_node_attributes < Flags > ( text , declaration ) ;
// Skip ?>
if ( text [ 0 ] ! = Ch ( ' ? ' ) | | text [ 1 ] ! = Ch ( ' > ' ) )
RAPIDXML_PARSE_ERROR ( " expected ?> " , text ) ;
text + = 2 ;
return declaration ;
}
// Parse XML comment (<!--...)
template < int Flags >
xml_node < Ch > * parse_comment ( Ch * & text )
{
// If parsing of comments is disabled
if ( ! ( Flags & parse_comment_nodes ) )
{
// Skip until end of comment
while ( text [ 0 ] ! = Ch ( ' - ' ) | | text [ 1 ] ! = Ch ( ' - ' ) | | text [ 2 ] ! = Ch ( ' > ' ) )
{
if ( ! text [ 0 ] )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
text + = 3 ; // Skip '-->'
return 0 ; // Do not produce comment node
}
// Remember value start
Ch * value = text ;
// Skip until end of comment
while ( text [ 0 ] ! = Ch ( ' - ' ) | | text [ 1 ] ! = Ch ( ' - ' ) | | text [ 2 ] ! = Ch ( ' > ' ) )
{
if ( ! text [ 0 ] )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
// Create comment node
xml_node < Ch > * comment = this - > allocate_node ( node_comment ) ;
comment - > value ( value , text - value ) ;
// Place zero terminator after comment value
if ( ! ( Flags & parse_no_string_terminators ) )
* text = Ch ( ' \0 ' ) ;
text + = 3 ; // Skip '-->'
return comment ;
}
// Parse DOCTYPE
template < int Flags >
xml_node < Ch > * parse_doctype ( Ch * & text )
{
// Remember value start
Ch * value = text ;
// Skip to >
while ( * text ! = Ch ( ' > ' ) )
{
// Determine character type
switch ( * text )
{
// If '[' encountered, scan for matching ending ']' using naive algorithm with depth
// This works for all W3C test files except for 2 most wicked
case Ch ( ' [ ' ) :
{
+ + text ; // Skip '['
int depth = 1 ;
while ( depth > 0 )
{
switch ( * text )
{
case Ch ( ' [ ' ) : + + depth ; break ;
case Ch ( ' ] ' ) : - - depth ; break ;
case 0 : RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
}
+ + text ;
}
break ;
}
// Error on end of text
case Ch ( ' \0 ' ) :
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
// Other character, skip it
default :
+ + text ;
}
}
// If DOCTYPE nodes enabled
if ( Flags & parse_doctype_node )
{
// Create a new doctype node
xml_node < Ch > * doctype = this - > allocate_node ( node_doctype ) ;
doctype - > value ( value , text - value ) ;
// Place zero terminator after value
if ( ! ( Flags & parse_no_string_terminators ) )
* text = Ch ( ' \0 ' ) ;
text + = 1 ; // skip '>'
return doctype ;
}
else
{
text + = 1 ; // skip '>'
return 0 ;
}
}
// Parse PI
template < int Flags >
xml_node < Ch > * parse_pi ( Ch * & text )
{
// If creation of PI nodes is enabled
if ( Flags & parse_pi_nodes )
{
// Create pi node
xml_node < Ch > * pi = this - > allocate_node ( node_pi ) ;
// Extract PI target name
Ch * name = text ;
skip < node_name_pred , Flags > ( text ) ;
if ( text = = name )
RAPIDXML_PARSE_ERROR ( " expected PI target " , text ) ;
pi - > name ( name , text - name ) ;
// Skip whitespace between pi target and pi
skip < whitespace_pred , Flags > ( text ) ;
// Remember start of pi
Ch * value = text ;
// Skip to '?>'
while ( text [ 0 ] ! = Ch ( ' ? ' ) | | text [ 1 ] ! = Ch ( ' > ' ) )
{
if ( * text = = Ch ( ' \0 ' ) )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
// Set pi value (verbatim, no entity expansion or whitespace normalization)
pi - > value ( value , text - value ) ;
// Place zero terminator after name and value
if ( ! ( Flags & parse_no_string_terminators ) )
{
pi - > name ( ) [ pi - > name_size ( ) ] = Ch ( ' \0 ' ) ;
pi - > value ( ) [ pi - > value_size ( ) ] = Ch ( ' \0 ' ) ;
}
text + = 2 ; // Skip '?>'
return pi ;
}
else
{
// Skip to '?>'
while ( text [ 0 ] ! = Ch ( ' ? ' ) | | text [ 1 ] ! = Ch ( ' > ' ) )
{
if ( * text = = Ch ( ' \0 ' ) )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
text + = 2 ; // Skip '?>'
return 0 ;
}
}
// Parse and append data
// Return character that ends data.
// This is necessary because this character might have been overwritten by a terminating 0
template < int Flags >
Ch parse_and_append_data ( xml_node < Ch > * node , Ch * & text , Ch * contents_start )
{
// Backup to contents start if whitespace trimming is disabled
if ( ! ( Flags & parse_trim_whitespace ) )
text = contents_start ;
// Skip until end of data
Ch * value = text , * end ;
if ( Flags & parse_normalize_whitespace )
end = skip_and_expand_character_refs < text_pred , text_pure_with_ws_pred , Flags > ( text ) ;
else
end = skip_and_expand_character_refs < text_pred , text_pure_no_ws_pred , Flags > ( text ) ;
// Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after >
if ( Flags & parse_trim_whitespace )
{
if ( Flags & parse_normalize_whitespace )
{
// Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end
if ( * ( end - 1 ) = = Ch ( ' ' ) )
- - end ;
}
else
{
// Backup until non-whitespace character is found
while ( whitespace_pred : : test ( * ( end - 1 ) ) )
- - end ;
}
}
// If characters are still left between end and value (this test is only necessary if normalization is enabled)
// Create new data node
if ( ! ( Flags & parse_no_data_nodes ) )
{
xml_node < Ch > * data = this - > allocate_node ( node_data ) ;
data - > value ( value , end - value ) ;
node - > append_node ( data ) ;
}
// Add data to parent node if no data exists yet
if ( ! ( Flags & parse_no_element_values ) )
if ( * node - > value ( ) = = Ch ( ' \0 ' ) )
node - > value ( value , end - value ) ;
// Place zero terminator after value
if ( ! ( Flags & parse_no_string_terminators ) )
{
Ch ch = * text ;
* end = Ch ( ' \0 ' ) ;
return ch ; // Return character that ends data; this is required because zero terminator overwritten it
}
// Return character that ends data
return * text ;
}
// Parse CDATA
template < int Flags >
xml_node < Ch > * parse_cdata ( Ch * & text )
{
// If CDATA is disabled
if ( Flags & parse_no_data_nodes )
{
// Skip until end of cdata
while ( text [ 0 ] ! = Ch ( ' ] ' ) | | text [ 1 ] ! = Ch ( ' ] ' ) | | text [ 2 ] ! = Ch ( ' > ' ) )
{
if ( ! text [ 0 ] )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
text + = 3 ; // Skip ]]>
return 0 ; // Do not produce CDATA node
}
// Skip until end of cdata
Ch * value = text ;
while ( text [ 0 ] ! = Ch ( ' ] ' ) | | text [ 1 ] ! = Ch ( ' ] ' ) | | text [ 2 ] ! = Ch ( ' > ' ) )
{
if ( ! text [ 0 ] )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
// Create new cdata node
xml_node < Ch > * cdata = this - > allocate_node ( node_cdata ) ;
cdata - > value ( value , text - value ) ;
// Place zero terminator after value
if ( ! ( Flags & parse_no_string_terminators ) )
* text = Ch ( ' \0 ' ) ;
text + = 3 ; // Skip ]]>
return cdata ;
}
// Parse element node
template < int Flags >
xml_node < Ch > * parse_element ( Ch * & text )
{
// Create element node
xml_node < Ch > * element = this - > allocate_node ( node_element ) ;
// Extract element name
Ch * name = text ;
skip < node_name_pred , Flags > ( text ) ;
if ( text = = name )
RAPIDXML_PARSE_ERROR ( " expected element name " , text ) ;
element - > name ( name , text - name ) ;
// Skip whitespace between element name and attributes or >
skip < whitespace_pred , Flags > ( text ) ;
// Parse attributes, if any
parse_node_attributes < Flags > ( text , element ) ;
// Determine ending type
if ( * text = = Ch ( ' > ' ) )
{
+ + text ;
parse_node_contents < Flags > ( text , element ) ;
}
else if ( * text = = Ch ( ' / ' ) )
{
+ + text ;
if ( * text ! = Ch ( ' > ' ) )
RAPIDXML_PARSE_ERROR ( " expected > " , text ) ;
+ + text ;
}
else
RAPIDXML_PARSE_ERROR ( " expected > " , text ) ;
// Place zero terminator after name
if ( ! ( Flags & parse_no_string_terminators ) )
element - > name ( ) [ element - > name_size ( ) ] = Ch ( ' \0 ' ) ;
// Return parsed element
return element ;
}
// Determine node type, and parse it
template < int Flags >
xml_node < Ch > * parse_node ( Ch * & text )
{
// Parse proper node type
switch ( text [ 0 ] )
{
// <...
default :
// Parse and append element node
return parse_element < Flags > ( text ) ;
// <?...
case Ch ( ' ? ' ) :
+ + text ; // Skip ?
if ( ( text [ 0 ] = = Ch ( ' x ' ) | | text [ 0 ] = = Ch ( ' X ' ) ) & &
( text [ 1 ] = = Ch ( ' m ' ) | | text [ 1 ] = = Ch ( ' M ' ) ) & &
( text [ 2 ] = = Ch ( ' l ' ) | | text [ 2 ] = = Ch ( ' L ' ) ) & &
whitespace_pred : : test ( text [ 3 ] ) )
{
// '<?xml ' - xml declaration
text + = 4 ; // Skip 'xml '
return parse_xml_declaration < Flags > ( text ) ;
}
else
{
// Parse PI
return parse_pi < Flags > ( text ) ;
}
// <!...
case Ch ( ' ! ' ) :
// Parse proper subset of <! node
switch ( text [ 1 ] )
{
// <!-
case Ch ( ' - ' ) :
if ( text [ 2 ] = = Ch ( ' - ' ) )
{
// '<!--' - xml comment
text + = 3 ; // Skip '!--'
return parse_comment < Flags > ( text ) ;
}
break ;
// <![
case Ch ( ' [ ' ) :
if ( text [ 2 ] = = Ch ( ' C ' ) & & text [ 3 ] = = Ch ( ' D ' ) & & text [ 4 ] = = Ch ( ' A ' ) & &
text [ 5 ] = = Ch ( ' T ' ) & & text [ 6 ] = = Ch ( ' A ' ) & & text [ 7 ] = = Ch ( ' [ ' ) )
{
// '<![CDATA[' - cdata
text + = 8 ; // Skip '![CDATA['
return parse_cdata < Flags > ( text ) ;
}
break ;
// <!D
case Ch ( ' D ' ) :
if ( text [ 2 ] = = Ch ( ' O ' ) & & text [ 3 ] = = Ch ( ' C ' ) & & text [ 4 ] = = Ch ( ' T ' ) & &
text [ 5 ] = = Ch ( ' Y ' ) & & text [ 6 ] = = Ch ( ' P ' ) & & text [ 7 ] = = Ch ( ' E ' ) & &
whitespace_pred : : test ( text [ 8 ] ) )
{
// '<!DOCTYPE ' - doctype
text + = 9 ; // skip '!DOCTYPE '
return parse_doctype < Flags > ( text ) ;
}
} // switch
// Attempt to skip other, unrecognized node types starting with <!
+ + text ; // Skip !
while ( * text ! = Ch ( ' > ' ) )
{
if ( * text = = 0 )
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
+ + text ;
}
+ + text ; // Skip '>'
return 0 ; // No node recognized
}
}
// Parse contents of the node - children, data etc.
template < int Flags >
void parse_node_contents ( Ch * & text , xml_node < Ch > * node )
{
// For all children and text
while ( 1 )
{
// Skip whitespace between > and node contents
Ch * contents_start = text ; // Store start of node contents before whitespace is skipped
skip < whitespace_pred , Flags > ( text ) ;
Ch next_char = * text ;
// After data nodes, instead of continuing the loop, control jumps here.
// This is because zero termination inside parse_and_append_data() function
// would wreak havoc with the above code.
// Also, skipping whitespace after data nodes is unnecessary.
after_data_node :
// Determine what comes next: node closing, child node, data node, or 0?
switch ( next_char )
{
// Node closing or child node
case Ch ( ' < ' ) :
if ( text [ 1 ] = = Ch ( ' / ' ) )
{
// Node closing
text + = 2 ; // Skip '</'
if ( Flags & parse_validate_closing_tags )
{
// Skip and validate closing tag name
Ch * closing_name = text ;
skip < node_name_pred , Flags > ( text ) ;
if ( ! internal : : compare ( node - > name ( ) , node - > name_size ( ) , closing_name , text - closing_name , true ) )
RAPIDXML_PARSE_ERROR ( " invalid closing tag name " , text ) ;
}
else
{
// No validation, just skip name
skip < node_name_pred , Flags > ( text ) ;
}
// Skip remaining whitespace after node name
skip < whitespace_pred , Flags > ( text ) ;
if ( * text ! = Ch ( ' > ' ) )
RAPIDXML_PARSE_ERROR ( " expected > " , text ) ;
+ + text ; // Skip '>'
return ; // Node closed, finished parsing contents
}
else
{
// Child node
+ + text ; // Skip '<'
if ( xml_node < Ch > * child = parse_node < Flags > ( text ) )
node - > append_node ( child ) ;
}
break ;
// End of data - error
case Ch ( ' \0 ' ) :
RAPIDXML_PARSE_ERROR ( " unexpected end of data " , text ) ;
// Data node
default :
next_char = parse_and_append_data < Flags > ( node , text , contents_start ) ;
goto after_data_node ; // Bypass regular processing after data nodes
}
}
}
// Parse XML attributes of the node
template < int Flags >
void parse_node_attributes ( Ch * & text , xml_node < Ch > * node )
{
// For all attributes
while ( attribute_name_pred : : test ( * text ) )
{
// Extract attribute name
Ch * name = text ;
+ + text ; // Skip first character of attribute name
skip < attribute_name_pred , Flags > ( text ) ;
if ( text = = name )
RAPIDXML_PARSE_ERROR ( " expected attribute name " , name ) ;
// Create new attribute
xml_attribute < Ch > * attribute = this - > allocate_attribute ( ) ;
attribute - > name ( name , text - name ) ;
node - > append_attribute ( attribute ) ;
// Skip whitespace after attribute name
skip < whitespace_pred , Flags > ( text ) ;
// Skip =
if ( * text ! = Ch ( ' = ' ) )
RAPIDXML_PARSE_ERROR ( " expected = " , text ) ;
+ + text ;
// Add terminating zero after name
if ( ! ( Flags & parse_no_string_terminators ) )
attribute - > name ( ) [ attribute - > name_size ( ) ] = 0 ;
// Skip whitespace after =
skip < whitespace_pred , Flags > ( text ) ;
// Skip quote and remember if it was ' or "
Ch quote = * text ;
if ( quote ! = Ch ( ' \' ' ) & & quote ! = Ch ( ' " ' ) )
RAPIDXML_PARSE_ERROR ( " expected ' or \" " , text ) ;
+ + text ;
// Extract attribute value and expand char refs in it
Ch * value = text , * end ;
const int AttFlags = Flags & ~ parse_normalize_whitespace ; // No whitespace normalization in attributes
if ( quote = = Ch ( ' \' ' ) )
end = skip_and_expand_character_refs < attribute_value_pred < Ch ( ' \' ' ) > , attribute_value_pure_pred < Ch ( ' \' ' ) > , AttFlags > ( text ) ;
else
end = skip_and_expand_character_refs < attribute_value_pred < Ch ( ' " ' ) > , attribute_value_pure_pred < Ch ( ' " ' ) > , AttFlags > ( text ) ;
// Set attribute value
attribute - > value ( value , end - value ) ;
// Make sure that end quote is present
if ( * text ! = quote )
RAPIDXML_PARSE_ERROR ( " expected ' or \" " , text ) ;
+ + text ; // Skip quote
// Add terminating zero after value
if ( ! ( Flags & parse_no_string_terminators ) )
attribute - > value ( ) [ attribute - > value_size ( ) ] = 0 ;
// Skip whitespace after attribute value
skip < whitespace_pred , Flags > ( text ) ;
}
}
} ;
//! \cond internal
namespace internal
{
// Whitespace (space \n \r \t)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_whitespace [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 0 , 0 , // 0
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 1
1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 2
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 3
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 4
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 7
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 8
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 9
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // A
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // B
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // C
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // D
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // E
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // F
} ;
// Node name (anything but space \n \r \t / > ? \0)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_node_name [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Text (i.e. PCDATA) (anything but < \0)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_text [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Text (i.e. PCDATA) that does not require processing when ws normalization is disabled
// (anything but < \0 &)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_text_pure_no_ws [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled
// (anything but < \0 & space \n \r \t)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_text_pure_with_ws [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Attribute name (anything but space \n \r \t / < > = ? ! \0)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_attribute_name [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Attribute data with single quote (anything but ' \0)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_attribute_data_1 [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Attribute data with single quote that does not require processing (anything but ' \0 &)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_attribute_data_1_pure [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Attribute data with double quote (anything but " \0)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_attribute_data_2 [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Attribute data with double quote that does not require processing (anything but " \0 &)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_attribute_data_2_pure [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 1
1 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 2
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 3
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 4
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 5
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 6
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 7
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 8
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 9
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // A
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // B
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // C
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // D
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // E
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 // F
} ;
// Digits (dec and hex, 255 denotes end of numeric character reference)
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_digits [ 256 ] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 0
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 1
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 2
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 255 , 255 , 255 , 255 , 255 , 255 , // 3
255 , 10 , 11 , 12 , 13 , 14 , 15 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 4
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 5
255 , 10 , 11 , 12 , 13 , 14 , 15 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 6
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 7
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 8
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // 9
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // A
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // B
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // C
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // D
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , // E
255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 // F
} ;
// Upper case conversion
template < int Dummy >
const unsigned char lookup_tables < Dummy > : : lookup_upcase [ 256 ] =
{
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , // 0
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , // 1
32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , // 2
48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 , // 3
64 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , // 4
80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 93 , 94 , 95 , // 5
96 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , // 6
80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 123 , 124 , 125 , 126 , 127 , // 7
128 , 129 , 130 , 131 , 132 , 133 , 134 , 135 , 136 , 137 , 138 , 139 , 140 , 141 , 142 , 143 , // 8
144 , 145 , 146 , 147 , 148 , 149 , 150 , 151 , 152 , 153 , 154 , 155 , 156 , 157 , 158 , 159 , // 9
160 , 161 , 162 , 163 , 164 , 165 , 166 , 167 , 168 , 169 , 170 , 171 , 172 , 173 , 174 , 175 , // A
176 , 177 , 178 , 179 , 180 , 181 , 182 , 183 , 184 , 185 , 186 , 187 , 188 , 189 , 190 , 191 , // B
192 , 193 , 194 , 195 , 196 , 197 , 198 , 199 , 200 , 201 , 202 , 203 , 204 , 205 , 206 , 207 , // C
208 , 209 , 210 , 211 , 212 , 213 , 214 , 215 , 216 , 217 , 218 , 219 , 220 , 221 , 222 , 223 , // D
224 , 225 , 226 , 227 , 228 , 229 , 230 , 231 , 232 , 233 , 234 , 235 , 236 , 237 , 238 , 239 , // E
240 , 241 , 242 , 243 , 244 , 245 , 246 , 247 , 248 , 249 , 250 , 251 , 252 , 253 , 254 , 255 // F
} ;
}
//! \endcond
}
// Undefine internal macros
# undef RAPIDXML_PARSE_ERROR
// On MSVC, restore warnings state
# ifdef _MSC_VER
# pragma warning(pop)
# endif
# endif