diff options
| -rw-r--r-- | src/pugixml.cpp | 56 | ||||
| -rw-r--r-- | src/pugixml.hpp | 6 | ||||
| -rw-r--r-- | 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::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; @@ -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 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, "<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\"")); | 
