1 /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
2 * Use of this file is governed by the BSD 3-clause license that
3 * can be found in the LICENSE.txt file in the project root.
6 #include "XPathLexer.h"
7 #include "XPathLexerErrorListener.h"
8 #include "XPathElement.h"
9 #include "XPathWildcardAnywhereElement.h"
10 #include "XPathWildcardElement.h"
11 #include "XPathTokenAnywhereElement.h"
12 #include "XPathTokenElement.h"
13 #include "XPathRuleAnywhereElement.h"
14 #include "XPathRuleElement.h"
18 using namespace antlr4;
19 using namespace antlr4::tree;
20 using namespace antlr4::tree::xpath;
22 const std::string XPath::WILDCARD = "*";
23 const std::string XPath::NOT = "!";
25 XPath::XPath(Parser *parser, const std::string &path) {
30 std::vector<std::unique_ptr<XPathElement>> XPath::split(const std::string &path) {
31 ANTLRInputStream in(path);
32 XPathLexer lexer(&in);
33 lexer.removeErrorListeners();
34 XPathLexerErrorListener listener;
35 lexer.addErrorListener(&listener);
36 CommonTokenStream tokenStream(&lexer);
39 } catch (LexerNoViableAltException &) {
40 size_t pos = lexer.getCharPositionInLine();
41 std::string msg = "Invalid tokens or characters at index " + std::to_string(pos) + " in path '" + path + "'";
42 throw IllegalArgumentException(msg);
45 std::vector<Token *> tokens = tokenStream.getTokens();
46 std::vector<std::unique_ptr<XPathElement>> elements;
47 size_t n = tokens.size();
50 while (!done && i < n) {
51 Token *el = tokens[i];
52 Token *next = nullptr;
53 switch (el->getType()) {
54 case XPathLexer::ROOT:
55 case XPathLexer::ANYWHERE: {
56 bool anywhere = el->getType() == XPathLexer::ANYWHERE;
59 bool invert = next->getType() == XPathLexer::BANG;
64 std::unique_ptr<XPathElement> pathElement = getXPathElement(next, anywhere);
65 pathElement->setInvert(invert);
66 elements.push_back(std::move(pathElement));
71 case XPathLexer::TOKEN_REF:
72 case XPathLexer::RULE_REF:
73 case XPathLexer::WILDCARD:
74 elements.push_back(getXPathElement(el, false));
83 throw IllegalArgumentException("Unknown path element " + el->toString());
90 std::unique_ptr<XPathElement> XPath::getXPathElement(Token *wordToken, bool anywhere) {
91 if (wordToken->getType() == Token::EOF) {
92 throw IllegalArgumentException("Missing path element at end of path");
95 std::string word = wordToken->getText();
96 size_t ttype = _parser->getTokenType(word);
97 ssize_t ruleIndex = _parser->getRuleIndex(word);
98 switch (wordToken->getType()) {
99 case XPathLexer::WILDCARD :
101 return std::unique_ptr<XPathWildcardAnywhereElement>(new XPathWildcardAnywhereElement());
102 return std::unique_ptr<XPathWildcardElement>(new XPathWildcardElement());
104 case XPathLexer::TOKEN_REF:
105 case XPathLexer::STRING :
106 if (ttype == Token::INVALID_TYPE) {
107 throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid token name");
110 return std::unique_ptr<XPathTokenAnywhereElement>(new XPathTokenAnywhereElement(word, (int)ttype));
111 return std::unique_ptr<XPathTokenElement>(new XPathTokenElement(word, (int)ttype));
114 if (ruleIndex == -1) {
115 throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid rule name");
118 return std::unique_ptr<XPathRuleAnywhereElement>(new XPathRuleAnywhereElement(word, (int)ruleIndex));
119 return std::unique_ptr<XPathRuleElement>(new XPathRuleElement(word, (int)ruleIndex));
123 static ParserRuleContext dummyRoot;
125 std::vector<ParseTree *> XPath::findAll(ParseTree *tree, std::string const& xpath, Parser *parser) {
126 XPath p(parser, xpath);
127 return p.evaluate(tree);
130 std::vector<ParseTree *> XPath::evaluate(ParseTree *t) {
131 dummyRoot.children = { t }; // don't set t's parent.
133 std::vector<ParseTree *> work = { &dummyRoot };
136 std::vector<std::unique_ptr<XPathElement>> elements = split(_path);
138 while (i < elements.size()) {
139 std::vector<ParseTree *> next;
140 for (auto *node : work) {
141 if (!node->children.empty()) {
142 // only try to match next element if it has children
143 // e.g., //func/*/stat might have a token node for which
144 // we can't go looking for stat nodes.
145 auto matching = elements[i]->evaluate(node);
146 next.insert(next.end(), matching.begin(), matching.end());