XPath: Added xpath_query::return_type() function, fixed evaluate_node_set documentation

git-svn-id: http://pugixml.googlecode.com/svn/trunk@232 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
arseny.kapoulkine 2009-11-08 19:05:05 +00:00
parent 85f97d8e08
commit f0f7ac697b
3 changed files with 99 additions and 76 deletions

View File

@ -258,6 +258,16 @@ namespace pugi
class xpath_ast_node; class xpath_ast_node;
class xpath_allocator; class xpath_allocator;
/// XPath query return type classification
enum xpath_type_t
{
xpath_type_none, ///< Unknown type (query failed to compile)
xpath_type_node_set, ///< Node set (\see xpath_node_set)
xpath_type_number, ///< Number
xpath_type_string, ///< String
xpath_type_boolean ///< Boolean
};
/** /**
* A class that holds compiled XPath query and allows to evaluate query result * A class that holds compiled XPath query and allows to evaluate query result
*/ */
@ -286,6 +296,13 @@ namespace pugi
* Dtor * Dtor
*/ */
~xpath_query(); ~xpath_query();
/**
* Get query expression return type
*
* \return expression return type
**/
xpath_type_t return_type() const;
/** /**
* Evaluate expression as boolean value for the context node \a n. * Evaluate expression as boolean value for the context node \a n.
@ -322,7 +339,7 @@ namespace pugi
/** /**
* Evaluate expression as node set for the context node \a n. * Evaluate expression as node set for the context node \a n.
* If expression does not directly evaluate to node set, function returns empty node set. * If expression does not directly evaluate to node set, throws xpath_exception.
* Throws std::bad_alloc on out of memory error. * Throws std::bad_alloc on out of memory error.
* *
* \param n - context node * \param n - context node

View File

@ -1214,15 +1214,6 @@ namespace pugi
ast_step_root // select root node ast_step_root // select root node
}; };
enum ast_rettype_t
{
ast_type_none,
ast_type_node_set,
ast_type_number,
ast_type_string,
ast_type_boolean
};
enum axis_t enum axis_t
{ {
axis_ancestor, axis_ancestor,
@ -1265,7 +1256,7 @@ namespace pugi
private: private:
ast_type_t m_type; ast_type_t m_type;
ast_rettype_t m_rettype; xpath_type_t m_rettype;
// tree node structure // tree node structure
xpath_ast_node* m_left; xpath_ast_node* m_left;
@ -1289,16 +1280,16 @@ namespace pugi
{ {
static bool run(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c) static bool run(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c)
{ {
if (lhs->rettype() != ast_type_node_set && rhs->rettype() != ast_type_node_set) if (lhs->rettype() != xpath_type_node_set && rhs->rettype() != xpath_type_node_set)
{ {
if (lhs->rettype() == ast_type_boolean || rhs->rettype() == ast_type_boolean) if (lhs->rettype() == xpath_type_boolean || rhs->rettype() == xpath_type_boolean)
return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c)); return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
else if (lhs->rettype() == ast_type_number || rhs->rettype() == ast_type_number) else if (lhs->rettype() == xpath_type_number || rhs->rettype() == xpath_type_number)
return Cdouble()(lhs->eval_number(c), rhs->eval_number(c)); return Cdouble()(lhs->eval_number(c), rhs->eval_number(c));
else if (lhs->rettype() == ast_type_string || rhs->rettype() == ast_type_string) else if (lhs->rettype() == xpath_type_string || rhs->rettype() == xpath_type_string)
return Cstring()(lhs->eval_string(c), rhs->eval_string(c)); return Cstring()(lhs->eval_string(c), rhs->eval_string(c));
} }
else if (lhs->rettype() == ast_type_node_set && rhs->rettype() == ast_type_node_set) else if (lhs->rettype() == xpath_type_node_set && rhs->rettype() == xpath_type_node_set)
{ {
xpath_node_set ls = lhs->eval_node_set(c); xpath_node_set ls = lhs->eval_node_set(c);
xpath_node_set rs = rhs->eval_node_set(c); xpath_node_set rs = rhs->eval_node_set(c);
@ -1312,11 +1303,11 @@ namespace pugi
return false; return false;
} }
else if (lhs->rettype() != ast_type_node_set && rhs->rettype() == ast_type_node_set) else if (lhs->rettype() != xpath_type_node_set && rhs->rettype() == xpath_type_node_set)
{ {
if (lhs->rettype() == ast_type_boolean) if (lhs->rettype() == xpath_type_boolean)
return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c)); return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
else if (lhs->rettype() == ast_type_number) else if (lhs->rettype() == xpath_type_number)
{ {
double l = lhs->eval_number(c); double l = lhs->eval_number(c);
xpath_node_set rs = rhs->eval_node_set(c); xpath_node_set rs = rhs->eval_node_set(c);
@ -1329,7 +1320,7 @@ namespace pugi
return false; return false;
} }
else if (lhs->rettype() == ast_type_string) else if (lhs->rettype() == xpath_type_string)
{ {
std::string l = lhs->eval_string(c); std::string l = lhs->eval_string(c);
xpath_node_set rs = rhs->eval_node_set(c); xpath_node_set rs = rhs->eval_node_set(c);
@ -1343,11 +1334,11 @@ namespace pugi
return false; return false;
} }
} }
else if (lhs->rettype() == ast_type_node_set && rhs->rettype() != ast_type_node_set) else if (lhs->rettype() == xpath_type_node_set && rhs->rettype() != xpath_type_node_set)
{ {
if (rhs->rettype() == ast_type_boolean) if (rhs->rettype() == xpath_type_boolean)
return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c)); return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
else if (rhs->rettype() == ast_type_number) else if (rhs->rettype() == xpath_type_number)
{ {
xpath_node_set ls = lhs->eval_node_set(c); xpath_node_set ls = lhs->eval_node_set(c);
double r = rhs->eval_number(c); double r = rhs->eval_number(c);
@ -1360,7 +1351,7 @@ namespace pugi
return false; return false;
} }
else if (rhs->rettype() == ast_type_string) else if (rhs->rettype() == xpath_type_string)
{ {
xpath_node_set ls = lhs->eval_node_set(c); xpath_node_set ls = lhs->eval_node_set(c);
std::string r = rhs->eval_string(c); std::string r = rhs->eval_string(c);
@ -1384,9 +1375,9 @@ namespace pugi
{ {
static bool run(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c) static bool run(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c)
{ {
if (lhs->rettype() != ast_type_node_set && rhs->rettype() != ast_type_node_set) if (lhs->rettype() != xpath_type_node_set && rhs->rettype() != xpath_type_node_set)
return Cdouble()(lhs->eval_number(c), rhs->eval_number(c)); return Cdouble()(lhs->eval_number(c), rhs->eval_number(c));
else if (lhs->rettype() == ast_type_node_set && rhs->rettype() == ast_type_node_set) else if (lhs->rettype() == xpath_type_node_set && rhs->rettype() == xpath_type_node_set)
{ {
xpath_node_set ls = lhs->eval_node_set(c); xpath_node_set ls = lhs->eval_node_set(c);
xpath_node_set rs = rhs->eval_node_set(c); xpath_node_set rs = rhs->eval_node_set(c);
@ -1404,7 +1395,7 @@ namespace pugi
return false; return false;
} }
else if (lhs->rettype() != ast_type_node_set && rhs->rettype() == ast_type_node_set) else if (lhs->rettype() != xpath_type_node_set && rhs->rettype() == xpath_type_node_set)
{ {
double l = lhs->eval_number(c); double l = lhs->eval_number(c);
xpath_node_set rs = rhs->eval_node_set(c); xpath_node_set rs = rhs->eval_node_set(c);
@ -1417,7 +1408,7 @@ namespace pugi
return false; return false;
} }
else if (lhs->rettype() == ast_type_node_set && rhs->rettype() != ast_type_node_set) else if (lhs->rettype() == xpath_type_node_set && rhs->rettype() != xpath_type_node_set)
{ {
xpath_node_set ls = lhs->eval_node_set(c); xpath_node_set ls = lhs->eval_node_set(c);
double r = rhs->eval_number(c); double r = rhs->eval_number(c);
@ -1455,7 +1446,7 @@ namespace pugi
c.position = i + 1; c.position = i + 1;
c.size = size; c.size = size;
if (expr->rettype() == ast_type_number) if (expr->rettype() == xpath_type_number)
{ {
if (expr->eval_number(c) == i + 1) if (expr->eval_number(c) == i + 1)
*last++ = *it; *last++ = *it;
@ -1911,24 +1902,24 @@ namespace pugi
} }
public: public:
xpath_ast_node(ast_type_t type, const char* contents, xpath_allocator& a): m_type(type), xpath_ast_node(ast_type_t type, const char* contents, xpath_allocator& a): m_type(type),
m_rettype(ast_type_none), m_left(0), m_right(0), m_third(0), m_next(0), m_contents(0) m_rettype(xpath_type_none), m_left(0), m_right(0), m_third(0), m_next(0), m_contents(0)
{ {
set_contents(contents, a); set_contents(contents, a);
} }
xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, axis_t axis): m_type(type), xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, axis_t axis): m_type(type),
m_rettype(ast_type_none), m_left(left), m_right(right), m_third(0), m_next(0), m_contents(0), m_rettype(xpath_type_none), m_left(left), m_right(right), m_third(0), m_next(0), m_contents(0),
m_axis(axis) m_axis(axis)
{ {
} }
xpath_ast_node(ast_type_t type, xpath_ast_node* left = 0, xpath_ast_node* right = 0, xpath_ast_node* third = 0): m_type(type), xpath_ast_node(ast_type_t type, xpath_ast_node* left = 0, xpath_ast_node* right = 0, xpath_ast_node* third = 0): m_type(type),
m_rettype(ast_type_none), m_left(left), m_right(right), m_third(third), m_next(0), m_contents(0) m_rettype(xpath_type_none), m_left(left), m_right(right), m_third(third), m_next(0), m_contents(0)
{ {
} }
xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char* contents, xpath_allocator& a): xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char* contents, xpath_allocator& a):
m_type(type), m_rettype(ast_type_none), m_left(left), m_right(0), m_third(0), m_next(0), m_type(type), m_rettype(xpath_type_none), m_left(left), m_right(0), m_third(0), m_next(0),
m_contents(0), m_axis(axis), m_test(test) m_contents(0), m_axis(axis), m_test(test)
{ {
set_contents(contents, a); set_contents(contents, a);
@ -2029,13 +2020,13 @@ namespace pugi
{ {
switch (m_rettype) switch (m_rettype)
{ {
case ast_type_number: case xpath_type_number:
return convert_number_to_boolean(eval_number(c)); return convert_number_to_boolean(eval_number(c));
case ast_type_string: case xpath_type_string:
return !eval_string(c).empty(); return !eval_string(c).empty();
case ast_type_node_set: case xpath_type_node_set:
return !eval_node_set(c).empty(); return !eval_node_set(c).empty();
default: default:
@ -2126,13 +2117,13 @@ namespace pugi
{ {
switch (m_rettype) switch (m_rettype)
{ {
case ast_type_boolean: case xpath_type_boolean:
return eval_boolean(c) ? 1 : 0; return eval_boolean(c) ? 1 : 0;
case ast_type_string: case xpath_type_string:
return convert_string_to_number(eval_string(c).c_str()); return convert_string_to_number(eval_string(c).c_str());
case ast_type_node_set: case xpath_type_node_set:
return convert_string_to_number(eval_string(c).c_str()); return convert_string_to_number(eval_string(c).c_str());
default: default:
@ -2326,13 +2317,13 @@ namespace pugi
{ {
switch (m_rettype) switch (m_rettype)
{ {
case ast_type_boolean: case xpath_type_boolean:
return eval_boolean(c) ? "true" : "false"; return eval_boolean(c) ? "true" : "false";
case ast_type_number: case xpath_type_number:
return convert_number_to_string(eval_number(c)); return convert_number_to_string(eval_number(c));
case ast_type_node_set: case xpath_type_node_set:
{ {
xpath_node_set ns = eval_node_set(c); xpath_node_set ns = eval_node_set(c);
return ns.empty() ? std::string("") : string_value(ns.first()); return ns.empty() ? std::string("") : string_value(ns.first());
@ -2383,7 +2374,7 @@ namespace pugi
c.position = i + 1; c.position = i + 1;
c.size = set.size(); c.size = set.size();
if (m_right->rettype() == ast_type_number) if (m_right->rettype() == xpath_type_number)
{ {
if (m_right->eval_number(c) == i + 1) if (m_right->eval_number(c) == i + 1)
*last++ = *it; *last++ = *it;
@ -2625,7 +2616,7 @@ namespace pugi
case ast_op_greater_or_equal: case ast_op_greater_or_equal:
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
m_rettype = ast_type_boolean; m_rettype = xpath_type_boolean;
break; break;
case ast_op_add: case ast_op_add:
@ -2635,65 +2626,65 @@ namespace pugi
case ast_op_mod: case ast_op_mod:
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_op_negate: case ast_op_negate:
m_left->check_semantics(); m_left->check_semantics();
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_op_union: case ast_op_union:
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
if (m_left->rettype() != ast_type_node_set || m_right->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set || m_right->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: union operator has to be applied to node sets"); throw xpath_exception("Semantics error: union operator has to be applied to node sets");
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
break; break;
case ast_filter: case ast_filter:
case ast_filter_posinv: case ast_filter_posinv:
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
if (m_left->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: predicate has to be applied to node set"); throw xpath_exception("Semantics error: predicate has to be applied to node set");
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
if (!m_right->contains(ast_func_position) && m_right->rettype() != ast_type_number) if (!m_right->contains(ast_func_position) && m_right->rettype() != xpath_type_number)
m_type = ast_filter_posinv; m_type = ast_filter_posinv;
break; break;
case ast_predicate: case ast_predicate:
m_left->check_semantics(); m_left->check_semantics();
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
break; break;
case ast_variable: case ast_variable:
throw xpath_exception("Semantics error: variable are not supported"); throw xpath_exception("Semantics error: variable are not supported");
case ast_string_constant: case ast_string_constant:
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
case ast_number_constant: case ast_number_constant:
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_last: case ast_func_last:
case ast_func_position: case ast_func_position:
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_count: case ast_func_count:
m_left->check_semantics(); m_left->check_semantics();
if (m_left->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: count() has to be applied to node set"); throw xpath_exception("Semantics error: count() has to be applied to node set");
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_id: case ast_func_id:
m_left->check_semantics(); m_left->check_semantics();
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
break; break;
case ast_func_local_name_0: case ast_func_local_name_0:
@ -2705,16 +2696,16 @@ namespace pugi
if (m_left) if (m_left)
{ {
m_left->check_semantics(); m_left->check_semantics();
if (m_left->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: function has to be applied to node set"); throw xpath_exception("Semantics error: function has to be applied to node set");
} }
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
case ast_func_string_0: case ast_func_string_0:
case ast_func_string_1: case ast_func_string_1:
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
case ast_func_concat: case ast_func_concat:
@ -2724,7 +2715,7 @@ namespace pugi
for (xpath_ast_node* n = m_right; n; n = n->m_next) for (xpath_ast_node* n = m_right; n; n = n->m_next)
n->check_semantics(); n->check_semantics();
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
} }
@ -2732,7 +2723,7 @@ namespace pugi
case ast_func_contains: case ast_func_contains:
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
m_rettype = ast_type_boolean; m_rettype = xpath_type_boolean;
break; break;
case ast_func_substring_before: case ast_func_substring_before:
@ -2742,13 +2733,13 @@ namespace pugi
m_left->check_semantics(); m_left->check_semantics();
m_right->check_semantics(); m_right->check_semantics();
if (m_third) m_third->check_semantics(); if (m_third) m_third->check_semantics();
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
case ast_func_string_length_0: case ast_func_string_length_0:
case ast_func_string_length_1: case ast_func_string_length_1:
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_normalize_space_0: case ast_func_normalize_space_0:
@ -2757,7 +2748,7 @@ namespace pugi
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
if (m_right) m_right->check_semantics(); if (m_right) m_right->check_semantics();
if (m_third) m_third->check_semantics(); if (m_third) m_third->check_semantics();
m_rettype = ast_type_string; m_rettype = xpath_type_string;
break; break;
case ast_func_boolean: case ast_func_boolean:
@ -2766,27 +2757,27 @@ namespace pugi
case ast_func_false: case ast_func_false:
case ast_func_lang: case ast_func_lang:
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
m_rettype = ast_type_boolean; m_rettype = xpath_type_boolean;
break; break;
case ast_func_number_0: case ast_func_number_0:
case ast_func_number_1: case ast_func_number_1:
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_sum: case ast_func_sum:
m_left->check_semantics(); m_left->check_semantics();
if (m_left->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: sum() has to be applied to node set"); throw xpath_exception("Semantics error: sum() has to be applied to node set");
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_func_floor: case ast_func_floor:
case ast_func_ceiling: case ast_func_ceiling:
case ast_func_round: case ast_func_round:
if (m_left) m_left->check_semantics(); if (m_left) m_left->check_semantics();
m_rettype = ast_type_number; m_rettype = xpath_type_number;
break; break;
case ast_step: case ast_step:
@ -2794,19 +2785,19 @@ namespace pugi
if (m_left) if (m_left)
{ {
m_left->check_semantics(); m_left->check_semantics();
if (m_left->rettype() != ast_type_node_set) if (m_left->rettype() != xpath_type_node_set)
throw xpath_exception("Semantics error: step has to be applied to node set"); throw xpath_exception("Semantics error: step has to be applied to node set");
} }
for (xpath_ast_node* n = m_right; n; n = n->m_next) for (xpath_ast_node* n = m_right; n; n = n->m_next)
n->check_semantics(); n->check_semantics();
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
break; break;
} }
case ast_step_root: case ast_step_root:
m_rettype = ast_type_node_set; m_rettype = xpath_type_node_set;
break; break;
default: default:
@ -2814,7 +2805,7 @@ namespace pugi
} }
} }
ast_rettype_t rettype() const xpath_type_t rettype() const
{ {
return m_rettype; return m_rettype;
} }
@ -3608,6 +3599,13 @@ namespace pugi
m_root->check_semantics(); m_root->check_semantics();
} }
xpath_type_t xpath_query::return_type() const
{
if (!m_root) return xpath_type_none;
return m_root->rettype();
}
bool xpath_query::evaluate_boolean(const xml_node& n) const bool xpath_query::evaluate_boolean(const xml_node& n) const
{ {
if (!m_root) return false; if (!m_root) return false;
@ -3653,7 +3651,7 @@ namespace pugi
xpath_node_set xpath_query::evaluate_node_set(const xml_node& n) const xpath_node_set xpath_query::evaluate_node_set(const xml_node& n) const
{ {
if (!m_root) return xpath_node_set(); if (!m_root) return xpath_node_set();
if (m_root->rettype() != ast_type_node_set) throw xpath_exception("Expression does not evaluate to node set"); if (m_root->rettype() != xpath_type_node_set) throw xpath_exception("Expression does not evaluate to node set");
xpath_context c; xpath_context c;

View File

@ -143,4 +143,12 @@ TEST(xpath_api_evaluate_node_set)
} }
} }
TEST(xpath_api_return_type)
{
CHECK(xpath_query("node").return_type() == xpath_type_node_set);
CHECK(xpath_query("1").return_type() == xpath_type_number);
CHECK(xpath_query("'s'").return_type() == xpath_type_string);
CHECK(xpath_query("true()").return_type() == xpath_type_boolean);
}
#endif #endif