summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pugixml.cpp372
-rw-r--r--src/pugixml.hpp15
2 files changed, 273 insertions, 114 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index ccc8276..4bb0c86 100644
--- a/src/pugixml.cpp
+++ b/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 = 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";
+ impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
+ if (!root) return xpath_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
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index 69b2cb2..e252e16 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -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;