The XPath visitor class is now working
This commit is contained in:
parent
ff43b1d135
commit
7747a8f93b
5 changed files with 120 additions and 60 deletions
|
|
@ -100,7 +100,7 @@ public:
|
||||||
// Ids 0-9 are reserved. 10-999 are for "builtin" names. 1000+ are
|
// Ids 0-9 are reserved. 10-999 are for "builtin" names. 1000+ are
|
||||||
// for dynamically registered names.
|
// for dynamically registered names.
|
||||||
enum special_names_t {
|
enum special_names_t {
|
||||||
CURRENT, PARENT, ROOT, ALL
|
CURRENT, PARENT, ROOT, ALL, LAST_BUILTIN = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
document_t(node_t::nameid_t _name_id)
|
document_t(node_t::nameid_t _name_id)
|
||||||
|
|
|
||||||
|
|
@ -276,13 +276,6 @@ static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
||||||
#if 1
|
#if 1
|
||||||
try {
|
try {
|
||||||
xml::xpath_t::path_t path_selection(xpath);
|
xml::xpath_t::path_t path_selection(xpath);
|
||||||
|
|
||||||
xml::xpath_t::path_t::element_t elem;
|
|
||||||
elem.ident = xml::document_t::ROOT;
|
|
||||||
path_selection.elements.push_back(elem);
|
|
||||||
elem.ident = xml::TRANSACTION_NODE;
|
|
||||||
elem.recurse = true;
|
|
||||||
path_selection.elements.push_back(elem);
|
|
||||||
path_selection.visit(xml_document, report, bind(print_node, _1));
|
path_selection.visit(xml_document, report, bind(print_node, _1));
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,10 @@ public:
|
||||||
return children.get<0>().end();
|
return children.get<0>().end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t size() const {
|
||||||
|
return children.get<0>().size();
|
||||||
|
}
|
||||||
|
|
||||||
children_by_nameid::iterator begin(nameid_t _name_id) {
|
children_by_nameid::iterator begin(nameid_t _name_id) {
|
||||||
return std::find_if(children.get<1>().begin(),
|
return std::find_if(children.get<1>().begin(),
|
||||||
children.get<1>().end(), match_nameid(_name_id));
|
children.get<1>().end(), match_nameid(_name_id));
|
||||||
|
|
|
||||||
112
src/xpath.cc
112
src/xpath.cc
|
|
@ -490,7 +490,7 @@ bool xpath_t::function_scope_t::resolve(const string& name,
|
||||||
switch (name[0]) {
|
switch (name[0]) {
|
||||||
case 'l':
|
case 'l':
|
||||||
if (name == "last") {
|
if (name == "last") {
|
||||||
result = (long)sequence.size();
|
result = (long)size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -504,10 +504,7 @@ bool xpath_t::function_scope_t::resolve(const string& name,
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
if (name == "text") {
|
if (name == "text") {
|
||||||
if (value.type == value_t::XML_NODE)
|
result = node.to_value();
|
||||||
result = value.as_xml_node()->to_value();
|
|
||||||
else
|
|
||||||
throw_(calc_error, "Attempt to call text() on a non-node value");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1622,12 +1619,13 @@ xpath_t::op_t::compile(value_t& context, scope_t * scope, bool resolve)
|
||||||
// jww (2006-09-24): What about when nothing is found?
|
// jww (2006-09-24): What about when nothing is found?
|
||||||
switch (lexpr.ptr->as_value().type) {
|
switch (lexpr.ptr->as_value().type) {
|
||||||
case value_t::XML_NODE: {
|
case value_t::XML_NODE: {
|
||||||
function_scope_t xpath_fscope(lexpr.ptr->as_value(), 0, scope);
|
value_t& value(lexpr.ptr->as_value());
|
||||||
|
function_scope_t xpath_fscope(*value.as_xml_node(), 0, 1, scope);
|
||||||
if (kind == O_PRED) {
|
if (kind == O_PRED) {
|
||||||
if (rexpr.ptr->test_value(lexpr.ptr->as_value(), &xpath_fscope))
|
if (rexpr.ptr->test_value(value, &xpath_fscope))
|
||||||
result_seq.push_back(lexpr.ptr->as_value());
|
result_seq.push_back(value);
|
||||||
} else {
|
} else {
|
||||||
rexpr.ptr->find_values(lexpr.ptr->as_value(), &xpath_fscope, result_seq,
|
rexpr.ptr->find_values(value, &xpath_fscope, result_seq,
|
||||||
kind == O_RFIND);
|
kind == O_RFIND);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1645,7 +1643,7 @@ xpath_t::op_t::compile(value_t& context, scope_t * scope, bool resolve)
|
||||||
throw_(compile_error, "Attempting to apply path selection "
|
throw_(compile_error, "Attempting to apply path selection "
|
||||||
"to non-node(s)");
|
"to non-node(s)");
|
||||||
|
|
||||||
function_scope_t xpath_fscope(seq, &(*i), index, scope);
|
function_scope_t xpath_fscope(seq, *(*i).as_xml_node(), index, scope);
|
||||||
if (kind == O_PRED) {
|
if (kind == O_PRED) {
|
||||||
if (rexpr.ptr->test_value(*i, &xpath_fscope, index))
|
if (rexpr.ptr->test_value(*i, &xpath_fscope, index))
|
||||||
result_seq.push_back(*i);
|
result_seq.push_back(*i);
|
||||||
|
|
@ -2158,38 +2156,87 @@ xpath_t::path_t::path_t(const xpath_t& path_expr)
|
||||||
ptr_op_t op = path_expr.ptr;
|
ptr_op_t op = path_expr.ptr;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (op->kind) {
|
element_t element;
|
||||||
case op_t::O_FIND:
|
|
||||||
case op_t::O_RFIND:
|
|
||||||
case op_t::O_PRED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case op_t::NODE_ID:
|
switch (op->kind) {
|
||||||
|
case op_t::O_RFIND:
|
||||||
|
element.recurse = true;
|
||||||
|
// fall through...
|
||||||
|
case op_t::O_FIND: {
|
||||||
|
ptr_op_t name;
|
||||||
|
if (op->right()->kind == op_t::O_PRED) {
|
||||||
|
element.predicate = op_predicate(op->right()->right());
|
||||||
|
name = op->right()->left();
|
||||||
|
} else {
|
||||||
|
name = op->right();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (name->kind) {
|
||||||
|
case op_t::NODE_ID: {
|
||||||
|
//case op_t::ATTR_ID:
|
||||||
|
node_t::nameid_t name_id = name->as_name();
|
||||||
|
if (name_id < document_t::LAST_BUILTIN)
|
||||||
|
element.ident = document_t::special_names_t(name_id);
|
||||||
|
else
|
||||||
|
element.ident = name_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case op_t::NODE_NAME:
|
case op_t::NODE_NAME:
|
||||||
case op_t::ATTR_ID:
|
//case op_t::ATTR_NAME:
|
||||||
case op_t::ATTR_NAME:
|
element.ident = name->as_string();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case op_t::NODE_ID: {
|
||||||
|
//case op_t::ATTR_ID:
|
||||||
|
node_t::nameid_t name_id = op->as_name();
|
||||||
|
if (name_id < document_t::LAST_BUILTIN)
|
||||||
|
element.ident = document_t::special_names_t(name_id);
|
||||||
|
else
|
||||||
|
element.ident = name_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case op_t::NODE_NAME:
|
||||||
|
//case op_t::ATTR_NAME:
|
||||||
|
element.ident = op->as_string();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw_(std::logic_error, "XPath expression is not strictly a path selection");
|
throw_(std::logic_error, "XPath expression is not strictly a path selection");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elements.push_front(element);
|
||||||
|
|
||||||
|
if (op->kind < op_t::TERMINALS)
|
||||||
break;
|
break;
|
||||||
|
else
|
||||||
|
op = op->left();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void xpath_t::path_t::check_element(node_t& start,
|
void xpath_t::path_t::check_element(node_t& start,
|
||||||
const element_iterator& element,
|
const element_iterator& element,
|
||||||
scope_t * scope,
|
scope_t * scope,
|
||||||
|
std::size_t index,
|
||||||
|
std::size_t size,
|
||||||
const visitor_t& func)
|
const visitor_t& func)
|
||||||
{
|
{
|
||||||
if (! element->predicate || element->predicate(start, scope)) {
|
if (element->predicate) {
|
||||||
|
function_scope_t xpath_fscope(start, index, size, scope);
|
||||||
|
if (! element->predicate(start, &xpath_fscope))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
element_iterator next_element = next(element);
|
element_iterator next_element = next(element);
|
||||||
if (next_element == elements.end())
|
if (next_element == elements.end())
|
||||||
func(start);
|
func(start);
|
||||||
else
|
else
|
||||||
walk_elements(start, next_element, scope, func);
|
walk_elements(start, next_element, scope, func);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void xpath_t::path_t::walk_elements(node_t& start,
|
void xpath_t::path_t::walk_elements(node_t& start,
|
||||||
|
|
@ -2200,44 +2247,47 @@ void xpath_t::path_t::walk_elements(node_t& start,
|
||||||
if (element->ident.type() == typeid(document_t::special_names_t)) {
|
if (element->ident.type() == typeid(document_t::special_names_t)) {
|
||||||
switch (boost::get<document_t::special_names_t>(element->ident)) {
|
switch (boost::get<document_t::special_names_t>(element->ident)) {
|
||||||
case document_t::CURRENT:
|
case document_t::CURRENT:
|
||||||
check_element(start, element, scope, func);
|
check_element(start, element, scope, 0, 1, func);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case document_t::PARENT:
|
case document_t::PARENT:
|
||||||
if (optional<parent_node_t&> parent = start.parent())
|
if (optional<parent_node_t&> parent = start.parent())
|
||||||
check_element(*parent, element, scope, func);
|
check_element(*parent, element, scope, 0, 1, func);
|
||||||
else
|
else
|
||||||
throw_(std::logic_error, "Attempt to access parent of root node");
|
throw_(std::logic_error, "Attempt to access parent of root node");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case document_t::ROOT:
|
case document_t::ROOT:
|
||||||
check_element(start.document(), element, scope, func);
|
check_element(start.document(), element, scope, 0, 1, func);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case document_t::ALL:
|
case document_t::ALL: {
|
||||||
if (! start.is_parent_node())
|
if (! start.is_parent_node())
|
||||||
throw_(compile_error, "Referencing child nodes from a non-parent value");
|
throw_(compile_error, "Referencing child nodes from a non-parent value");
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
std::size_t size = start.as_parent_node().size();
|
||||||
foreach (node_t * node, start.as_parent_node())
|
foreach (node_t * node, start.as_parent_node())
|
||||||
check_element(*node, element, scope, func);
|
check_element(*node, element, scope, index++, size, func);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (start.is_parent_node()) {
|
else if (start.is_parent_node()) {
|
||||||
bool have_name_id = element->ident.type() == typeid(node_t::nameid_t);
|
bool have_name_id = element->ident.type() == typeid(node_t::nameid_t);
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
std::size_t size = start.as_parent_node().size();
|
||||||
foreach (node_t * child, start.as_parent_node()) {
|
foreach (node_t * child, start.as_parent_node()) {
|
||||||
if ((have_name_id &&
|
if ((have_name_id &&
|
||||||
boost::get<node_t::nameid_t>(element->ident) == child->name_id()) ||
|
boost::get<node_t::nameid_t>(element->ident) == child->name_id()) ||
|
||||||
(! have_name_id &&
|
(! have_name_id &&
|
||||||
boost::get<string>(element->ident) == child->name()))
|
boost::get<string>(element->ident) == child->name()))
|
||||||
check_element(*child, element, scope, func);
|
check_element(*child, element, scope, index++, size, func);
|
||||||
}
|
else if (element->recurse)
|
||||||
}
|
|
||||||
|
|
||||||
if (element->recurse && start.is_parent_node())
|
|
||||||
foreach (node_t * child, start.as_parent_node())
|
|
||||||
walk_elements(*child, element, scope, func);
|
walk_elements(*child, element, scope, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xml
|
} // namespace xml
|
||||||
|
|
|
||||||
47
src/xpath.h
47
src/xpath.h
|
|
@ -99,19 +99,21 @@ public:
|
||||||
|
|
||||||
class function_scope_t : public scope_t
|
class function_scope_t : public scope_t
|
||||||
{
|
{
|
||||||
value_t::sequence_t sequence;
|
node_t& node;
|
||||||
value_t value;
|
std::size_t index;
|
||||||
int index;
|
std::size_t size;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
function_scope_t(const value_t::sequence_t& _sequence,
|
function_scope_t(const value_t::sequence_t& _sequence,
|
||||||
value_t * _value, int _index,
|
node_t& _node, std::size_t _index,
|
||||||
scope_t * _parent = NULL)
|
scope_t * _parent = NULL)
|
||||||
: scope_t(_parent, STATIC),
|
: scope_t(_parent, STATIC), node(_node), index(_index),
|
||||||
sequence(_sequence), value(_value), index(_index) {}
|
size(_sequence.size()) {}
|
||||||
function_scope_t(const value_t& _value, int _index,
|
|
||||||
scope_t * _parent = NULL)
|
function_scope_t(node_t& _node, std::size_t _index,
|
||||||
: scope_t(_parent, STATIC), value(_value), index(_index) {}
|
std::size_t _size, scope_t * _parent = NULL)
|
||||||
|
: scope_t(_parent, STATIC), node(_node), index(_index),
|
||||||
|
size(_size) {}
|
||||||
|
|
||||||
virtual bool resolve(const string& name, value_t& result,
|
virtual bool resolve(const string& name, value_t& result,
|
||||||
scope_t * locals = NULL);
|
scope_t * locals = NULL);
|
||||||
|
|
@ -212,9 +214,8 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class path_t : public noncopyable
|
class path_t
|
||||||
{
|
{
|
||||||
public: // jww (2007-05-14): for testing
|
|
||||||
typedef function<void (node_t&)> visitor_t;
|
typedef function<void (node_t&)> visitor_t;
|
||||||
typedef function<bool (node_t&, scope_t *)> predicate_t;
|
typedef function<bool (node_t&, scope_t *)> predicate_t;
|
||||||
|
|
||||||
|
|
@ -233,7 +234,7 @@ public:
|
||||||
|
|
||||||
struct value_node_appender_t {
|
struct value_node_appender_t {
|
||||||
value_t::sequence_t& sequence;
|
value_t::sequence_t& sequence;
|
||||||
node_appender_t(value_t::sequence_t& _sequence)
|
value_node_appender_t(value_t::sequence_t& _sequence)
|
||||||
: sequence(_sequence) {}
|
: sequence(_sequence) {}
|
||||||
void operator()(node_t& node) {
|
void operator()(node_t& node) {
|
||||||
sequence.push_back(&node);
|
sequence.push_back(&node);
|
||||||
|
|
@ -255,10 +256,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void walk_elements(node_t& start, const element_iterator& element,
|
void walk_elements(node_t& start,
|
||||||
scope_t * scope, const function<void (node_t&)>& func);
|
const element_iterator& element,
|
||||||
void check_element(node_t& start, const element_iterator& element,
|
scope_t * scope,
|
||||||
scope_t * scope, const function<void (node_t&)>& func);
|
const visitor_t& func);
|
||||||
|
|
||||||
|
void check_element(node_t& start,
|
||||||
|
const element_iterator& element,
|
||||||
|
scope_t * scope,
|
||||||
|
std::size_t index,
|
||||||
|
std::size_t size,
|
||||||
|
const visitor_t& func);
|
||||||
};
|
};
|
||||||
|
|
||||||
class path_iterator_t
|
class path_iterator_t
|
||||||
|
|
@ -279,7 +287,8 @@ public:
|
||||||
typedef std::vector<node_t *>::iterator iterator;
|
typedef std::vector<node_t *>::iterator iterator;
|
||||||
typedef std::vector<node_t *>::const_iterator const_iterator;
|
typedef std::vector<node_t *>::const_iterator const_iterator;
|
||||||
|
|
||||||
path_iterator_t(const xpath_t& path_expr, node_t& start, scope_t * scope)
|
path_iterator_t(const xpath_t& path_expr,
|
||||||
|
node_t& start, scope_t * scope)
|
||||||
: path(path_expr) {
|
: path(path_expr) {
|
||||||
path.visit(start, scope, node_appender_t(sequence));
|
path.visit(start, scope, node_appender_t(sequence));
|
||||||
}
|
}
|
||||||
|
|
@ -746,6 +755,10 @@ public:
|
||||||
path.visit(start, scope, func);
|
path.visit(start, scope, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path_iterator_t sequence(node_t& start, scope_t * scope) {
|
||||||
|
return path_iterator_t(*this, start, scope);
|
||||||
|
}
|
||||||
|
|
||||||
void print(std::ostream& out, xml::document_t& document) const {
|
void print(std::ostream& out, xml::document_t& document) const {
|
||||||
print(out, document, true, NULL, NULL, NULL);
|
print(out, document, true, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue