package org.apache.lucene.queryparser.flexible.aqp.processors; import java.util.ArrayList; import java.util.List; import org.antlr.runtime.CharStream; import org.apache.lucene.queryparser.flexible.messages.MessageImpl; import org.apache.lucene.queryparser.flexible.core.QueryNodeException; import org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler; 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.builders.AqpFunctionQueryBuilder; import org.apache.lucene.queryparser.flexible.aqp.config.AqpAdsabsQueryConfigHandler; import org.apache.lucene.queryparser.flexible.aqp.config.AqpFunctionQueryBuilderConfig; import org.apache.lucene.queryparser.flexible.aqp.nodes.AqpANTLRNode; import org.apache.lucene.queryparser.flexible.aqp.nodes.AqpFunctionQueryNode; /** * Processing of functional queries may be more involved than the standard * queries. The functions calls can be nested. * * Also each functional processor can be different, therefore my decision * was to register individual function builders inside the config * @see AqpFunctionQueryBuilderConfig * and then insert this builder into the QNode. It will wait there * until it is picked by the {@link AqpFunctionQueryBuilder} which * will decide how it wants to parse the query (ie. it may just want * the literals, or it will request a full query object to be built, etc) * * You should check each {@link AqpFunctionQueryBuilder} * for details on how are data processed. * */ public class AqpQFUNCProcessor extends AqpQProcessor { public boolean nodeIsWanted(AqpANTLRNode node) { if (node.getTokenLabel().equals("QFUNC")) { return true; } return false; } public QueryNode createQNode(AqpANTLRNode node) throws QueryNodeException { List<QueryNode> children = node.getChildren(); AqpANTLRNode funcHead = ((AqpANTLRNode) children.get(0)); String funcName = funcHead.getTokenInput(); if (funcName.endsWith("(")) { funcName = funcName.substring(0, funcName.length()-1); } QueryConfigHandler config = getQueryConfigHandler(); if (!config.has(AqpAdsabsQueryConfigHandler.ConfigurationKeys.FUNCTION_QUERY_BUILDER_CONFIG)) { throw new QueryNodeException(new MessageImpl( "Invalid configuration", "Missing FunctionQueryBuilder provider")); } AqpFunctionQueryBuilder builder = config.get(AqpAdsabsQueryConfigHandler.ConfigurationKeys.FUNCTION_QUERY_BUILDER_CONFIG) .getBuilder(funcName, (QueryNode) node, config); if (builder == null) { throw new QueryNodeException(new MessageImpl(QueryParserMessages.INVALID_SYNTAX, "Unknown function \"" + funcName + "\"" )); } if (((AqpANTLRNode) children.get(children.size()-1)).getTokenName().equals("RPAREN")) { CharStream inputStream = AqpQProcessor.getInputStream(node); AqpANTLRNode funcTail = (AqpANTLRNode) children.get(children.size()-1); OriginalInput originalInput = new OriginalInput(inputStream.substring(funcHead.getInputTokenEnd(), funcTail.getInputTokenStart()), funcHead.getInputTokenStart(), funcHead.getInputTokenEnd()); ArrayList<OriginalInput> values = new ArrayList<OriginalInput>(); int start = funcHead.getInputTokenEnd()+1; int stop = start; for (QueryNode n: children.get(1).getChildren()) { AqpANTLRNode a = (AqpANTLRNode) n; int l = a.hasTokenName("QDELIMITER", 0); if (l > 0 && l < 4) { // MODIFIER/TMODIFIER/FIELD/QDELIMITER AqpANTLRNode delimiter = (AqpANTLRNode) AqpQProcessor.getTerminalNode(a); stop = delimiter.getInputTokenStart()-1; values.add(new OriginalInput(inputStream.substring(start, stop), start, stop)); start = delimiter.getInputTokenEnd()+1; } } stop = funcTail.getInputTokenEnd()-1; values.add(new OriginalInput(inputStream.substring(start, stop), start, stop)); return new AqpFunctionQueryNode(funcName, builder, originalInput, values); } else { // the old semantics when we try hard to discover elements of the function return new AqpFunctionQueryNode(funcName, builder, (AqpANTLRNode) children.get(1)); } } }