XPath: Implemented variable support in queries

git-svn-id: http://pugixml.googlecode.com/svn/trunk@680 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
arseny.kapoulkine 2010-08-29 15:38:43 +00:00
parent f481f038d6
commit 23d84cdf7c
2 changed files with 111 additions and 15 deletions

View File

@ -5385,6 +5385,7 @@ namespace
template <typename T> T* new_xpath_variable(const char_t* name) template <typename T> T* new_xpath_variable(const char_t* name)
{ {
size_t length = strlength(name); size_t length = strlength(name);
if (length == 0) return 0; // empty variable names are invalid
// $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
void* memory = global_allocate(sizeof(T) + length * sizeof(char_t)); void* memory = global_allocate(sizeof(T) + length * sizeof(char_t));
@ -5448,6 +5449,32 @@ namespace
assert(false); assert(false);
} }
} }
xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end)
{
char_t buffer[32];
size_t length = static_cast<size_t>(end - begin);
char_t* scratch = buffer;
if (length >= sizeof(buffer) / sizeof(buffer[0]))
{
// need to make dummy on-heap copy
scratch = static_cast<char_t*>(global_allocate((length + 1) * sizeof(char_t)));
if (!scratch) return 0;
}
// copy string to zero-terminated buffer and perform lookup
memcpy(scratch, begin, length * sizeof(char_t));
scratch[length] = 0;
xpath_variable* result = set->get(scratch);
// free dummy buffer
if (scratch != buffer) global_deallocate(scratch);
return result;
}
} }
namespace pugi namespace pugi
@ -6125,6 +6152,7 @@ namespace pugi
ast_filter_posinv, // select * from left where right; proximity position invariant ast_filter_posinv, // select * from left where right; proximity position invariant
ast_string_constant, // string constant ast_string_constant, // string constant
ast_number_constant, // number constant ast_number_constant, // number constant
ast_variable, // variable
ast_func_last, // last() ast_func_last, // last()
ast_func_position, // position() ast_func_position, // position()
ast_func_count, // count(left) ast_func_count, // count(left)
@ -6223,6 +6251,8 @@ namespace pugi
const char_t* string; const char_t* string;
// value for ast_number_constant // value for ast_number_constant
double number; double number;
// variable for ast_variable
xpath_variable* variable;
// node test for ast_step (node name/namespace/node type/pi target) // node test for ast_step (node name/namespace/node type/pi target)
const char_t* nodetest; const char_t* nodetest;
} _data; } _data;
@ -6838,6 +6868,13 @@ namespace pugi
_data.number = value; _data.number = value;
} }
xpath_ast_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value):
_type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(0), _right(0), _next(0)
{
assert(type == ast_variable);
_data.variable = value;
}
xpath_ast_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0): xpath_ast_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0):
_type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(left), _right(right), _next(0) _type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(left), _right(right), _next(0)
{ {
@ -6940,6 +6977,16 @@ namespace pugi
return false; return false;
} }
case ast_variable:
{
assert(_rettype == _data.variable->type());
if (_rettype == xpath_type_boolean)
return _data.variable->get_boolean();
// fallthrough to type conversion
}
default: default:
{ {
switch (_rettype) switch (_rettype)
@ -7036,6 +7083,16 @@ namespace pugi
case ast_func_round: case ast_func_round:
return round_nearest_nzero(_left->eval_number(c)); return round_nearest_nzero(_left->eval_number(c));
case ast_variable:
{
assert(_rettype == _data.variable->type());
if (_rettype == xpath_type_number)
return _data.variable->get_number();
// fallthrough to type conversion
}
default: default:
{ {
switch (_rettype) switch (_rettype)
@ -7211,6 +7268,16 @@ namespace pugi
return s; return s;
} }
case ast_variable:
{
assert(_rettype == _data.variable->type());
if (_rettype == xpath_type_string)
return xpath_string_const(_data.variable->get_string());
// fallthrough to type conversion
}
default: default:
{ {
switch (_rettype) switch (_rettype)
@ -7347,6 +7414,16 @@ namespace pugi
return ns; return ns;
} }
case ast_variable:
{
assert(_rettype == _data.variable->type());
if (_rettype == xpath_type_node_set)
return _data.variable->get_node_set();
// fallthrough to type conversion
}
default: default:
assert(!"Wrong expression for return type node set"); assert(!"Wrong expression for return type node set");
return xpath_node_set(); return xpath_node_set();
@ -7362,7 +7439,7 @@ namespace pugi
case ast_string_constant: case ast_string_constant:
case ast_number_constant: case ast_number_constant:
// $$ case ast_variable: case ast_variable:
return true; return true;
case ast_step: case ast_step:
@ -7394,7 +7471,10 @@ namespace pugi
{ {
xpath_allocator& _alloc; xpath_allocator& _alloc;
xpath_lexer _lexer; xpath_lexer _lexer;
const char_t* _query; const char_t* _query;
xpath_variable_set* _variables;
xpath_parse_result* _result; xpath_parse_result* _result;
jmp_buf _error_handler; jmp_buf _error_handler;
@ -7658,9 +7738,24 @@ namespace pugi
{ {
case lex_var_ref: case lex_var_ref:
{ {
throw_error("Variables are not supported"); _lexer.next();
return 0; if (_lexer.current() != lex_string)
throw_error("Variable name expected");
if (!_variables)
throw_error("Unknown variable: variable set is not provided");
xpath_lexer_string name = _lexer.contents();
xpath_variable* var = get_variable(_variables, name.begin, name.end);
if (!var)
throw_error("Unknown variable: variable set does not contain the given name");
_lexer.next();
return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var);
} }
case lex_open_brace: case lex_open_brace:
@ -8190,7 +8285,7 @@ namespace pugi
return parse_or_expression(); return parse_or_expression();
} }
xpath_parser(const char_t* query, xpath_allocator& alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _result(result) xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator& alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
{ {
} }
@ -8207,9 +8302,9 @@ namespace pugi
return result; return result;
} }
static xpath_ast_node* parse(const char_t* query, xpath_allocator& alloc, xpath_parse_result* result) static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator& alloc, xpath_parse_result* result)
{ {
xpath_parser parser(query, alloc, result); xpath_parser parser(query, variables, alloc, result);
int error = setjmp(parser._error_handler); int error = setjmp(parser._error_handler);
@ -8404,7 +8499,7 @@ namespace pugi
return find(name); return find(name);
} }
xpath_query::xpath_query(const char_t* query): _alloc(0), _root(0) xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _alloc(0), _root(0)
{ {
_result.error = 0; _result.error = 0;
_result.offset = 0; _result.offset = 0;
@ -8417,7 +8512,7 @@ namespace pugi
} }
else else
{ {
_root = xpath_parser::parse(query, *_alloc, &_result); _root = xpath_parser::parse(query, variables, *_alloc, &_result);
} }
if (!_root) if (!_root)
@ -8519,9 +8614,9 @@ namespace pugi
return !_root; return !_root;
} }
xpath_node xml_node::select_single_node(const char_t* query) const xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
{ {
xpath_query q(query); xpath_query q(query, variables);
return select_single_node(q); return select_single_node(q);
} }
@ -8531,9 +8626,9 @@ namespace pugi
return s.empty() ? xpath_node() : s.first(); return s.empty() ? xpath_node() : s.first();
} }
xpath_node_set xml_node::select_nodes(const char_t* query) const xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
{ {
xpath_query q(query); xpath_query q(query, variables);
return select_nodes(q); return select_nodes(q);
} }

View File

@ -325,6 +325,7 @@ namespace pugi
class xpath_node; class xpath_node;
class xpath_node_set; class xpath_node_set;
class xpath_query; class xpath_query;
class xpath_variable_set;
#endif #endif
/** /**
@ -1192,7 +1193,7 @@ namespace pugi
* \param query - query string * \param query - query string
* \return first node from the resulting node set by document order, or empty node if none found * \return first node from the resulting node set by document order, or empty node if none found
*/ */
xpath_node select_single_node(const char_t* query) const; xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
/** /**
* Select single node by evaluating XPath query * Select single node by evaluating XPath query
@ -1208,7 +1209,7 @@ namespace pugi
* \param query - query string * \param query - query string
* \return resulting node set * \return resulting node set
*/ */
xpath_node_set select_nodes(const char_t* query) const; xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
/** /**
* Select node set by evaluating XPath query * Select node set by evaluating XPath query
@ -1889,7 +1890,7 @@ namespace pugi
* *
* \param query - string with XPath expression * \param query - string with XPath expression
*/ */
explicit xpath_query(const char_t* query); explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
/** /**
* Destructor * Destructor