package org.apache.lucene.queryparser.flexible.aqp; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Lexer; import org.antlr.runtime.Parser; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.TokenSource; import org.antlr.runtime.TokenStream; import org.antlr.runtime.tree.TreeAdaptor; import org.apache.lucene.queryparser.flexible.messages.Message; import org.apache.lucene.queryparser.flexible.messages.MessageImpl; import org.apache.lucene.queryparser.flexible.core.QueryNodeParseException; import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.aqp.parser.AqpStandardLuceneParser; import org.apache.lucene.queryparser.flexible.aqp.util.AqpCommonTree; import org.apache.lucene.queryparser.flexible.aqp.util.AqpCommonTreeAdaptor; /** * This implementation can load any AST grammar from the repository of grammars * without a need to provide a Java implementation. It uses reflection, so it * might be slower than a dedicated parsing class. * * Every grammar must have a top-level rule called <b>mainQ</b> * * And every grammar must return AST. * * If you know that you are going to instantiate specific parser, then * you should not use this generic class. * * @see AqpSyntaxParserAbstract * @see AqpStandardLuceneParser#init() * */ public class AqpSyntaxParserLoadableImpl extends AqpSyntaxParserAbstract { @SuppressWarnings("rawtypes") private Class clsLexer; @SuppressWarnings("rawtypes") private Class clsParser; private Object iLexer; private Object iParser; private Method invokeMainQ; private Method getTreeMethod; private Method getNumberOfSyntaxErrorsMethod; private Lexer lexer; private Parser parser; private String[] tokenNames; public AqpSyntaxParserLoadableImpl() { // empty constructor } @SuppressWarnings({ "unchecked", "rawtypes" }) public AqpSyntaxParser initializeGrammar(String grammarName) throws QueryNodeParseException { try { // get the Classes clsLexer = Class .forName("org.apache.lucene.queryparser.flexible.aqp.parser." + grammarName + "Lexer"); clsParser = Class .forName("org.apache.lucene.queryparser.flexible.aqp.parser." + grammarName + "Parser"); // instantiate lexer with no parameter Class partypes[] = new Class[0]; // partypes[0] = CharStream.class; Constructor ctLexer = clsLexer.getConstructor(partypes); Object arglist[] = new Object[0]; iLexer = ctLexer.newInstance(arglist); // instantiate parser using no parameters // ANTLRStringStream fakeInput = new ANTLRStringStream("none"); CommonTokenStream fakeTokens = new CommonTokenStream( (TokenSource) clsLexer.cast(iLexer)); Class partypes2[] = new Class[1]; partypes2[0] = TokenStream.class; Constructor ct = clsParser.getConstructor(partypes2); iParser = ct.newInstance(fakeTokens); parser = (Parser) iParser; lexer = (Lexer) iLexer; // get tokenNames Method getTokenNames = clsParser.getDeclaredMethod("getTokenNames"); tokenNames = (String[]) getTokenNames.invoke(iParser); // create adaptor AqpCommonTreeAdaptor adaptor = new AqpCommonTreeAdaptor(tokenNames); // set adaptor Method setTreeAdaptor = clsParser.getDeclaredMethod("setTreeAdaptor", TreeAdaptor.class); setTreeAdaptor.invoke(iParser, adaptor); // get the mainQ parser rule & return value invokeMainQ = clsParser.getDeclaredMethod("mainQ"); getTreeMethod = invokeMainQ.getReturnType().getMethod("getTree"); getNumberOfSyntaxErrorsMethod = clsParser .getMethod("getNumberOfSyntaxErrors"); // AqpCommonTree ast = parseTest("hey:joe"); return this; } catch (Exception e) { e.printStackTrace(); throw new QueryNodeParseException(e); } } public TokenStream getTokenStream(CharSequence query) { ANTLRStringStream input = new ANTLRStringStream(query.toString()); lexer.setCharStream(input); // get tokens CommonTokenStream tokens = new CommonTokenStream( (TokenSource) clsLexer.cast(iLexer)); return tokens; } public QueryNode parseTokenStream(TokenStream tokens, CharSequence query, CharSequence field) throws QueryNodeParseException { // set tokens parser.setTokenStream(tokens); // get tree back Object retVal; AqpCommonTree astTree; try { retVal = invokeMainQ.invoke(iParser); astTree = (AqpCommonTree) (getTreeMethod.invoke(retVal)); // this prevents parser from recovering, however it can also interfere // with custom error handling (if present inside the grammar) Object errNo = getNumberOfSyntaxErrorsMethod.invoke(iParser); if (errNo instanceof Integer && ((Integer) errNo > 0)) { throw new Error( "The parser reported a syntax error, antlrqueryparser hates errors!"); } } catch (Error e) { Message message = new MessageImpl( QueryParserMessages.INVALID_SYNTAX_CANNOT_PARSE, query, e.getMessage()); QueryNodeParseException ee = new QueryNodeParseException(e); ee.setQuery(query); ee.setNonLocalizedMessage(message); throw ee; } catch (Exception e) { // TODO: these exceptions are from the code, should // not be printed // e.printStackTrace(); QueryNodeParseException ee = new QueryNodeParseException(e); throw ee; } try { return astTree.toQueryNodeTree(); } catch (RecognitionException e) { throw new QueryNodeParseException(new MessageImpl(query + " >> " + parser.getErrorMessage(e, parser.getTokenNames()))); } } }