From f6635588758ed1b650be22903c2e2e81273e05c5 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 19 Oct 2014 07:33:42 +0000 Subject: 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 --- src/pugixml.cpp | 56 +++++++++++++++++++++++++++++++++--------------- src/pugixml.hpp | 6 ++++++ tests/test_xpath_api.cpp | 33 +++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 1d9dcfe..6f230dd 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -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 = static_cast(_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)); + if (!root) return xpath_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)); + 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 diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 69b2cb2..2947bf4 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -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; diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index d831712..8ec5694 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -154,6 +154,9 @@ TEST_XML(xpath_api_evaluate, "") 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, "") @@ -173,6 +176,9 @@ TEST_XML(xpath_api_evaluate_attr, "") 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, "") #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\"")); -- cgit v1.2.3