XPath expressions may now yield values.
This commit is contained in:
parent
7c7e5c5e36
commit
51ef4d7914
5 changed files with 159 additions and 127 deletions
14
src/main.cc
14
src/main.cc
|
|
@ -268,17 +268,11 @@ static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
|||
xpath.print(*out, xml_document);
|
||||
*out << std::endl;
|
||||
|
||||
value_t result = xpath.calc(xml_document, report);
|
||||
|
||||
if (result.is_sequence()) {
|
||||
foreach (const value_t& value, result.as_sequence()) {
|
||||
if (value.is_xml_node()) {
|
||||
value.as_xml_node()->print(std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
foreach (const value_t& value, xpath.find_all(xml_document, report)) {
|
||||
if (value.is_xml_node()) {
|
||||
value.as_xml_node()->print(std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << result << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
10
src/value.cc
10
src/value.cc
|
|
@ -82,16 +82,6 @@ void value_t::shutdown()
|
|||
false_value = intrusive_ptr<storage_t>();
|
||||
}
|
||||
|
||||
value_t& value_t::operator=(const value_t& val)
|
||||
{
|
||||
if (this == &val || storage == val.storage)
|
||||
return *this;
|
||||
|
||||
storage = val.storage;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_t::operator bool() const
|
||||
{
|
||||
switch (type()) {
|
||||
|
|
|
|||
19
src/value.h
19
src/value.h
|
|
@ -229,7 +229,12 @@ public:
|
|||
TRACE_DTOR(value_t);
|
||||
}
|
||||
|
||||
value_t& operator=(const value_t& val);
|
||||
value_t& operator=(const value_t& val) {
|
||||
if (this == &val || storage == val.storage)
|
||||
return *this;
|
||||
storage = val.storage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* _dup() makes a private copy of the current value so that it can
|
||||
|
|
@ -427,6 +432,11 @@ public:
|
|||
assert(is_xml_node());
|
||||
return *(const xml::node_t **) storage->data;
|
||||
}
|
||||
template <typename T>
|
||||
T * as_xml_node() const {
|
||||
assert(is_xml_node());
|
||||
return *(T **) storage->data;
|
||||
}
|
||||
void set_xml_node(xml::node_t * val) {
|
||||
set_type(XML_NODE);
|
||||
*(xml::node_t **) storage->data = val;
|
||||
|
|
@ -592,6 +602,13 @@ public:
|
|||
friend std::ostream& operator<<(std::ostream& out, const value_t& val);
|
||||
};
|
||||
|
||||
template <>
|
||||
inline const xml::node_t * value_t::as_xml_node() const {
|
||||
assert(is_xml_node());
|
||||
assert(! is_type(CONST_XML_NODE));
|
||||
return *(const xml::node_t **) storage->data;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const value_t& val);
|
||||
|
||||
DECLARE_EXCEPTION(value_error);
|
||||
|
|
|
|||
141
src/xpath.cc
141
src/xpath.cc
|
|
@ -1484,7 +1484,10 @@ xpath_t::op_t::compile(const node_t& context, scope_t * scope, bool resolve)
|
|||
case O_RFIND:
|
||||
case NODE_ID:
|
||||
case NODE_NAME:
|
||||
return wrap_value(path_t(ptr_op_t(this)).find_all(context, scope));
|
||||
if (resolve)
|
||||
return wrap_value(path_t(ptr_op_t(this)).find_all(context, scope));
|
||||
else
|
||||
return this;
|
||||
|
||||
case O_PRED:
|
||||
assert(false); // this should never occur by itself
|
||||
|
|
@ -1981,13 +1984,13 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
|
|||
}
|
||||
}
|
||||
|
||||
template <typename NodeType, typename FuncType>
|
||||
void xpath_t::path_t::check_element(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const FuncType& func)
|
||||
template <typename NodeType>
|
||||
void xpath_t::path_t::check_element(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const visitor_t& func)
|
||||
{
|
||||
if (element->kind > op_t::TERMINALS &&
|
||||
element->left()->kind == op_t::O_PRED) {
|
||||
|
|
@ -1996,40 +1999,49 @@ void xpath_t::path_t::check_element(NodeType& start,
|
|||
return;
|
||||
}
|
||||
|
||||
if (element->kind < op_t::TERMINALS)
|
||||
func(start);
|
||||
else
|
||||
walk_elements<NodeType, FuncType>
|
||||
(start, element->right(), element->kind == op_t::O_RFIND, scope, func);
|
||||
if (element->kind < op_t::TERMINALS) {
|
||||
value_t temp(&start);
|
||||
assert(temp.is_xml_node());
|
||||
func(temp);
|
||||
} else {
|
||||
walk_elements<NodeType>(start, element->right(),
|
||||
element->kind == op_t::O_RFIND, scope, func);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename NodeType, typename FuncType>
|
||||
void xpath_t::path_t::walk_elements(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
scope_t * scope,
|
||||
const FuncType& func)
|
||||
template <typename NodeType>
|
||||
void xpath_t::path_t::walk_elements(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
scope_t * scope,
|
||||
const visitor_t& func)
|
||||
{
|
||||
ptr_op_t name(element->kind < op_t::TERMINALS ? element : element->left());
|
||||
if (name->kind == op_t::O_PRED)
|
||||
name = name->left();
|
||||
ptr_op_t name(element);
|
||||
|
||||
if (name->kind > op_t::TERMINALS &&
|
||||
(name->kind == op_t::O_FIND || name->kind == op_t::O_RFIND)) {
|
||||
name = element->left();
|
||||
|
||||
if (name->kind == op_t::O_PRED)
|
||||
name = name->left();
|
||||
}
|
||||
|
||||
switch (name->kind) {
|
||||
case op_t::NODE_ID:
|
||||
switch (name->as_name()) {
|
||||
case document_t::CURRENT:
|
||||
check_element(start, element, scope, 0, 1, func);
|
||||
check_element<NodeType>(start, element, scope, 0, 1, func);
|
||||
break;
|
||||
|
||||
case document_t::PARENT:
|
||||
if (optional<parent_node_t&> parent = start.parent())
|
||||
check_element(*parent, element, scope, 0, 1, func);
|
||||
check_element<NodeType>(*parent, element, scope, 0, 1, func);
|
||||
else
|
||||
throw_(std::logic_error, "Attempt to access parent of root node");
|
||||
break;
|
||||
|
||||
case document_t::ROOT:
|
||||
check_element(start.document(), element, scope, 0, 1, func);
|
||||
check_element<NodeType>(start.document(), element, scope, 0, 1, func);
|
||||
break;
|
||||
|
||||
case document_t::ALL: {
|
||||
|
|
@ -2038,8 +2050,8 @@ void xpath_t::path_t::walk_elements(NodeType& start,
|
|||
|
||||
std::size_t index = 0;
|
||||
std::size_t size = start.as_parent_node().size();
|
||||
foreach (node_t * node, start.as_parent_node())
|
||||
check_element(*node, element, scope, index++, size, func);
|
||||
foreach (NodeType * node, start.as_parent_node())
|
||||
check_element<NodeType>(*node, element, scope, index++, size, func);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -2054,27 +2066,63 @@ void xpath_t::path_t::walk_elements(NodeType& start,
|
|||
|
||||
std::size_t index = 0;
|
||||
std::size_t size = start.as_parent_node().size();
|
||||
foreach (node_t * child, start.as_parent_node()) {
|
||||
foreach (NodeType * child, start.as_parent_node()) {
|
||||
if ((have_name_id && name->as_name() == child->name_id()) ||
|
||||
(! have_name_id && name->as_string() == child->name()))
|
||||
check_element(*child, element, scope, index++, size, func);
|
||||
check_element<NodeType>(*child, element, scope, index++, size, func);
|
||||
else if (recurse)
|
||||
walk_elements(*child, element, recurse, scope, func);
|
||||
walk_elements<NodeType>(*child, element, recurse, scope, func);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// jww (2007-05-15): Instead of a name, this might be an
|
||||
// expression resulting in a nodelist with respect to the current
|
||||
// context.
|
||||
throw_(std::logic_error, "XPath expression is not strictly a path selection");
|
||||
default: {
|
||||
xpath_t final(name->compile(start, scope, true));
|
||||
|
||||
if (final.ptr->is_value()) {
|
||||
value_t& result(final.ptr->as_value());
|
||||
|
||||
if (result.is_xml_node()) {
|
||||
check_element<NodeType>(*result.template as_xml_node<NodeType>(),
|
||||
element, scope, 0, 1, func);
|
||||
}
|
||||
else if (result.is_sequence()) {
|
||||
std::size_t index = 0;
|
||||
std::size_t size = start.as_parent_node().size();
|
||||
|
||||
foreach (const value_t& value, result.as_sequence()) {
|
||||
if (value.is_xml_node()) {
|
||||
check_element<NodeType>(*value.template as_xml_node<NodeType>(),
|
||||
element, scope, index++, size, func);
|
||||
|
||||
// Apply to every child if this part is recursive
|
||||
if (recurse)
|
||||
walk_elements<NodeType>(*value.template as_xml_node<NodeType>(),
|
||||
element, recurse, scope, func);
|
||||
} else {
|
||||
if (element->kind > op_t::TERMINALS)
|
||||
throw_(compile_error,
|
||||
"Non-final expression in XPath selection returns non-node");
|
||||
func(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (element->kind > op_t::TERMINALS)
|
||||
throw_(compile_error,
|
||||
"Non-final expression in XPath selection returns non-node");
|
||||
func(result);
|
||||
}
|
||||
} else {
|
||||
throw_(compile_error, "Expression in XPath selection is invalid");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
void xpath_t::path_t::walk_elements<node_t, xpath_t::path_t::visitor_t>
|
||||
void xpath_t::path_t::walk_elements<node_t>
|
||||
(node_t& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
|
|
@ -2082,30 +2130,13 @@ void xpath_t::path_t::walk_elements<node_t, xpath_t::path_t::visitor_t>
|
|||
const visitor_t& func);
|
||||
|
||||
template
|
||||
void xpath_t::path_t::walk_elements<const node_t, xpath_t::path_t::const_visitor_t>
|
||||
(const node_t& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
scope_t * scope,
|
||||
const const_visitor_t& func);
|
||||
|
||||
template
|
||||
void xpath_t::path_t::check_element<node_t, xpath_t::path_t::visitor_t>
|
||||
(node_t& start,
|
||||
void xpath_t::path_t::check_element<const node_t>
|
||||
(const node_t& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const visitor_t& func);
|
||||
|
||||
template
|
||||
void xpath_t::path_t::check_element<const node_t, xpath_t::path_t::const_visitor_t>
|
||||
(const node_t& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const const_visitor_t& func);
|
||||
|
||||
} // namespace xml
|
||||
} // namespace ledger
|
||||
|
|
|
|||
102
src/xpath.h
102
src/xpath.h
|
|
@ -216,45 +216,36 @@ private:
|
|||
public:
|
||||
class path_t
|
||||
{
|
||||
typedef function<void (node_t&)> visitor_t;
|
||||
typedef function<void (const node_t&)> const_visitor_t;
|
||||
|
||||
public:
|
||||
typedef function<void (const value_t&)> visitor_t;
|
||||
typedef function<bool (const node_t&, scope_t *)> predicate_t;
|
||||
|
||||
struct value_node_appender_t {
|
||||
private:
|
||||
struct value_appender_t {
|
||||
value_t::sequence_t& sequence;
|
||||
value_node_appender_t(value_t::sequence_t& _sequence)
|
||||
value_appender_t(value_t::sequence_t& _sequence)
|
||||
: sequence(_sequence) {}
|
||||
void operator()(node_t& node) {
|
||||
sequence.push_back(&node);
|
||||
}
|
||||
};
|
||||
|
||||
struct const_value_node_appender_t {
|
||||
value_t::sequence_t& sequence;
|
||||
const_value_node_appender_t(value_t::sequence_t& _sequence)
|
||||
: sequence(_sequence) {}
|
||||
void operator()(const node_t& node) {
|
||||
sequence.push_back(&node);
|
||||
void operator()(const value_t& val) {
|
||||
sequence.push_back(val);
|
||||
}
|
||||
};
|
||||
|
||||
ptr_op_t path_expr;
|
||||
|
||||
template <typename NodeType, typename FuncType>
|
||||
void walk_elements(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
scope_t * scope,
|
||||
const FuncType& func);
|
||||
template <typename NodeType>
|
||||
void walk_elements(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
const bool recurse,
|
||||
scope_t * scope,
|
||||
const visitor_t& func);
|
||||
|
||||
template <typename NodeType, typename FuncType>
|
||||
void check_element(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const FuncType& func);
|
||||
template <typename NodeType>
|
||||
void check_element(NodeType& start,
|
||||
const ptr_op_t& element,
|
||||
scope_t * scope,
|
||||
std::size_t index,
|
||||
std::size_t size,
|
||||
const visitor_t& func);
|
||||
|
||||
public:
|
||||
path_t(const xpath_t& xpath) : path_expr(xpath.ptr) {}
|
||||
|
|
@ -262,53 +253,53 @@ public:
|
|||
|
||||
value_t find_all(node_t& start, scope_t * scope) {
|
||||
value_t result = value_t::sequence_t();
|
||||
visit(start, scope, value_node_appender_t(result.as_sequence_lval()));
|
||||
visit(start, scope, value_appender_t(result.as_sequence_lval()));
|
||||
return result;
|
||||
}
|
||||
value_t find_all(const node_t& start, scope_t * scope) {
|
||||
value_t result = value_t::sequence_t();
|
||||
visit(start, scope,
|
||||
const_value_node_appender_t(result.as_sequence_lval()));
|
||||
visit(start, scope, value_appender_t(result.as_sequence_lval()));
|
||||
return result;
|
||||
}
|
||||
|
||||
void visit(node_t& start, scope_t * scope, const visitor_t& func) {
|
||||
if (path_expr)
|
||||
walk_elements<node_t, visitor_t>
|
||||
(start, path_expr, false, scope, func);
|
||||
walk_elements<node_t>(start, path_expr, false, scope, func);
|
||||
}
|
||||
void visit(const node_t& start, scope_t * scope,
|
||||
const const_visitor_t& func) {
|
||||
void visit(const node_t& start, scope_t * scope, const visitor_t& func) {
|
||||
if (path_expr)
|
||||
walk_elements<const node_t, const_visitor_t>
|
||||
(start, path_expr, false, scope, func);
|
||||
walk_elements<const node_t>(start, path_expr, false, scope, func);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NodeType>
|
||||
class path_iterator_t
|
||||
{
|
||||
typedef NodeType * pointer;
|
||||
typedef NodeType& reference;
|
||||
|
||||
path_t path;
|
||||
node_t& start;
|
||||
reference start;
|
||||
scope_t * scope;
|
||||
|
||||
mutable std::vector<node_t *> sequence;
|
||||
mutable value_t::sequence_t sequence;
|
||||
mutable bool searched;
|
||||
|
||||
struct node_appender_t {
|
||||
std::vector<node_t *>& sequence;
|
||||
node_appender_t(std::vector<node_t *>& _sequence)
|
||||
value_t::sequence_t& sequence;
|
||||
node_appender_t(value_t::sequence_t& _sequence)
|
||||
: sequence(_sequence) {}
|
||||
void operator()(node_t& node) {
|
||||
sequence.push_back(&node);
|
||||
void operator()(const value_t& node) {
|
||||
sequence.push_back(node);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::vector<node_t *>::iterator iterator;
|
||||
typedef std::vector<node_t *>::const_iterator const_iterator;
|
||||
typedef value_t::sequence_t::iterator iterator;
|
||||
typedef value_t::sequence_t::const_iterator const_iterator;
|
||||
|
||||
path_iterator_t(const xpath_t& path_expr,
|
||||
node_t& _start, scope_t * _scope)
|
||||
reference _start, scope_t * _scope)
|
||||
: path(path_expr), start(_start), scope(_scope),
|
||||
searched(false) {
|
||||
}
|
||||
|
|
@ -753,18 +744,27 @@ public:
|
|||
return xpath_t(_expr).calc(context, scope);
|
||||
}
|
||||
|
||||
path_iterator_t find_all(node_t& start, scope_t * scope) {
|
||||
return path_iterator_t(*this, start, scope);
|
||||
path_iterator_t<node_t>
|
||||
find_all(node_t& start, scope_t * scope) {
|
||||
return path_iterator_t<node_t>(*this, start, scope);
|
||||
}
|
||||
path_iterator_t<const node_t>
|
||||
find_all(const node_t& start, scope_t * scope) {
|
||||
return path_iterator_t<const node_t>(*this, start, scope);
|
||||
}
|
||||
|
||||
void visit(node_t& start, scope_t * scope,
|
||||
const function<void (node_t&)>& func) {
|
||||
void visit(node_t& start, scope_t * scope, const path_t::visitor_t& func) {
|
||||
path_t(*this).visit(start, scope, func);
|
||||
}
|
||||
void visit(const node_t& start, scope_t * scope, const
|
||||
path_t::visitor_t& func) {
|
||||
path_t(*this).visit(start, scope, func);
|
||||
}
|
||||
|
||||
void print(std::ostream& out, xml::document_t& document) const {
|
||||
print(out, document, true, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void dump(std::ostream& out) const {
|
||||
if (ptr)
|
||||
ptr->dump(out, 0);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue