XPath: Introduce xpath_query::evaluate_node

This method is equivalent to xml_node::select_single_node. This makes
select_single_node faster in certain cases by avoiding an allocation and -
more importantly - paves the way for future step optimizations.

git-svn-id: https://pugixml.googlecode.com/svn/trunk@1064 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
Arseny Kapoulkine 2014-10-19 07:33:42 +00:00
parent 72ec01c5f6
commit f663558875
3 changed files with 75 additions and 20 deletions

View file

@ -10581,6 +10581,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
@ -11082,22 +11101,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;
@ -11110,6 +11116,23 @@ namespace pugi
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);
return r.first();
}
PUGI__FN const xpath_parse_result& xpath_query::result() const
{
return _result;
@ -11137,8 +11160,7 @@ namespace pugi
PUGI__FN xpath_node xml_node::select_single_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

View file

@ -1134,6 +1134,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;

View file

@ -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\""));