Started working on an XPath visitor class
This commit is contained in:
parent
9e55655e0c
commit
3244c693f8
4 changed files with 218 additions and 30 deletions
22
src/main.cc
22
src/main.cc
|
|
@ -46,6 +46,10 @@
|
||||||
#include <fdstream.hpp>
|
#include <fdstream.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void print_node(ledger::xml::node_t& node) {
|
||||||
|
node.print(std::cout);
|
||||||
|
}
|
||||||
|
|
||||||
static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
||||||
char * envp[])
|
char * envp[])
|
||||||
{
|
{
|
||||||
|
|
@ -269,12 +273,28 @@ static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
||||||
xpath.print(*out, xml_document);
|
xpath.print(*out, xml_document);
|
||||||
*out << std::endl;
|
*out << std::endl;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
try {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
#else
|
||||||
value_t nodelist;
|
value_t nodelist;
|
||||||
xpath.calc(nodelist, xml_document, report);
|
xpath.calc(nodelist, xml_document, report);
|
||||||
|
|
||||||
foreach (const value_t& node, nodelist.as_sequence())
|
foreach (const value_t& node, nodelist.as_sequence())
|
||||||
node.as_xml_node()->print(*out);
|
node.as_xml_node()->print(*out);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1440,9 +1440,12 @@ value_t value_t::strip_annotations(const bool keep_price,
|
||||||
case POINTER:
|
case POINTER:
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
case SEQUENCE:
|
case SEQUENCE: {
|
||||||
assert(false); // jww (2006-09-28): strip them all!
|
sequence_t temp;
|
||||||
break;
|
foreach (const value_t& value, as_sequence())
|
||||||
|
temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag));
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
case AMOUNT:
|
case AMOUNT:
|
||||||
return as_amount().strip_annotations
|
return as_amount().strip_annotations
|
||||||
|
|
|
||||||
92
src/xpath.cc
92
src/xpath.cc
|
|
@ -1143,8 +1143,8 @@ void xpath_t::op_t::append_value(value_t& val,
|
||||||
result_seq.push_back(val);
|
result_seq.push_back(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
xpath_t::ptr_op_t xpath_t::op_t::compile(value_t& context, scope_t * scope,
|
xpath_t::ptr_op_t
|
||||||
bool resolve)
|
xpath_t::op_t::compile(value_t& context, scope_t * scope, bool resolve)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
try {
|
try {
|
||||||
|
|
@ -1661,7 +1661,6 @@ xpath_t::ptr_op_t xpath_t::op_t::compile(value_t& context, scope_t * scope,
|
||||||
throw_(compile_error, "Attempting to apply path selection "
|
throw_(compile_error, "Attempting to apply path selection "
|
||||||
"to non-node(s)");
|
"to non-node(s)");
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrap_value(result_seq);
|
return wrap_value(result_seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2156,5 +2155,92 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xpath_t::path_t::path_t(const xpath_t& path_expr)
|
||||||
|
{
|
||||||
|
ptr_op_t op = path_expr.ptr;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (op->kind) {
|
||||||
|
case op_t::O_FIND:
|
||||||
|
case op_t::O_RFIND:
|
||||||
|
case op_t::O_PRED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case op_t::NODE_ID:
|
||||||
|
case op_t::NODE_NAME:
|
||||||
|
case op_t::ATTR_ID:
|
||||||
|
case op_t::ATTR_NAME:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw_(std::logic_error, "XPath expression is not strictly a path selection");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void xpath_t::path_t::check_element(node_t& start,
|
||||||
|
const element_iterator& element,
|
||||||
|
scope_t * scope,
|
||||||
|
const visitor_t& func)
|
||||||
|
{
|
||||||
|
if (! element->predicate || element->predicate(start, scope)) {
|
||||||
|
element_iterator next_element = next(element);
|
||||||
|
if (next_element == elements.end())
|
||||||
|
func(start);
|
||||||
|
else
|
||||||
|
walk_elements(start, next_element, scope, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void xpath_t::path_t::walk_elements(node_t& start,
|
||||||
|
const element_iterator& element,
|
||||||
|
scope_t * scope,
|
||||||
|
const visitor_t& func)
|
||||||
|
{
|
||||||
|
if (element->ident.type() == typeid(document_t::special_names_t)) {
|
||||||
|
switch (boost::get<document_t::special_names_t>(element->ident)) {
|
||||||
|
case document_t::CURRENT:
|
||||||
|
check_element(start, element, scope, func);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case document_t::PARENT:
|
||||||
|
if (optional<parent_node_t&> parent = start.parent())
|
||||||
|
check_element(*parent, element, scope, func);
|
||||||
|
else
|
||||||
|
throw_(std::logic_error, "Attempt to access parent of root node");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case document_t::ROOT:
|
||||||
|
check_element(start.document(), element, scope, func);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case document_t::ALL:
|
||||||
|
if (! start.is_parent_node())
|
||||||
|
throw_(compile_error, "Referencing child nodes from a non-parent value");
|
||||||
|
|
||||||
|
foreach (node_t * node, start.as_parent_node())
|
||||||
|
check_element(*node, element, scope, func);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (start.is_parent_node()) {
|
||||||
|
bool have_name_id = element->ident.type() == typeid(node_t::nameid_t);
|
||||||
|
|
||||||
|
foreach (node_t * child, start.as_parent_node()) {
|
||||||
|
if ((have_name_id &&
|
||||||
|
boost::get<node_t::nameid_t>(element->ident) == child->name_id()) ||
|
||||||
|
(! have_name_id &&
|
||||||
|
boost::get<string>(element->ident) == child->name()))
|
||||||
|
check_element(*child, element, scope, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element->recurse && start.is_parent_node())
|
||||||
|
foreach (node_t * child, start.as_parent_node())
|
||||||
|
walk_elements(*child, element, scope, func);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xml
|
} // namespace xml
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
79
src/xpath.h
79
src/xpath.h
|
|
@ -212,6 +212,55 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
class path_t : public noncopyable
|
||||||
|
{
|
||||||
|
public: // jww (2007-05-14): for testing
|
||||||
|
typedef function<void (node_t&)> visitor_t;
|
||||||
|
typedef function<bool (node_t&, scope_t *)> predicate_t;
|
||||||
|
|
||||||
|
struct element_t
|
||||||
|
{
|
||||||
|
variant<node_t::nameid_t, string,
|
||||||
|
document_t::special_names_t> ident;
|
||||||
|
|
||||||
|
bool recurse;
|
||||||
|
predicate_t predicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::list<element_t> elements;
|
||||||
|
|
||||||
|
typedef std::list<element_t>::const_iterator element_iterator;
|
||||||
|
|
||||||
|
struct node_appender_t {
|
||||||
|
value_t::sequence_t& sequence;
|
||||||
|
node_appender_t(value_t::sequence_t& _sequence)
|
||||||
|
: sequence(_sequence) {}
|
||||||
|
void operator()(node_t& node) {
|
||||||
|
sequence.push_back(&node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
path_t(const xpath_t& path_expr);
|
||||||
|
|
||||||
|
void find_all(value_t::sequence_t& result,
|
||||||
|
node_t& start, scope_t * scope) {
|
||||||
|
visit(start, scope, node_appender_t(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(node_t& start, scope_t * scope,
|
||||||
|
const function<void (node_t&)>& func) {
|
||||||
|
if (elements.begin() != elements.end())
|
||||||
|
walk_elements(start, elements.begin(), scope, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void walk_elements(node_t& start, const element_iterator& element,
|
||||||
|
scope_t * scope, const function<void (node_t&)>& func);
|
||||||
|
void check_element(node_t& start, const element_iterator& element,
|
||||||
|
scope_t * scope, const function<void (node_t&)>& func);
|
||||||
|
};
|
||||||
|
|
||||||
struct op_t : public noncopyable
|
struct op_t : public noncopyable
|
||||||
{
|
{
|
||||||
enum kind_t {
|
enum kind_t {
|
||||||
|
|
@ -366,6 +415,22 @@ public:
|
||||||
data = val;
|
data = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool is_path() const {
|
||||||
|
return kind == PATH;
|
||||||
|
}
|
||||||
|
path_t& as_path() {
|
||||||
|
assert(kind == PATH);
|
||||||
|
return boost::get<path_t>(data);
|
||||||
|
}
|
||||||
|
const path_t& as_path() const {
|
||||||
|
return const_cast<op_t *>(this)->as_path();
|
||||||
|
}
|
||||||
|
void set_path(const path_t& val) {
|
||||||
|
data = val;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ptr_op_t& as_op() {
|
ptr_op_t& as_op() {
|
||||||
assert(kind > TERMINALS);
|
assert(kind > TERMINALS);
|
||||||
return boost::get<ptr_op_t>(data);
|
return boost::get<ptr_op_t>(data);
|
||||||
|
|
@ -437,6 +502,20 @@ public:
|
||||||
void dump(std::ostream& out, const int depth) const;
|
void dump(std::ostream& out, const int depth) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class op_predicate
|
||||||
|
{
|
||||||
|
ptr_op_t op;
|
||||||
|
|
||||||
|
public:
|
||||||
|
op_predicate(ptr_op_t _op) : op(_op) {}
|
||||||
|
|
||||||
|
bool operator()(node_t& node, scope_t * scope) {
|
||||||
|
value_t context_node(&node);
|
||||||
|
xpath_t result(op->compile(context_node, scope, true));
|
||||||
|
return result.ptr->as_value().to_boolean();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ptr_op_t ptr;
|
ptr_op_t ptr;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue