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)
{
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
void* memory = global_allocate(sizeof(T) + length * sizeof(char_t));
@ -5448,6 +5449,32 @@ namespace
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
@ -6125,6 +6152,7 @@ namespace pugi
ast_filter_posinv, // select * from left where right; proximity position invariant
ast_string_constant, // string constant
ast_number_constant, // number constant
ast_variable, // variable
ast_func_last, // last()
ast_func_position, // position()
ast_func_count, // count(left)
@ -6223,6 +6251,8 @@ namespace pugi
const char_t* string;
// value for ast_number_constant
double number;
// variable for ast_variable
xpath_variable* variable;
// node test for ast_step (node name/namespace/node type/pi target)
const char_t* nodetest;
} _data;
@ -6838,6 +6868,13 @@ namespace pugi
_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):
_type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(left), _right(right), _next(0)
{
@ -6940,6 +6977,16 @@ namespace pugi
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:
{
switch (_rettype)
@ -7036,6 +7083,16 @@ namespace pugi
case ast_func_round:
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:
{
switch (_rettype)
@ -7211,6 +7268,16 @@ namespace pugi
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:
{
switch (_rettype)
@ -7347,6 +7414,16 @@ namespace pugi
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:
assert(!"Wrong expression for return type node set");
return xpath_node_set();
@ -7362,7 +7439,7 @@ namespace pugi
case ast_string_constant:
case ast_number_constant:
// $$ case ast_variable:
case ast_variable:
return true;
case ast_step:
@ -7394,7 +7471,10 @@ namespace pugi
{
xpath_allocator& _alloc;
xpath_lexer _lexer;
const char_t* _query;
xpath_variable_set* _variables;
xpath_parse_result* _result;
jmp_buf _error_handler;
@ -7658,9 +7738,24 @@ namespace pugi
{
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:
@ -8190,7 +8285,7 @@ namespace pugi
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;
}
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);
@ -8404,7 +8499,7 @@ namespace pugi
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.offset = 0;
@ -8417,7 +8512,7 @@ namespace pugi
}
else
{
_root = xpath_parser::parse(query, *_alloc, &_result);
_root = xpath_parser::parse(query, variables, *_alloc, &_result);
}
if (!_root)
@ -8519,9 +8614,9 @@ namespace pugi
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);
}
@ -8531,9 +8626,9 @@ namespace pugi
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);
}

View File

@ -325,6 +325,7 @@ namespace pugi
class xpath_node;
class xpath_node_set;
class xpath_query;
class xpath_variable_set;
#endif
/**
@ -1192,7 +1193,7 @@ namespace pugi
* \param query - query string
* \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
@ -1208,7 +1209,7 @@ namespace pugi
* \param query - query string
* \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
@ -1889,7 +1890,7 @@ namespace pugi
*
* \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