20
#include <boost/algorithm/string.hpp>
21
#include <boost/algorithm/string/split.hpp>
22
#include <boost/algorithm/string/classification.hpp>
23
#include <boost/algorithm/string/predicate.hpp>
24
#include <boost/bind.hpp>
26
21
#include "xpathselect.h"
22
#include "xpathquerypart.h"
28
25
namespace xpathselect
30
27
// anonymous namespace for internal-only utility class:
33
// Stores a part of an XPath query.
37
enum class QueryPartType {Normal, Search};
38
XPathQueryPart(std::string const& query_part)
40
type_ = (query_part == "") ? QueryPartType::Search : QueryPartType::Normal;
42
std::vector<std::string> part_pieces;
43
boost::algorithm::split(part_pieces,
45
boost::algorithm::is_any_of("[]="),
46
boost::algorithm::token_compress_on);
48
// Boost's split() implementation does not match it's documentation! According to the
49
// docs, it's not supposed to add empty strings, but it does, which is a PITA. This
50
// next line removes them:
51
part_pieces.erase( std::remove_if( part_pieces.begin(),
53
boost::bind( &std::string::empty, _1 ) ),
56
if (part_pieces.size() == 1)
58
node_name_ = part_pieces.at(0);
60
else if (part_pieces.size() == 3)
62
node_name_ = part_pieces.at(0);
63
param_name_ = part_pieces.at(1);
64
param_value_ = part_pieces.at(2);
68
// assume it's just a node name:
69
node_name_ = query_part;
73
bool Matches(Node::Ptr const& node) const
75
bool matches = (node_name_ == "*" || node->GetName() == node_name_);
76
if (!param_name_.empty())
78
matches &= node->MatchProperty(param_name_, param_value_);
84
QueryPartType Type() const { return type_; }
87
std::string node_name_;
88
std::string param_name_;
89
std::string param_value_;
93
typedef std::vector<XPathQueryPart> QueryList;
95
30
QueryList GetQueryPartsFromQuery(std::string const& query)
32
xpathselect::parser::xpath_grammar<std::string::const_iterator> grammar;
97
33
QueryList query_parts;
99
// split query into parts
100
std::list<std::string> query_strings;
101
boost::algorithm::split(query_strings,
103
boost::algorithm::is_any_of("/"),
104
boost::algorithm::token_compress_off);
106
for(std::string part : query_strings)
35
auto begin = query.cbegin();
36
auto end = query.cend();
37
if (boost::spirit::qi::parse(begin, end, grammar, query_parts) && (begin == end))
108
query_parts.push_back(XPathQueryPart(part));
111
// bost::split leaves the initial token in the output list, so we ignore the
112
// first search token:
113
if (query_parts.front().Type() == XPathQueryPart::QueryPartType::Search)
114
query_parts.erase(query_parts.begin());
119
44
// Starting at each node listed in 'start_points', search the tree for nodes that match
155
80
query = "/" + root->GetName();
157
// sanity checking some obvious invalid queries:
158
if (boost::algorithm::ends_with(query, "//"))
83
QueryList query_parts = GetQueryPartsFromQuery(query);
84
if (query_parts.empty())
159
85
return NodeList();
161
QueryList query_parts = GetQueryPartsFromQuery(query);
163
86
auto query_part = query_parts.cbegin();
164
87
NodeList start_nodes { root };
165
88
while (query_part != query_parts.cend())