| /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. |
| * Use of this file is governed by the BSD 3-clause license that |
| * can be found in the LICENSE.txt file in the project root. |
| */ |
| |
| #include "XPathLexer.h" |
| #include "XPathLexerErrorListener.h" |
| #include "XPathElement.h" |
| #include "XPathWildcardAnywhereElement.h" |
| #include "XPathWildcardElement.h" |
| #include "XPathTokenAnywhereElement.h" |
| #include "XPathTokenElement.h" |
| #include "XPathRuleAnywhereElement.h" |
| #include "XPathRuleElement.h" |
| |
| #include "XPath.h" |
| |
| using namespace antlr4; |
| using namespace antlr4::tree; |
| using namespace antlr4::tree::xpath; |
| |
| const std::string XPath::WILDCARD = "*"; |
| const std::string XPath::NOT = "!"; |
| |
| XPath::XPath(Parser *parser, const std::string &path) { |
| _parser = parser; |
| _path = path; |
| _elements = split(path); |
| } |
| |
| std::vector<XPathElement> XPath::split(const std::string &path) { |
| ANTLRFileStream in(path); |
| XPathLexer lexer(&in); |
| lexer.removeErrorListeners(); |
| XPathLexerErrorListener listener; |
| lexer.addErrorListener(&listener); |
| CommonTokenStream tokenStream(&lexer); |
| try { |
| tokenStream.fill(); |
| } catch (LexerNoViableAltException &) { |
| size_t pos = lexer.getCharPositionInLine(); |
| std::string msg = "Invalid tokens or characters at index " + std::to_string(pos) + " in path '" + path + "'"; |
| throw IllegalArgumentException(msg); |
| } |
| |
| std::vector<Token *> tokens = tokenStream.getTokens(); |
| std::vector<XPathElement> elements; |
| size_t n = tokens.size(); |
| size_t i = 0; |
| bool done = false; |
| while (!done && i < n) { |
| Token *el = tokens[i]; |
| Token *next = nullptr; |
| switch (el->getType()) { |
| case XPathLexer::ROOT: |
| case XPathLexer::ANYWHERE: { |
| bool anywhere = el->getType() == XPathLexer::ANYWHERE; |
| i++; |
| next = tokens[i]; |
| bool invert = next->getType() == XPathLexer::BANG; |
| if (invert) { |
| i++; |
| next = tokens[i]; |
| } |
| XPathElement pathElement = getXPathElement(next, anywhere); |
| pathElement.setInvert(invert); |
| elements.push_back(pathElement); |
| i++; |
| break; |
| |
| } |
| case XPathLexer::TOKEN_REF: |
| case XPathLexer::RULE_REF: |
| case XPathLexer::WILDCARD: |
| elements.push_back(getXPathElement(el, false)); |
| i++; |
| break; |
| |
| case Token::EOF: |
| done = true; |
| break; |
| |
| default : |
| throw IllegalArgumentException("Unknow path element " + el->toString()); |
| } |
| } |
| |
| return elements; |
| } |
| |
| XPathElement XPath::getXPathElement(Token *wordToken, bool anywhere) { |
| if (wordToken->getType() == Token::EOF) { |
| throw IllegalArgumentException("Missing path element at end of path"); |
| } |
| std::string word = wordToken->getText(); |
| size_t ttype = _parser->getTokenType(word); |
| ssize_t ruleIndex = _parser->getRuleIndex(word); |
| switch (wordToken->getType()) { |
| case XPathLexer::WILDCARD : |
| if (anywhere) |
| return XPathWildcardAnywhereElement(); |
| return XPathWildcardElement(); |
| |
| case XPathLexer::TOKEN_REF: |
| case XPathLexer::STRING : |
| if (ttype == Token::INVALID_TYPE) { |
| throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid token name"); |
| } |
| if (anywhere) |
| return XPathTokenAnywhereElement(word, (int)ttype); |
| return XPathTokenElement(word, (int)ttype); |
| |
| default : |
| if (ruleIndex == -1) { |
| throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid rule name"); |
| } |
| if (anywhere) |
| return XPathRuleAnywhereElement(word, (int)ruleIndex); |
| return XPathRuleElement(word, (int)ruleIndex); |
| } |
| } |
| |
| static ParserRuleContext dummyRoot; |
| |
| std::vector<ParseTree *> XPath::evaluate(ParseTree *t) { |
| dummyRoot.children = { t }; // don't set t's parent. |
| |
| std::vector<ParseTree *> work = { &dummyRoot }; |
| |
| size_t i = 0; |
| while (i < _elements.size()) { |
| std::vector<ParseTree *> next; |
| for (auto node : work) { |
| if (!node->children.empty()) { |
| // only try to match next element if it has children |
| // e.g., //func/*/stat might have a token node for which |
| // we can't go looking for stat nodes. |
| auto matching = _elements[i].evaluate(node); |
| next.insert(next.end(), matching.begin(), matching.end()); |
| } |
| } |
| i++; |
| work = next; |
| } |
| |
| return work; |
| } |