XPath expressions may now yield values.

This commit is contained in:
John Wiegley 2007-05-16 05:38:49 +00:00
parent 7c7e5c5e36
commit 51ef4d7914
5 changed files with 159 additions and 127 deletions

View file

@ -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;
}

View file

@ -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()) {

View file

@ -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);

View file

@ -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

View file

@ -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);