Merge branch 'master' into compact
This commit is contained in:
commit
d7ac5e2e0c
@ -1665,20 +1665,20 @@ The constructor copies the specified range and sets the specified type. The obje
|
||||
|
||||
[section:select Selecting nodes via XPath expression]
|
||||
|
||||
[#xml_node::select_single_node][#xml_node::select_nodes]
|
||||
[#xml_node::select_node][#xml_node::select_nodes]
|
||||
If you want to select nodes that match some XPath expression, you can do it with the following functions:
|
||||
|
||||
xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
|
||||
`select_nodes` function compiles the expression and then executes it with the node as a context node, and returns the resulting node set. `select_single_node` returns only the first node in document order from the result, and is equivalent to calling `select_nodes(query).first()`. If the XPath expression does not match anything, or the node handle is null, `select_nodes` returns an empty set, and `select_single_node` returns null XPath node.
|
||||
`select_nodes` function compiles the expression and then executes it with the node as a context node, and returns the resulting node set. `select_node` returns only the first node in document order from the result, and is equivalent to calling `select_nodes(query).first()`. If the XPath expression does not match anything, or the node handle is null, `select_nodes` returns an empty set, and `select_node` returns null XPath node.
|
||||
|
||||
If exception handling is not disabled, both functions throw [link xpath_exception] if the query can not be compiled or if it returns a value with type other than node set; see [sref manual.xpath.errors] for details.
|
||||
|
||||
[#xml_node::select_single_node_precomp][#xml_node::select_nodes_precomp]
|
||||
[#xml_node::select_node_precomp][#xml_node::select_nodes_precomp]
|
||||
While compiling expressions is fast, the compilation time can introduce a significant overhead if the same expression is used many times on small subtrees. If you're doing many similar queries, consider compiling them into query objects (see [sref manual.xpath.query] for further reference). Once you get a compiled query object, you can pass it to select functions instead of an expression string:
|
||||
|
||||
xpath_node xml_node::select_single_node(const xpath_query& query) const;
|
||||
xpath_node xml_node::select_node(const xpath_query& query) const;
|
||||
xpath_node_set xml_node::select_nodes(const xpath_query& query) const;
|
||||
|
||||
If exception handling is not disabled, both functions throw [link xpath_exception] if the query returns a value with type other than node set.
|
||||
@ -1711,17 +1711,18 @@ The expression is compiled and the compiled representation is stored in the new
|
||||
|
||||
xpath_value_type xpath_query::return_type() const;
|
||||
|
||||
[#xpath_query::evaluate_boolean][#xpath_query::evaluate_number][#xpath_query::evaluate_string][#xpath_query::evaluate_node_set]
|
||||
[#xpath_query::evaluate_boolean][#xpath_query::evaluate_number][#xpath_query::evaluate_string][#xpath_query::evaluate_node_set][#xpath_query::evaluate_node]
|
||||
You can evaluate the query using one of the following functions:
|
||||
|
||||
bool xpath_query::evaluate_boolean(const xpath_node& n) const;
|
||||
double xpath_query::evaluate_number(const xpath_node& n) const;
|
||||
string_t xpath_query::evaluate_string(const xpath_node& n) const;
|
||||
xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const;
|
||||
xpath_node xpath_query::evaluate_node(const xpath_node& n) const;
|
||||
|
||||
All functions take the context node as an argument, compute the expression and return the result, converted to the requested type. According to XPath specification, value of any type can be converted to boolean, number or string value, but no type other than node set can be converted to node set. Because of this, `evaluate_boolean`, `evaluate_number` and `evaluate_string` always return a result, but `evaluate_node_set` results in an error if the return type is not node set (see [sref manual.xpath.errors]).
|
||||
All functions take the context node as an argument, compute the expression and return the result, converted to the requested type. According to XPath specification, value of any type can be converted to boolean, number or string value, but no type other than node set can be converted to node set. Because of this, `evaluate_boolean`, `evaluate_number` and `evaluate_string` always return a result, but `evaluate_node_set` and `evaluate_node` result in an error if the return type is not node set (see [sref manual.xpath.errors]).
|
||||
|
||||
[note Calling `node.select_nodes("query")` is equivalent to calling `xpath_query("query").evaluate_node_set(node)`.]
|
||||
[note Calling `node.select_nodes("query")` is equivalent to calling `xpath_query("query").evaluate_node_set(node)`. Calling `node.select_node("query")` is equivalent to calling `xpath_query("query").evaluate_node(node)`.]
|
||||
|
||||
[#xpath_query::evaluate_string_buffer]
|
||||
Note that `evaluate_string` function returns the STL string; as such, it's not available in [link PUGIXML_NO_STL] mode and also usually allocates memory. There is another string evaluation function:
|
||||
@ -1744,10 +1745,10 @@ This is an example of using query objects ([@samples/xpath_query.cpp]):
|
||||
|
||||
XPath queries may contain references to variables; this is useful if you want to use queries that depend on some dynamic parameter without manually preparing the complete query string, or if you want to reuse the same query object for similar queries.
|
||||
|
||||
Variable references have the form '''<code><phrase role="identifier">$name</phrase></code>'''; in order to use them, you have to provide a variable set, which includes all variables present in the query with correct types. This set is passed to `xpath_query` constructor or to `select_nodes`/`select_single_node` functions:
|
||||
Variable references have the form '''<code><phrase role="identifier">$name</phrase></code>'''; in order to use them, you have to provide a variable set, which includes all variables present in the query with correct types. This set is passed to `xpath_query` constructor or to `select_nodes`/`select_node` functions:
|
||||
|
||||
explicit xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables = 0);
|
||||
xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
|
||||
If you're using query objects, you can change the variable values before `evaluate`/`select` calls to change the query behavior.
|
||||
@ -2481,8 +2482,8 @@ Classes:
|
||||
* `void `[link xml_node::print_stream print]`(std::wostream& os, const char_t* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0) const;`
|
||||
[lbr]
|
||||
|
||||
* `xpath_node `[link xml_node::select_single_node select_single_node]`(const char_t* query, xpath_variable_set* variables = 0) const;`
|
||||
* `xpath_node `[link xml_node::select_single_node_precomp select_single_node]`(const xpath_query& query) const;`
|
||||
* `xpath_node `[link xml_node::select_node select_node]`(const char_t* query, xpath_variable_set* variables = 0) const;`
|
||||
* `xpath_node `[link xml_node::select_node_precomp select_node]`(const xpath_query& query) const;`
|
||||
* `xpath_node_set `[link xml_node::select_nodes select_nodes]`(const char_t* query, xpath_variable_set* variables = 0) const;`
|
||||
* `xpath_node_set `[link xml_node::select_nodes_precomp select_nodes]`(const xpath_query& query) const;`
|
||||
[lbr]
|
||||
@ -2620,6 +2621,7 @@ Classes:
|
||||
* `string_t `[link xpath_query::evaluate_string evaluate_string]`(const xpath_node& n) const;`
|
||||
* `size_t `[link xpath_query::evaluate_string_buffer evaluate_string]`(char_t* buffer, size_t capacity, const xpath_node& n) const;`
|
||||
* `xpath_node_set `[link xpath_query::evaluate_node_set evaluate_node_set]`(const xpath_node& n) const;`
|
||||
* `xpath_node `[link xpath_query::evaluate_node evaluate_node]`(const xpath_node& n) const;`
|
||||
[lbr]
|
||||
|
||||
* `xpath_value_type `[link xpath_query::return_type return_type]`() const;`
|
||||
|
||||
@ -18,7 +18,7 @@ int main()
|
||||
std::cout << node.node().attribute("Filename").value() << "\n";
|
||||
}
|
||||
|
||||
pugi::xpath_node build_tool = doc.select_single_node("//Tool[contains(Description, 'build system')]");
|
||||
pugi::xpath_node build_tool = doc.select_node("//Tool[contains(Description, 'build system')]");
|
||||
|
||||
if (build_tool)
|
||||
std::cout << "Build tool: " << build_tool.node().attribute("Filename").value() << "\n";
|
||||
|
||||
@ -27,7 +27,7 @@ int main()
|
||||
std::cout << "Local tool: ";
|
||||
tools_local[0].node().print(std::cout);
|
||||
|
||||
// You can pass the context directly to select_nodes/select_single_node
|
||||
// You can pass the context directly to select_nodes/select_node
|
||||
pugi::xpath_node_set tools_local_imm = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote = string($remote)]", &vars);
|
||||
|
||||
std::cout << "Local tool imm: ";
|
||||
|
||||
372
src/pugixml.cpp
372
src/pugixml.cpp
@ -3920,6 +3920,31 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
|
||||
{
|
||||
writer.write('<', '!', '-', '-');
|
||||
|
||||
while (*s)
|
||||
{
|
||||
const char_t* prev = s;
|
||||
|
||||
// look for -\0 or -- sequence - we can't output it since -- is illegal in comment body
|
||||
while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;
|
||||
|
||||
writer.write_buffer(prev, static_cast<size_t>(s - prev));
|
||||
|
||||
if (*s)
|
||||
{
|
||||
assert(*s == '-');
|
||||
|
||||
writer.write('-', ' ');
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write('-', '-', '>');
|
||||
}
|
||||
|
||||
PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
|
||||
{
|
||||
const char_t* default_name = PUGIXML_TEXT(":anonymous");
|
||||
@ -4019,22 +4044,15 @@ PUGI__NS_BEGIN
|
||||
break;
|
||||
|
||||
case node_comment:
|
||||
writer.write('<', '!', '-', '-');
|
||||
writer.write_string(node.value());
|
||||
writer.write('-', '-', '>');
|
||||
node_output_comment(writer, node.value());
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_pi:
|
||||
case node_declaration:
|
||||
writer.write('<', '?');
|
||||
writer.write_string(node.name()[0] ? node.name() : default_name);
|
||||
|
||||
if (node.type() == node_declaration)
|
||||
{
|
||||
node_output_attributes(writer, node, flags);
|
||||
}
|
||||
else if (node.value()[0])
|
||||
if (node.value()[0])
|
||||
{
|
||||
writer.write(' ');
|
||||
writer.write_string(node.value());
|
||||
@ -4044,6 +4062,14 @@ PUGI__NS_BEGIN
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_declaration:
|
||||
writer.write('<', '?');
|
||||
writer.write_string(node.name()[0] ? node.name() : default_name);
|
||||
node_output_attributes(writer, node, flags);
|
||||
writer.write('?', '>');
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_doctype:
|
||||
writer.write('<', '!', 'D', 'O', 'C');
|
||||
writer.write('T', 'Y', 'P', 'E');
|
||||
@ -7999,6 +8025,11 @@ PUGI__NS_BEGIN
|
||||
*write = 0;
|
||||
}
|
||||
|
||||
inline bool is_xpath_attribute(const char_t* name)
|
||||
{
|
||||
return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':'));
|
||||
}
|
||||
|
||||
struct xpath_variable_boolean: xpath_variable
|
||||
{
|
||||
xpath_variable_boolean(): value(false)
|
||||
@ -8743,7 +8774,6 @@ PUGI__NS_BEGIN
|
||||
ast_func_normalize_space_0, // normalize-space()
|
||||
ast_func_normalize_space_1, // normalize-space(left)
|
||||
ast_func_translate, // translate(left, right, third)
|
||||
ast_func_translate_table, // translate(left, right, third) where right/third are constants
|
||||
ast_func_boolean, // boolean(left)
|
||||
ast_func_not, // not(left)
|
||||
ast_func_true, // true()
|
||||
@ -8756,7 +8786,10 @@ PUGI__NS_BEGIN
|
||||
ast_func_ceiling, // ceiling(left)
|
||||
ast_func_round, // round(left)
|
||||
ast_step, // process set left with step
|
||||
ast_step_root // select root node
|
||||
ast_step_root, // select root node
|
||||
|
||||
ast_opt_translate_table, // translate(left, right, third) where right/third are constants
|
||||
ast_opt_compare_attribute // @name = 'string'
|
||||
};
|
||||
|
||||
enum axis_t
|
||||
@ -8789,6 +8822,13 @@ PUGI__NS_BEGIN
|
||||
nodetest_all_in_namespace
|
||||
};
|
||||
|
||||
enum nodeset_eval_t
|
||||
{
|
||||
nodeset_eval_all,
|
||||
nodeset_eval_any,
|
||||
nodeset_eval_first
|
||||
};
|
||||
|
||||
template <axis_t N> struct axis_to_type
|
||||
{
|
||||
static const axis_t axis;
|
||||
@ -8822,7 +8862,7 @@ PUGI__NS_BEGIN
|
||||
xpath_variable* variable;
|
||||
// node test for ast_step (node name/namespace/node type/pi target)
|
||||
const char_t* nodetest;
|
||||
// table for ast_func_translate_table
|
||||
// table for ast_opt_translate_table
|
||||
const unsigned char* table;
|
||||
} _data;
|
||||
|
||||
@ -8853,8 +8893,8 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
|
||||
for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
|
||||
@ -8882,7 +8922,7 @@ PUGI__NS_BEGIN
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
double l = lhs->eval_number(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
|
||||
{
|
||||
@ -8899,7 +8939,7 @@ PUGI__NS_BEGIN
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_string l = lhs->eval_string(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
|
||||
{
|
||||
@ -8927,8 +8967,8 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
|
||||
{
|
||||
@ -8952,7 +8992,7 @@ PUGI__NS_BEGIN
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
double l = lhs->eval_number(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
|
||||
{
|
||||
@ -8968,7 +9008,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
|
||||
double r = rhs->eval_number(c, stack);
|
||||
|
||||
for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
|
||||
@ -8988,7 +9028,7 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
|
||||
static void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
|
||||
{
|
||||
assert(ns.size() >= first);
|
||||
|
||||
@ -9024,89 +9064,120 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
void step_push(xpath_node_set_raw& ns, const xml_attribute a, const xml_node parent, xpath_allocator* alloc)
|
||||
bool step_push(xpath_node_set_raw& ns, const xml_attribute a, const xml_node parent, xpath_allocator* alloc)
|
||||
{
|
||||
if (!a) return;
|
||||
if (!a) return false;
|
||||
|
||||
const char_t* name = a.name();
|
||||
|
||||
// There are no attribute nodes corresponding to attributes that declare namespaces
|
||||
// That is, "xmlns:..." or "xmlns"
|
||||
if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;
|
||||
|
||||
switch (_test)
|
||||
{
|
||||
case nodetest_name:
|
||||
if (strequal(name, _data.nodetest))
|
||||
if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
|
||||
{
|
||||
ns.push_back(xpath_node(a, parent), alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_type_node:
|
||||
case nodetest_all:
|
||||
ns.push_back(xpath_node(a, parent), alloc);
|
||||
if (is_xpath_attribute(name))
|
||||
{
|
||||
ns.push_back(xpath_node(a, parent), alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_all_in_namespace:
|
||||
if (starts_with(name, _data.nodetest))
|
||||
if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
|
||||
{
|
||||
ns.push_back(xpath_node(a, parent), alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void step_push(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc)
|
||||
bool step_push(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc)
|
||||
{
|
||||
if (!n) return;
|
||||
if (!n) return false;
|
||||
|
||||
switch (_test)
|
||||
{
|
||||
case nodetest_name:
|
||||
if (n.type() == node_element && strequal(n.name(), _data.nodetest))
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_type_node:
|
||||
ns.push_back(n, alloc);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case nodetest_type_comment:
|
||||
if (n.type() == node_comment)
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_type_text:
|
||||
if (n.type() == node_pcdata || n.type() == node_cdata)
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_type_pi:
|
||||
if (n.type() == node_pi)
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_pi:
|
||||
if (n.type() == node_pi && strequal(n.name(), _data.nodetest))
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_all:
|
||||
if (n.type() == node_element)
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case nodetest_all_in_namespace:
|
||||
if (n.type() == node_element && starts_with(n.name(), _data.nodetest))
|
||||
{
|
||||
ns.push_back(n, alloc);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!"Unknown axis");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc, T)
|
||||
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc, bool once, T)
|
||||
{
|
||||
const axis_t axis = T::axis;
|
||||
|
||||
@ -9115,7 +9186,8 @@ PUGI__NS_BEGIN
|
||||
case axis_attribute:
|
||||
{
|
||||
for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
|
||||
step_push(ns, a, n, alloc);
|
||||
if (step_push(ns, a, n, alloc) & once)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -9123,7 +9195,8 @@ PUGI__NS_BEGIN
|
||||
case axis_child:
|
||||
{
|
||||
for (xml_node c = n.first_child(); c; c = c.next_sibling())
|
||||
step_push(ns, c, alloc);
|
||||
if (step_push(ns, c, alloc) & once)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -9132,13 +9205,15 @@ PUGI__NS_BEGIN
|
||||
case axis_descendant_or_self:
|
||||
{
|
||||
if (axis == axis_descendant_or_self)
|
||||
step_push(ns, n, alloc);
|
||||
if (step_push(ns, n, alloc) & once)
|
||||
return;
|
||||
|
||||
xml_node cur = n.first_child();
|
||||
|
||||
while (cur && cur != n)
|
||||
{
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
|
||||
if (cur.first_child())
|
||||
cur = cur.first_child();
|
||||
@ -9159,7 +9234,8 @@ PUGI__NS_BEGIN
|
||||
case axis_following_sibling:
|
||||
{
|
||||
for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
|
||||
step_push(ns, c, alloc);
|
||||
if (step_push(ns, c, alloc) & once)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -9167,7 +9243,8 @@ PUGI__NS_BEGIN
|
||||
case axis_preceding_sibling:
|
||||
{
|
||||
for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
|
||||
step_push(ns, c, alloc);
|
||||
if (step_push(ns, c, alloc) & once)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -9180,9 +9257,10 @@ PUGI__NS_BEGIN
|
||||
while (cur && !cur.next_sibling()) cur = cur.parent();
|
||||
cur = cur.next_sibling();
|
||||
|
||||
for (;;)
|
||||
while (cur)
|
||||
{
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
|
||||
if (cur.first_child())
|
||||
cur = cur.first_child();
|
||||
@ -9207,14 +9285,15 @@ PUGI__NS_BEGIN
|
||||
while (cur && !cur.previous_sibling()) cur = cur.parent();
|
||||
cur = cur.previous_sibling();
|
||||
|
||||
for (;;)
|
||||
while (cur)
|
||||
{
|
||||
if (cur.last_child())
|
||||
cur = cur.last_child();
|
||||
else
|
||||
{
|
||||
// leaf node, can't be ancestor
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
|
||||
if (cur.previous_sibling())
|
||||
cur = cur.previous_sibling();
|
||||
@ -9225,7 +9304,9 @@ PUGI__NS_BEGIN
|
||||
cur = cur.parent();
|
||||
if (!cur) break;
|
||||
|
||||
if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);
|
||||
if (!node_is_ancestor(cur, n))
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
}
|
||||
while (!cur.previous_sibling());
|
||||
|
||||
@ -9243,13 +9324,15 @@ PUGI__NS_BEGIN
|
||||
case axis_ancestor_or_self:
|
||||
{
|
||||
if (axis == axis_ancestor_or_self)
|
||||
step_push(ns, n, alloc);
|
||||
if (step_push(ns, n, alloc) & once)
|
||||
return;
|
||||
|
||||
xml_node cur = n.parent();
|
||||
|
||||
while (cur)
|
||||
{
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
|
||||
cur = cur.parent();
|
||||
}
|
||||
@ -9276,7 +9359,7 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute a, const xml_node p, xpath_allocator* alloc, T v)
|
||||
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute a, const xml_node p, xpath_allocator* alloc, bool once, T v)
|
||||
{
|
||||
const axis_t axis = T::axis;
|
||||
|
||||
@ -9286,13 +9369,15 @@ PUGI__NS_BEGIN
|
||||
case axis_ancestor_or_self:
|
||||
{
|
||||
if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
|
||||
step_push(ns, a, p, alloc);
|
||||
if (step_push(ns, a, p, alloc) & once)
|
||||
return;
|
||||
|
||||
xml_node cur = p;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
|
||||
cur = cur.parent();
|
||||
}
|
||||
@ -9327,7 +9412,8 @@ PUGI__NS_BEGIN
|
||||
if (!cur) break;
|
||||
}
|
||||
|
||||
step_push(ns, cur, alloc);
|
||||
if (step_push(ns, cur, alloc) & once)
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -9343,7 +9429,7 @@ PUGI__NS_BEGIN
|
||||
case axis_preceding:
|
||||
{
|
||||
// preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
|
||||
step_fill(ns, p, alloc, v);
|
||||
step_fill(ns, p, alloc, once, v);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -9351,18 +9437,24 @@ PUGI__NS_BEGIN
|
||||
assert(!"Unimplemented axis");
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)
|
||||
|
||||
template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
|
||||
{
|
||||
const axis_t axis = T::axis;
|
||||
bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
|
||||
bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
|
||||
bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
|
||||
|
||||
bool once =
|
||||
(axis == axis_attribute && _test == nodetest_name)
|
||||
? true
|
||||
: !_right && (axis_reverse ? eval == nodeset_eval_any : eval != nodeset_eval_all);
|
||||
|
||||
xpath_node_set_raw ns;
|
||||
ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
|
||||
ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
|
||||
|
||||
if (_left)
|
||||
{
|
||||
xpath_node_set_raw s = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
// self axis preserves the original order
|
||||
if (axis == axis_self) ns.set_type(s.type());
|
||||
@ -9375,9 +9467,9 @@ PUGI__NS_BEGIN
|
||||
if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
|
||||
|
||||
if (it->node())
|
||||
step_fill(ns, it->node(), stack.result, v);
|
||||
else if (attributes)
|
||||
step_fill(ns, it->attribute(), it->parent(), stack.result, v);
|
||||
step_fill(ns, it->node(), stack.result, once, v);
|
||||
else if (axis_has_attributes && it->attribute() && it->parent())
|
||||
step_fill(ns, it->attribute(), it->parent(), stack.result, once, v);
|
||||
|
||||
apply_predicates(ns, size, stack);
|
||||
}
|
||||
@ -9385,9 +9477,9 @@ PUGI__NS_BEGIN
|
||||
else
|
||||
{
|
||||
if (c.n.node())
|
||||
step_fill(ns, c.n.node(), stack.result, v);
|
||||
else if (attributes)
|
||||
step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);
|
||||
step_fill(ns, c.n.node(), stack.result, once, v);
|
||||
else if (axis_has_attributes && c.n.attribute() && c.n.parent())
|
||||
step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, once, v);
|
||||
|
||||
apply_predicates(ns, 0, stack);
|
||||
}
|
||||
@ -9533,6 +9625,15 @@ PUGI__NS_BEGIN
|
||||
return false;
|
||||
}
|
||||
|
||||
case ast_opt_compare_attribute:
|
||||
{
|
||||
const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();
|
||||
|
||||
xml_attribute attr = c.n.node().attribute(_left->_data.nodetest);
|
||||
|
||||
return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());
|
||||
}
|
||||
|
||||
case ast_variable:
|
||||
{
|
||||
assert(_rettype == _data.variable->type());
|
||||
@ -9561,7 +9662,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
return !eval_node_set(c, stack).empty();
|
||||
return !eval_node_set(c, stack, nodeset_eval_any).empty();
|
||||
}
|
||||
|
||||
default:
|
||||
@ -9607,7 +9708,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
return static_cast<double>(_left->eval_node_set(c, stack).size());
|
||||
return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());
|
||||
}
|
||||
|
||||
case ast_func_string_length_0:
|
||||
@ -9640,7 +9741,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
double r = 0;
|
||||
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
|
||||
{
|
||||
@ -9776,7 +9877,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
|
||||
xpath_node na = ns.first();
|
||||
|
||||
return xpath_string::from_const(local_name(na));
|
||||
@ -9793,7 +9894,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
|
||||
xpath_node na = ns.first();
|
||||
|
||||
return xpath_string::from_const(qualified_name(na));
|
||||
@ -9810,7 +9911,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xpath_allocator_capture cr(stack.result);
|
||||
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
|
||||
xpath_node na = ns.first();
|
||||
|
||||
return xpath_string::from_const(namespace_uri(na));
|
||||
@ -9938,7 +10039,7 @@ PUGI__NS_BEGIN
|
||||
return s;
|
||||
}
|
||||
|
||||
case ast_func_translate_table:
|
||||
case ast_opt_translate_table:
|
||||
{
|
||||
xpath_string s = _left->eval_string(c, stack);
|
||||
|
||||
@ -9973,7 +10074,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
xpath_stack swapped_stack = {stack.temp, stack.result};
|
||||
|
||||
xpath_node_set_raw ns = eval_node_set(c, swapped_stack);
|
||||
xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);
|
||||
return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
|
||||
}
|
||||
|
||||
@ -9985,7 +10086,7 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)
|
||||
xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval)
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
@ -9995,8 +10096,8 @@ PUGI__NS_BEGIN
|
||||
|
||||
xpath_stack swapped_stack = {stack.temp, stack.result};
|
||||
|
||||
xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack);
|
||||
xpath_node_set_raw rs = _right->eval_node_set(c, stack);
|
||||
xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
|
||||
xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
|
||||
|
||||
// we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
|
||||
rs.set_type(xpath_node_set::type_unsorted);
|
||||
@ -10010,7 +10111,7 @@ PUGI__NS_BEGIN
|
||||
case ast_filter:
|
||||
case ast_filter_posinv:
|
||||
{
|
||||
xpath_node_set_raw set = _left->eval_node_set(c, stack);
|
||||
xpath_node_set_raw set = _left->eval_node_set(c, stack, nodeset_eval_all);
|
||||
|
||||
// either expression is a number or it contains position() call; sort by document order
|
||||
if (_type == ast_filter) set.sort_do();
|
||||
@ -10028,44 +10129,44 @@ PUGI__NS_BEGIN
|
||||
switch (_axis)
|
||||
{
|
||||
case axis_ancestor:
|
||||
return step_do(c, stack, axis_to_type<axis_ancestor>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_ancestor>());
|
||||
|
||||
case axis_ancestor_or_self:
|
||||
return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());
|
||||
|
||||
case axis_attribute:
|
||||
return step_do(c, stack, axis_to_type<axis_attribute>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_attribute>());
|
||||
|
||||
case axis_child:
|
||||
return step_do(c, stack, axis_to_type<axis_child>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_child>());
|
||||
|
||||
case axis_descendant:
|
||||
return step_do(c, stack, axis_to_type<axis_descendant>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_descendant>());
|
||||
|
||||
case axis_descendant_or_self:
|
||||
return step_do(c, stack, axis_to_type<axis_descendant_or_self>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());
|
||||
|
||||
case axis_following:
|
||||
return step_do(c, stack, axis_to_type<axis_following>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_following>());
|
||||
|
||||
case axis_following_sibling:
|
||||
return step_do(c, stack, axis_to_type<axis_following_sibling>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_following_sibling>());
|
||||
|
||||
case axis_namespace:
|
||||
// namespaced axis is not supported
|
||||
return xpath_node_set_raw();
|
||||
|
||||
case axis_parent:
|
||||
return step_do(c, stack, axis_to_type<axis_parent>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_parent>());
|
||||
|
||||
case axis_preceding:
|
||||
return step_do(c, stack, axis_to_type<axis_preceding>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_preceding>());
|
||||
|
||||
case axis_preceding_sibling:
|
||||
return step_do(c, stack, axis_to_type<axis_preceding_sibling>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());
|
||||
|
||||
case axis_self:
|
||||
return step_do(c, stack, axis_to_type<axis_self>());
|
||||
return step_do(c, stack, eval, axis_to_type<axis_self>());
|
||||
|
||||
default:
|
||||
assert(!"Unknown axis");
|
||||
@ -10120,12 +10221,17 @@ PUGI__NS_BEGIN
|
||||
|
||||
// Replace descendant-or-self::node()/child::foo with descendant::foo
|
||||
// The former is a full form of //foo, the latter is much faster since it executes the node test immediately
|
||||
// Do a similar kind of replacement for self/descendant/descendant-or-self axes
|
||||
// Note that we only replace positionally invariant steps (//foo[1] != /descendant::foo[1])
|
||||
if (_type == ast_step && _axis == axis_child && _left &&
|
||||
if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
|
||||
_left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
|
||||
is_posinv_step())
|
||||
{
|
||||
_axis = axis_descendant;
|
||||
if (_axis == axis_child || _axis == axis_descendant)
|
||||
_axis = axis_descendant;
|
||||
else
|
||||
_axis = axis_descendant_or_self;
|
||||
|
||||
_left = _left->_left;
|
||||
}
|
||||
|
||||
@ -10136,10 +10242,18 @@ PUGI__NS_BEGIN
|
||||
|
||||
if (table)
|
||||
{
|
||||
_type = ast_func_translate_table;
|
||||
_type = ast_opt_translate_table;
|
||||
_data.table = table;
|
||||
}
|
||||
}
|
||||
|
||||
// Use optimized path for @attr = 'value' or @attr = $value
|
||||
if (_type == ast_op_equal &&
|
||||
_left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
|
||||
(_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->rettype() == xpath_type_string)))
|
||||
{
|
||||
_type = ast_opt_compare_attribute;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_posinv_expr() const
|
||||
@ -10147,6 +10261,7 @@ PUGI__NS_BEGIN
|
||||
switch (_type)
|
||||
{
|
||||
case ast_func_position:
|
||||
case ast_func_last:
|
||||
return false;
|
||||
|
||||
case ast_string_constant:
|
||||
@ -11074,6 +11189,25 @@ PUGI__NS_BEGIN
|
||||
|
||||
return impl->root->eval_string(c, sd.stack);
|
||||
}
|
||||
|
||||
PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
|
||||
{
|
||||
if (!impl) return 0;
|
||||
|
||||
if (impl->root->rettype() != xpath_type_node_set)
|
||||
{
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
return 0;
|
||||
#else
|
||||
xpath_parse_result res;
|
||||
res.error = "Expression does not evaluate to node set";
|
||||
|
||||
throw xpath_exception(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
return impl->root;
|
||||
}
|
||||
PUGI__NS_END
|
||||
|
||||
namespace pugi
|
||||
@ -11575,22 +11709,9 @@ namespace pugi
|
||||
|
||||
PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
|
||||
{
|
||||
if (!_impl) return xpath_node_set();
|
||||
impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
|
||||
if (!root) return xpath_node_set();
|
||||
|
||||
impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;
|
||||
|
||||
if (root->rettype() != xpath_type_node_set)
|
||||
{
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
return xpath_node_set();
|
||||
#else
|
||||
xpath_parse_result res;
|
||||
res.error = "Expression does not evaluate to node set";
|
||||
|
||||
throw xpath_exception(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
impl::xpath_context c(n, 1, 1);
|
||||
impl::xpath_stack_data sd;
|
||||
|
||||
@ -11598,11 +11719,28 @@ namespace pugi
|
||||
if (setjmp(sd.error_handler)) return xpath_node_set();
|
||||
#endif
|
||||
|
||||
impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack);
|
||||
impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
|
||||
|
||||
return xpath_node_set(r.begin(), r.end(), r.type());
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const
|
||||
{
|
||||
impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
|
||||
if (!root) return xpath_node();
|
||||
|
||||
impl::xpath_context c(n, 1, 1);
|
||||
impl::xpath_stack_data sd;
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
if (setjmp(sd.error_handler)) return xpath_node();
|
||||
#endif
|
||||
|
||||
impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);
|
||||
|
||||
return r.first();
|
||||
}
|
||||
|
||||
PUGI__FN const xpath_parse_result& xpath_query::result() const
|
||||
{
|
||||
return _result;
|
||||
@ -11622,16 +11760,15 @@ namespace pugi
|
||||
return !_impl;
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
|
||||
PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
|
||||
{
|
||||
xpath_query q(query, variables);
|
||||
return select_single_node(q);
|
||||
return select_node(q);
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
|
||||
PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
|
||||
{
|
||||
xpath_node_set s = query.evaluate_node_set(*this);
|
||||
return s.empty() ? xpath_node() : s.first();
|
||||
return query.evaluate_node(*this);
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
|
||||
@ -11644,6 +11781,17 @@ namespace pugi
|
||||
{
|
||||
return query.evaluate_node_set(*this);
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
|
||||
{
|
||||
xpath_query q(query, variables);
|
||||
return select_single_node(q);
|
||||
}
|
||||
|
||||
PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
|
||||
{
|
||||
return query.evaluate_node(*this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -585,12 +585,17 @@ namespace pugi
|
||||
|
||||
#ifndef PUGIXML_NO_XPATH
|
||||
// Select single node by evaluating XPath query. Returns first node from the resulting node set.
|
||||
xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node select_single_node(const xpath_query& query) const;
|
||||
xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node select_node(const xpath_query& query) const;
|
||||
|
||||
// Select node set by evaluating XPath query
|
||||
xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node_set select_nodes(const xpath_query& query) const;
|
||||
|
||||
// (deprecated: use select_node instead) Select single node by evaluating XPath query.
|
||||
xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
|
||||
xpath_node select_single_node(const xpath_query& query) const;
|
||||
|
||||
#endif
|
||||
|
||||
// Print subtree using a writer object
|
||||
@ -1134,6 +1139,12 @@ namespace pugi
|
||||
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
|
||||
xpath_node_set evaluate_node_set(const xpath_node& n) const;
|
||||
|
||||
// Evaluate expression as node set in the specified context.
|
||||
// Return first node in document order, or empty node if node set is empty.
|
||||
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
|
||||
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
|
||||
xpath_node evaluate_node(const xpath_node& n) const;
|
||||
|
||||
// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
|
||||
const xpath_parse_result& result() const;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "allocator.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Low-level allocation functions
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
@ -97,6 +98,8 @@ void* memory_allocate(size_t size)
|
||||
|
||||
size_t memory_size(void* ptr)
|
||||
{
|
||||
assert(ptr);
|
||||
|
||||
size_t result;
|
||||
memcpy(&result, static_cast<size_t*>(ptr) - 1, sizeof(size_t));
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ static void* custom_allocate(size_t size)
|
||||
else
|
||||
{
|
||||
void* ptr = memory_allocate(size);
|
||||
assert(ptr);
|
||||
|
||||
g_memory_total_size += memory_size(ptr);
|
||||
g_memory_total_count++;
|
||||
|
||||
@ -1043,3 +1043,19 @@ TEST_XML(dom_node_children_attributes, "<node1 attr1='value1' attr2='value2' /><
|
||||
CHECK(r4.begin() == xml_attribute_iterator());
|
||||
CHECK(r4.end() == xml_attribute_iterator());
|
||||
}
|
||||
|
||||
TEST_XML(dom_unspecified_bool_coverage, "<node attr='value'>text</node>")
|
||||
{
|
||||
xml_node node = doc.first_child();
|
||||
|
||||
node(0);
|
||||
node.first_attribute()(0);
|
||||
node.text()(0);
|
||||
|
||||
#ifndef PUGIXML_NO_XPATH
|
||||
xpath_query q(STR("/node"));
|
||||
|
||||
q(0);
|
||||
q.evaluate_node(doc)(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -57,6 +57,29 @@ TEST_XML_FLAGS(write_comment, "<!--text-->", parse_comments | parse_fragment)
|
||||
CHECK_NODE_EX(doc, STR("<!--text-->\n"), STR(""), 0);
|
||||
}
|
||||
|
||||
TEST(write_comment_invalid)
|
||||
{
|
||||
xml_document doc;
|
||||
xml_node child = doc.append_child(node_comment);
|
||||
|
||||
CHECK_NODE(doc, STR("<!---->"));
|
||||
|
||||
child.set_value(STR("-"));
|
||||
CHECK_NODE(doc, STR("<!--- -->"));
|
||||
|
||||
child.set_value(STR("--"));
|
||||
CHECK_NODE(doc, STR("<!--- - -->"));
|
||||
|
||||
child.set_value(STR("---"));
|
||||
CHECK_NODE(doc, STR("<!--- - - -->"));
|
||||
|
||||
child.set_value(STR("-->"));
|
||||
CHECK_NODE(doc, STR("<!--- ->-->"));
|
||||
|
||||
child.set_value(STR("-->-"));
|
||||
CHECK_NODE(doc, STR("<!--- ->- -->"));
|
||||
}
|
||||
|
||||
TEST_XML_FLAGS(write_pi, "<?name value?>", parse_pi | parse_fragment)
|
||||
{
|
||||
CHECK_NODE(doc, STR("<?name value?>"));
|
||||
|
||||
@ -122,6 +122,28 @@ TEST_XML(xpath_sort_attributes, "<node/>")
|
||||
xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3;
|
||||
}
|
||||
|
||||
TEST(xpath_sort_random_medium)
|
||||
{
|
||||
xml_document doc;
|
||||
load_document_copy(doc, STR("<node>")
|
||||
STR("<child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2><child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2>")
|
||||
STR("<child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2><child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2>")
|
||||
STR("<child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2><child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2>")
|
||||
STR("</node>"));
|
||||
|
||||
xpath_node_set ns = doc.select_nodes(STR("//node() | //@*"));
|
||||
|
||||
std::vector<xpath_node> nsv(ns.begin(), ns.end());
|
||||
std::random_shuffle(nsv.begin(), nsv.end());
|
||||
|
||||
xpath_node_set copy(&nsv[0], &nsv[0] + nsv.size());
|
||||
copy.sort();
|
||||
|
||||
xpath_node_set_tester tester(copy, "sorted order failed");
|
||||
|
||||
for (unsigned int i = 2; i < 39; ++i) tester % i;
|
||||
}
|
||||
|
||||
TEST(xpath_sort_random_large)
|
||||
{
|
||||
xml_document doc;
|
||||
|
||||
@ -19,22 +19,22 @@ TEST_XML(xpath_api_select_nodes, "<node><head/><foo/><foo/><tail/></node>")
|
||||
xpath_node_set_tester(ns2, "ns2") % 4 % 5;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_api_select_single_node, "<node><head/><foo id='1'/><foo/><tail/></node>")
|
||||
TEST_XML(xpath_api_select_node, "<node><head/><foo id='1'/><foo/><tail/></node>")
|
||||
{
|
||||
xpath_node n1 = doc.select_single_node(STR("node/foo"));
|
||||
xpath_node n1 = doc.select_node(STR("node/foo"));
|
||||
|
||||
xpath_query q(STR("node/foo"));
|
||||
xpath_node n2 = doc.select_single_node(q);
|
||||
xpath_node n2 = doc.select_node(q);
|
||||
|
||||
CHECK(n1.node().attribute(STR("id")).as_int() == 1);
|
||||
CHECK(n2.node().attribute(STR("id")).as_int() == 1);
|
||||
|
||||
xpath_node n3 = doc.select_single_node(STR("node/bar"));
|
||||
xpath_node n3 = doc.select_node(STR("node/bar"));
|
||||
|
||||
CHECK(!n3);
|
||||
|
||||
xpath_node n4 = doc.select_single_node(STR("node/head/following-sibling::foo"));
|
||||
xpath_node n5 = doc.select_single_node(STR("node/tail/preceding-sibling::foo"));
|
||||
xpath_node n4 = doc.select_node(STR("node/head/following-sibling::foo"));
|
||||
xpath_node n5 = doc.select_node(STR("node/tail/preceding-sibling::foo"));
|
||||
|
||||
CHECK(n4.node().attribute(STR("id")).as_int() == 1);
|
||||
CHECK(n5.node().attribute(STR("id")).as_int() == 1);
|
||||
@ -42,20 +42,20 @@ TEST_XML(xpath_api_select_single_node, "<node><head/><foo id='1'/><foo/><tail/><
|
||||
|
||||
TEST_XML(xpath_api_node_bool_ops, "<node attr='value'/>")
|
||||
{
|
||||
generic_bool_ops_test(doc.select_single_node(STR("node")));
|
||||
generic_bool_ops_test(doc.select_single_node(STR("node/@attr")));
|
||||
generic_bool_ops_test(doc.select_node(STR("node")));
|
||||
generic_bool_ops_test(doc.select_node(STR("node/@attr")));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_api_node_eq_ops, "<node attr='value'/>")
|
||||
{
|
||||
generic_eq_ops_test(doc.select_single_node(STR("node")), doc.select_single_node(STR("node/@attr")));
|
||||
generic_eq_ops_test(doc.select_node(STR("node")), doc.select_node(STR("node/@attr")));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_api_node_accessors, "<node attr='value'/>")
|
||||
{
|
||||
xpath_node null;
|
||||
xpath_node node = doc.select_single_node(STR("node"));
|
||||
xpath_node attr = doc.select_single_node(STR("node/@attr"));
|
||||
xpath_node node = doc.select_node(STR("node"));
|
||||
xpath_node attr = doc.select_node(STR("node/@attr"));
|
||||
|
||||
CHECK(!null.node());
|
||||
CHECK(!null.attribute());
|
||||
@ -154,6 +154,9 @@ TEST_XML(xpath_api_evaluate, "<node attr='3'/>")
|
||||
|
||||
xpath_node_set ns = q.evaluate_node_set(doc);
|
||||
CHECK(ns.size() == 1 && ns[0].attribute() == doc.child(STR("node")).attribute(STR("attr")));
|
||||
|
||||
xpath_node nr = q.evaluate_node(doc);
|
||||
CHECK(nr.attribute() == doc.child(STR("node")).attribute(STR("attr")));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_api_evaluate_attr, "<node attr='3'/>")
|
||||
@ -173,6 +176,9 @@ TEST_XML(xpath_api_evaluate_attr, "<node attr='3'/>")
|
||||
|
||||
xpath_node_set ns = q.evaluate_node_set(n);
|
||||
CHECK(ns.size() == 1 && ns[0] == n);
|
||||
|
||||
xpath_node nr = q.evaluate_node(n);
|
||||
CHECK(nr == n);
|
||||
}
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
@ -190,18 +196,20 @@ TEST_XML(xpath_api_evaluate_fail, "<node attr='3'/>")
|
||||
#endif
|
||||
|
||||
CHECK(q.evaluate_node_set(doc).empty());
|
||||
|
||||
CHECK(!q.evaluate_node(doc));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(xpath_api_evaluate_node_set_fail)
|
||||
{
|
||||
xpath_query q(STR("1"));
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
CHECK_XPATH_NODESET(xml_node(), STR("1"));
|
||||
CHECK(q.evaluate_node_set(xml_node()).empty());
|
||||
#else
|
||||
try
|
||||
{
|
||||
xpath_query q(STR("1"));
|
||||
|
||||
q.evaluate_node_set(xml_node());
|
||||
|
||||
CHECK_FORCE_FAIL("Expected exception");
|
||||
@ -212,6 +220,25 @@ TEST(xpath_api_evaluate_node_set_fail)
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(xpath_api_evaluate_node_fail)
|
||||
{
|
||||
xpath_query q(STR("1"));
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
CHECK(!q.evaluate_node(xml_node()));
|
||||
#else
|
||||
try
|
||||
{
|
||||
q.evaluate_node(xml_node());
|
||||
|
||||
CHECK_FORCE_FAIL("Expected exception");
|
||||
}
|
||||
catch (const xpath_exception&)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(xpath_api_evaluate_string)
|
||||
{
|
||||
xpath_query q(STR("\"0123456789\""));
|
||||
@ -384,4 +411,14 @@ TEST_XML(xpath_api_node_set_assign_out_of_memory_preserve, "<node><a/><b/></node
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_XML(xpath_api_deprecated_select_single_node, "<node><head/><foo id='1'/><foo/><tail/></node>")
|
||||
{
|
||||
xpath_node n1 = doc.select_single_node(STR("node/foo"));
|
||||
|
||||
xpath_query q(STR("node/foo"));
|
||||
xpath_node n2 = doc.select_single_node(q);
|
||||
|
||||
CHECK(n1.node().attribute(STR("id")).as_int() == 1);
|
||||
CHECK(n2.node().attribute(STR("id")).as_int() == 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -114,7 +114,7 @@ TEST(xpath_number_ceiling)
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div ceiling(0))"), STR("Infinity"));
|
||||
|
||||
// ceiling with argument in range (-1, -0] should result in minus zero
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0)
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(__CLR_VER) // MacOS X gcc 4.0.1 and x64 CLR implement ceil incorrectly (ceil never returns -0)
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0))"), STR("-Infinity"));
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0.1))"), STR("-Infinity"));
|
||||
#endif
|
||||
@ -145,7 +145,7 @@ TEST(xpath_number_round)
|
||||
// round with argument in range [-0.5, -0] should result in minus zero
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div round(0))"), STR("Infinity"));
|
||||
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0)
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(__CLR_VER) // MacOS X gcc 4.0.1 and x64 CLR implement ceil incorrectly (ceil never returns -0)
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div round(-0.5))"), STR("-Infinity"));
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div round(-0))"), STR("-Infinity"));
|
||||
CHECK_XPATH_STRING(c, STR("string(1 div round(-0.1))"), STR("-Infinity"));
|
||||
|
||||
@ -531,6 +531,27 @@ TEST_XML(xpath_paths_descendant_optimize, "<node><para><para/><para/><para><para
|
||||
CHECK_XPATH_NODESET(doc, STR("/descendant-or-self::node()[3]/child::para")) % 4 % 5 % 6;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_descendant_optimize_axes, "<node><para><para/><para/><para><para/></para></para><para/></node>")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("//.")) % 1 % 2 % 3 % 4 % 5 % 6 % 7 % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("//descendant::*")) % 2 % 3 % 4 % 5 % 6 % 7 % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("//descendant-or-self::*")) % 2 % 3 % 4 % 5 % 6 % 7 % 8;
|
||||
|
||||
CHECK_XPATH_NODESET(doc, STR("//..")) % 1 % 2 % 3 % 6;
|
||||
CHECK_XPATH_NODESET(doc, STR("//ancestor::*")) % 2 % 3 % 6;
|
||||
CHECK_XPATH_NODESET(doc, STR("//ancestor-or-self::*")) % 2 % 3 % 4 % 5 % 6 % 7 % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("//preceding-sibling::*")) % 3 % 4 % 5;
|
||||
CHECK_XPATH_NODESET(doc, STR("//following-sibling::*")) % 5 % 6 % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("//preceding::*")) % 3 % 4 % 5 % 6 % 7;
|
||||
CHECK_XPATH_NODESET(doc, STR("//following::*")) % 5 % 6 % 7 % 8;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_descendant_optimize_last, "<node><para><para/><para/><para><para/></para></para><para/></node>")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("//para[last()]")) % 6 % 7 % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("//para[last() = 1]")) % 7;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_precision, "<node><para/><para/><para/><para/><para/></node>")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("//para[1]")) % 3;
|
||||
@ -555,4 +576,67 @@ TEST_XML(xpath_paths_unsorted_child, "<node><foo><bar/></foo><node><foo><bar/></
|
||||
CHECK(ns[2] == nss[1]);
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_optimize_compare_attribute, "<node id='1' /><node id='2' /><node xmlns='3' />")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("node[@id = '1']")) % 2;
|
||||
CHECK_XPATH_NODESET(doc, STR("node[@id = '2']")) % 4;
|
||||
CHECK_XPATH_NODESET(doc, STR("node[@id = 2]")) % 4;
|
||||
CHECK_XPATH_NODESET(doc, STR("node[@id[. > 3] = '2']"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node['1' = @id]")) % 2;
|
||||
|
||||
xpath_variable_set set;
|
||||
set.set(STR("var1"), STR("2"));
|
||||
set.set(STR("var2"), 2.0);
|
||||
|
||||
CHECK_XPATH_NODESET_VAR(doc, STR("node[@id = $var1]"), &set) % 4;
|
||||
CHECK_XPATH_NODESET_VAR(doc, STR("node[@id = $var2]"), &set) % 4;
|
||||
|
||||
CHECK_XPATH_NODESET(doc, STR("node[@xmlns = '3']"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_optimize_step_once, "<node><para1><para2/><para3/><para4><para5 attr5=''/></para4></para1><para6/></node>")
|
||||
{
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("node//para2/following::*"), true);
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("node//para6/following::*"), false);
|
||||
|
||||
CHECK_XPATH_STRING(doc, STR("name(node//para2/following::*)"), STR("para3"));
|
||||
CHECK_XPATH_STRING(doc, STR("name(node//para6/following::*)"), STR(""));
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("node//para1/preceding::*"), false);
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("node//para6/preceding::*"), true);
|
||||
|
||||
CHECK_XPATH_STRING(doc, STR("name(node//para1/preceding::*)"), STR(""));
|
||||
CHECK_XPATH_STRING(doc, STR("name(node//para6/preceding::*)"), STR("para1"));
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("node//para6/preceding::para4"), true);
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//@attr5/ancestor-or-self::*"), true);
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//@attr5/ancestor::*"), true);
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//@attr5/following::para6"), true);
|
||||
CHECK_XPATH_STRING(doc, STR("name(//@attr5/following::para6)"), STR("para6"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_null_nodeset_entries, "<node attr='value'/>")
|
||||
{
|
||||
xpath_node nodes[] =
|
||||
{
|
||||
xpath_node(doc.first_child()),
|
||||
xpath_node(xml_node()),
|
||||
xpath_node(doc.first_child().first_attribute(), doc.first_child()),
|
||||
xpath_node(xml_attribute(), doc.first_child()),
|
||||
xpath_node(xml_attribute(), xml_node()),
|
||||
};
|
||||
|
||||
xpath_node_set ns(nodes, nodes + sizeof(nodes) / sizeof(nodes[0]));
|
||||
|
||||
xpath_variable_set vars;
|
||||
vars.set(STR("x"), ns);
|
||||
|
||||
xpath_node_set rs = xpath_query("$x/.", &vars).evaluate_node_set(xml_node());
|
||||
|
||||
CHECK(rs.size() == 2);
|
||||
CHECK(rs[0] == nodes[0]);
|
||||
CHECK(rs[1] == nodes[2]);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -281,7 +281,7 @@ TEST_XML(xpath_variables_select, "<node attr='1'/><node attr='2'/>")
|
||||
xpath_node_set ns = doc.select_nodes(STR("node[@attr=$one+1]"), &set);
|
||||
CHECK(ns.size() == 1 && ns[0].node() == doc.last_child());
|
||||
|
||||
xpath_node n = doc.select_single_node(STR("node[@attr=$one+1]"), &set);
|
||||
xpath_node n = doc.select_node(STR("node[@attr=$one+1]"), &set);
|
||||
CHECK(n == ns[0]);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
TEST_XML(xpath_xalan_axes_1, "<far-north><north-north-west1/><north-north-west2/><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><near-south><south><far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>")
|
||||
{
|
||||
xml_node center = doc.select_single_node(STR("//center")).node();
|
||||
xml_node center = doc.select_node(STR("//center")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(center, STR("self::*[near-south]")) % 10;
|
||||
CHECK_XPATH_NODESET(center, STR("self::*[@center-attr-2]")) % 10;
|
||||
@ -35,7 +35,7 @@ TEST_XML(xpath_xalan_axes_1, "<far-north><north-north-west1/><north-north-west2/
|
||||
|
||||
TEST_XML_FLAGS(xpath_xalan_axes_2, "<far-north> Level-1<north-north-west1/><north-north-west2/><!-- Comment-2 --> Level-2<?a-pi pi-2?><north><!-- Comment-3 --> Level-3<?a-pi pi-3?><near-north><far-west/><west/><near-west/><!-- Comment-4 --> Level-4<?a-pi pi-4?><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><!--Comment-5--> Level-5<?a-pi pi-5?><near-south><!--Comment-6--> Level-6<?a-pi pi-6?><south attr1='First' attr2='Last'> <far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>", parse_default | parse_comments | parse_pi)
|
||||
{
|
||||
xml_node center = doc.select_single_node(STR("//center")).node();
|
||||
xml_node center = doc.select_node(STR("//center")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(center, STR("@*")) % 21 % 22 % 23;
|
||||
CHECK_XPATH_NODESET(center, STR("@*/child::*"));
|
||||
@ -65,7 +65,7 @@ TEST_XML_FLAGS(xpath_xalan_axes_2, "<far-north> Level-1<north-north-west1/><nort
|
||||
|
||||
TEST_XML(xpath_xalan_axes_3, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
|
||||
{
|
||||
xml_node center = doc.select_single_node(STR("//center")).node();
|
||||
xml_node center = doc.select_node(STR("//center")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(center, STR("ancestor-or-self::*")) % 8 % 4 % 3 % 2;
|
||||
CHECK_XPATH_NODESET(center, STR("ancestor::*[3]")) % 2;
|
||||
@ -99,7 +99,7 @@ TEST_XML(xpath_xalan_axes_3, "<far-north><north><near-north><far-west/><west/><n
|
||||
|
||||
TEST_XML(xpath_xalan_axes_4, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
|
||||
{
|
||||
xml_node north = doc.select_single_node(STR("//north")).node();
|
||||
xml_node north = doc.select_node(STR("//north")).node();
|
||||
|
||||
CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north)"), STR("north"));
|
||||
CHECK_XPATH_STRING(north, STR("name(/descendant::near-north)"), STR("near-north"));
|
||||
@ -166,7 +166,7 @@ TEST_XML(xpath_xalan_axes_6, "<doc><T>Test for source tree depth</T><a><T>A</T><
|
||||
|
||||
TEST_XML(xpath_xalan_axes_7, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
|
||||
{
|
||||
xml_node center = doc.select_single_node(STR("//center")).node();
|
||||
xml_node center = doc.select_node(STR("//center")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(center, STR("attribute::*[2]")) % 10;
|
||||
CHECK_XPATH_NODESET(center, STR("@*")) % 9 % 10 % 11;
|
||||
@ -177,7 +177,7 @@ TEST_XML(xpath_xalan_axes_7, "<far-north><north><near-north><far-west/><west/><n
|
||||
|
||||
TEST_XML(xpath_xalan_axes_8, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-east/><near-south><south><far-south/></south></near-south><near-south-west/></center><near-east/><east/><far-east/></near-north></north></far-north>")
|
||||
{
|
||||
xml_node near_north = doc.select_single_node(STR("//near-north")).node();
|
||||
xml_node near_north = doc.select_node(STR("//near-north")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(near_north, STR("center//child::*")) % 12 % 13 % 14 % 15 % 16;
|
||||
CHECK_XPATH_NODESET(near_north, STR("center//descendant::*")) % 12 % 13 % 14 % 15 % 16;
|
||||
@ -188,7 +188,7 @@ TEST_XML(xpath_xalan_axes_8, "<far-north><north><near-north><far-west/><west/><n
|
||||
|
||||
TEST_XML(xpath_xalan_axes_9, "<doc><foo att1='c'><foo att1='b'><foo att1='a'><baz/></foo></foo></foo><bar/></doc>")
|
||||
{
|
||||
xml_node baz = doc.select_single_node(STR("//baz")).node();
|
||||
xml_node baz = doc.select_node(STR("//baz")).node();
|
||||
|
||||
CHECK_XPATH_NODESET(baz, STR("ancestor-or-self::*[@att1][1]/@att1")) % 8;
|
||||
CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*)[@att1][1]/@att1")) % 4;
|
||||
@ -243,7 +243,7 @@ TEST_XML_FLAGS(xpath_xalan_axes_12, "<far-north><north>north-text1<near-north><f
|
||||
TEST_XML(xpath_xalan_axes_13, "<doc att1='e'><foo att1='d'><foo att1='c'><foo att1='b'><baz att1='a'/></foo></foo></foo></doc>")
|
||||
{
|
||||
xml_node d = doc.child(STR("doc"));
|
||||
xml_node baz = doc.select_single_node(STR("//baz")).node();
|
||||
xml_node baz = doc.select_node(STR("//baz")).node();
|
||||
|
||||
CHECK_XPATH_NUMBER(d, STR("count(descendant-or-self::*/@att1)"), 5);
|
||||
CHECK_XPATH_NODESET(d, STR("descendant-or-self::*/@att1[last()]")) % 3 % 5 % 7 % 9 % 11;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user