100
101
boost::algorithm::split(query_strings,
102
103
boost::algorithm::is_any_of("/"),
103
boost::algorithm::token_compress_on);
105
// Boost's split() implementation does not match it's documentation! According to the
106
// docs, it's not supposed to add empty strings, but it does, which is a PITA. This
107
// next line removes them:
108
query_strings.erase( std::remove_if( query_strings.begin(),
110
boost::bind( &std::string::empty, _1 ) ),
111
query_strings.end());
104
boost::algorithm::token_compress_off);
113
106
for(std::string part : query_strings)
115
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());
118
116
return query_parts;
121
NodeList MatchAbsoluteQuery(Node::Ptr const& root, QueryList const& query_parts)
125
if (query_parts.front().Matches(root))
127
matches.push_back(root);
132
NodeList MatchRelativeQuery(Node::Ptr const& root, QueryList const& query_parts)
135
// non-recursive BFS traversal to find starting points:
136
std::queue<Node::Ptr> queue;
138
while (!queue.empty())
140
Node::Ptr node = queue.front();
142
if (query_parts.front().Matches(node))
144
// found one. We keep going deeper, as there may be another node beneath this one
145
// with the same node name.
146
matches.push_back(node);
148
// Add all children of current node to queue.
149
for(Node::Ptr child : node->Children())
157
NodeList ProcessStartPoints(NodeList start_points, QueryList query_parts)
159
query_parts.erase(query_parts.begin());
160
typedef std::pair<Node::Ptr, QueryList::iterator> node_match_pair;
162
std::queue<node_match_pair> traverse_queue;
163
for(Node::Ptr node : start_points)
165
traverse_queue.push(node_match_pair(node, query_parts.begin()));
167
start_points.clear();
169
while (!traverse_queue.empty())
171
node_match_pair p = traverse_queue.front();
172
traverse_queue.pop();
174
Node::Ptr node = p.first;
175
auto query_it = p.second;
177
if (query_it == query_parts.end())
180
start_points.push_back(node);
184
// push all children of current node to start of queue, advance search iterator, and loop again.
185
for (Node::Ptr child : node->Children())
187
if (query_it->Matches(child))
189
auto it_copy(query_it);
191
traverse_queue.push(node_match_pair(child, it_copy));
119
// Starting at each node listed in 'start_points', search the tree for nodes that match
120
// 'next_match'. next_match *must* be a normal query part object, not a search token.
121
NodeList SearchTreeForNode(NodeList const& start_points, XPathQueryPart const& next_match)
124
for (auto root: start_points)
126
// non-recursive BFS traversal to find starting points:
127
std::queue<Node::Ptr> queue;
129
while (!queue.empty())
131
Node::Ptr node = queue.front();
133
if (next_match.Matches(node))
135
// found one. We keep going deeper, as there may be another node beneath this one
136
// with the same node name.
137
matches.push_back(node);
139
// Add all children of current node to queue.
140
for(Node::Ptr child : node->Children())
148
} // end of anonymous namespace
200
150
NodeList SelectNodes(Node::Ptr const& root, std::string query)
205
155
query = "/" + root->GetName();
157
// sanity checking some obvious invalid queries:
158
if (boost::algorithm::ends_with(query, "//"))
208
bool is_absolute = utils::IsQueryAbsolute(query);
209
161
QueryList query_parts = GetQueryPartsFromQuery(query);
211
NodeList start_nodes;
214
start_nodes = MatchAbsoluteQuery(root, query_parts);
218
start_nodes = MatchRelativeQuery(root, query_parts);
221
return ProcessStartPoints(start_nodes, query_parts);
163
auto query_part = query_parts.cbegin();
164
NodeList start_nodes { root };
165
while (query_part != query_parts.cend())
167
// If the current query piece is a recursive search token ('//')...
168
if (query_part->Type() == XPathQueryPart::QueryPartType::Search)
170
// advance to look at the next piece.
172
// do some sanity checking...
173
if (query_part->Type() == XPathQueryPart::QueryPartType::Search)
174
// invalid query - cannot specify multiple search sequences in a row.
176
// then find all the nodes that match the new query part, and store them as
177
// the new start nodes. We pass in 'start_nodes' rather than 'root' since
178
// there's a chance we'll be doing more than one search in different parts of the tree.
179
start_nodes = SearchTreeForNode(start_nodes, *query_part);
183
// this isn't a search token. Look at each node in the start_nodes list,
184
// and discard any that don't match the current query part.
185
// C++11 is the shit:
190
[query_part](Node::Ptr n) -> bool {
191
return ! query_part->Matches(n);
197
// then replace each node still in the list with all it's children.
198
// ... but only if we're not on the last query part:
199
if (query_part + 1 != query_parts.cend())
201
NodeList new_start_nodes;
202
for (auto node: start_nodes)
204
auto children = node->Children();
207
new_start_nodes.insert(
208
new_start_nodes.end(),
213
start_nodes = new_start_nodes;