You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by bi...@apache.org on 2011/12/05 21:05:51 UTC
svn commit: r1210600 [13/16] - in
/incubator/accumulo/trunk/contrib/accumulo_sample: ./ ingest/
ingest/src/main/java/aggregator/ ingest/src/main/java/ingest/
ingest/src/main/java/iterator/ ingest/src/main/java/normalizer/
ingest/src/main/java/protobuf/...
Modified: incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/JexlOperatorConstants.java
URL: http://svn.apache.org/viewvc/incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/JexlOperatorConstants.java?rev=1210600&r1=1210599&r2=1210600&view=diff
==============================================================================
--- incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/JexlOperatorConstants.java (original)
+++ incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/JexlOperatorConstants.java Mon Dec 5 20:05:49 2011
@@ -1,19 +1,19 @@
/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements. See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package parser;
import java.util.Map;
@@ -34,72 +34,72 @@ import org.apache.commons.jexl2.parser.J
import org.apache.commons.jexl2.parser.ParserTreeConstants;
public class JexlOperatorConstants implements ParserTreeConstants {
-
- private static Map<Class<? extends JexlNode>,String> operatorMap = new ConcurrentHashMap<Class<? extends JexlNode>,String>();
- private static Map<String, Class<? extends JexlNode>> classMap = new ConcurrentHashMap<String, Class<? extends JexlNode>>();
- private static Map<Integer,String> jjtOperatorMap = new ConcurrentHashMap<Integer,String>();
- private static Map<String,Integer> jjtTypeMap = new ConcurrentHashMap<String,Integer>();
-
- static {
- operatorMap.put(ASTEQNode.class, "==");
- operatorMap.put(ASTNENode.class, "!=");
- operatorMap.put(ASTLTNode.class, "<");
- operatorMap.put(ASTLENode.class, "<=");
- operatorMap.put(ASTGTNode.class, ">");
- operatorMap.put(ASTGENode.class, ">=");
- operatorMap.put(ASTERNode.class, "=~");
- operatorMap.put(ASTNRNode.class, "!~");
- operatorMap.put(ASTFunctionNode.class, "f");
- operatorMap.put(ASTAndNode.class,"and");
- operatorMap.put(ASTOrNode.class,"or");
-
- classMap.put("==", ASTEQNode.class);
- classMap.put("!=", ASTNENode.class);
- classMap.put("<", ASTLTNode.class);
- classMap.put("<=", ASTLENode.class);
- classMap.put(">", ASTGTNode.class);
- classMap.put(">=", ASTGENode.class);
- classMap.put("=~", ASTERNode.class);
- classMap.put("!~", ASTNRNode.class);
- classMap.put("f", ASTFunctionNode.class);
-
- jjtOperatorMap.put(JJTEQNODE, "==");
- jjtOperatorMap.put(JJTNENODE, "!=");
- jjtOperatorMap.put(JJTLTNODE, "<");
- jjtOperatorMap.put(JJTLENODE, "<=");
- jjtOperatorMap.put(JJTGTNODE, ">");
- jjtOperatorMap.put(JJTGENODE, ">=");
- jjtOperatorMap.put(JJTERNODE, "=~");
- jjtOperatorMap.put(JJTNRNODE, "!~");
- jjtOperatorMap.put(JJTFUNCTIONNODE, "f");
- jjtOperatorMap.put(JJTANDNODE, "and");
- jjtOperatorMap.put(JJTORNODE, "or");
-
- jjtTypeMap.put("==", JJTEQNODE);
- jjtTypeMap.put("!=", JJTNENODE);
- jjtTypeMap.put("<", JJTLTNODE);
- jjtTypeMap.put("<=", JJTLENODE);
- jjtTypeMap.put(">", JJTGTNODE);
- jjtTypeMap.put(">=", JJTGENODE);
- jjtTypeMap.put("=~", JJTERNODE);
- jjtTypeMap.put("!~", JJTNRNODE);
- jjtTypeMap.put("f", JJTFUNCTIONNODE);
-
- }
-
- public static String getOperator(Class<? extends JexlNode> nodeType) {
- return operatorMap.get(nodeType);
- }
-
- public static String getOperator(Integer jjtNode) {
- return jjtOperatorMap.get(jjtNode);
- }
-
- public static Class<? extends JexlNode> getClass(String operator) {
- return classMap.get(operator);
- }
-
- public static int getJJTNodeType(String operator) {
- return jjtTypeMap.get(operator);
- }
+
+ private static Map<Class<? extends JexlNode>,String> operatorMap = new ConcurrentHashMap<Class<? extends JexlNode>,String>();
+ private static Map<String,Class<? extends JexlNode>> classMap = new ConcurrentHashMap<String,Class<? extends JexlNode>>();
+ private static Map<Integer,String> jjtOperatorMap = new ConcurrentHashMap<Integer,String>();
+ private static Map<String,Integer> jjtTypeMap = new ConcurrentHashMap<String,Integer>();
+
+ static {
+ operatorMap.put(ASTEQNode.class, "==");
+ operatorMap.put(ASTNENode.class, "!=");
+ operatorMap.put(ASTLTNode.class, "<");
+ operatorMap.put(ASTLENode.class, "<=");
+ operatorMap.put(ASTGTNode.class, ">");
+ operatorMap.put(ASTGENode.class, ">=");
+ operatorMap.put(ASTERNode.class, "=~");
+ operatorMap.put(ASTNRNode.class, "!~");
+ operatorMap.put(ASTFunctionNode.class, "f");
+ operatorMap.put(ASTAndNode.class, "and");
+ operatorMap.put(ASTOrNode.class, "or");
+
+ classMap.put("==", ASTEQNode.class);
+ classMap.put("!=", ASTNENode.class);
+ classMap.put("<", ASTLTNode.class);
+ classMap.put("<=", ASTLENode.class);
+ classMap.put(">", ASTGTNode.class);
+ classMap.put(">=", ASTGENode.class);
+ classMap.put("=~", ASTERNode.class);
+ classMap.put("!~", ASTNRNode.class);
+ classMap.put("f", ASTFunctionNode.class);
+
+ jjtOperatorMap.put(JJTEQNODE, "==");
+ jjtOperatorMap.put(JJTNENODE, "!=");
+ jjtOperatorMap.put(JJTLTNODE, "<");
+ jjtOperatorMap.put(JJTLENODE, "<=");
+ jjtOperatorMap.put(JJTGTNODE, ">");
+ jjtOperatorMap.put(JJTGENODE, ">=");
+ jjtOperatorMap.put(JJTERNODE, "=~");
+ jjtOperatorMap.put(JJTNRNODE, "!~");
+ jjtOperatorMap.put(JJTFUNCTIONNODE, "f");
+ jjtOperatorMap.put(JJTANDNODE, "and");
+ jjtOperatorMap.put(JJTORNODE, "or");
+
+ jjtTypeMap.put("==", JJTEQNODE);
+ jjtTypeMap.put("!=", JJTNENODE);
+ jjtTypeMap.put("<", JJTLTNODE);
+ jjtTypeMap.put("<=", JJTLENODE);
+ jjtTypeMap.put(">", JJTGTNODE);
+ jjtTypeMap.put(">=", JJTGENODE);
+ jjtTypeMap.put("=~", JJTERNODE);
+ jjtTypeMap.put("!~", JJTNRNODE);
+ jjtTypeMap.put("f", JJTFUNCTIONNODE);
+
+ }
+
+ public static String getOperator(Class<? extends JexlNode> nodeType) {
+ return operatorMap.get(nodeType);
+ }
+
+ public static String getOperator(Integer jjtNode) {
+ return jjtOperatorMap.get(jjtNode);
+ }
+
+ public static Class<? extends JexlNode> getClass(String operator) {
+ return classMap.get(operator);
+ }
+
+ public static int getJJTNodeType(String operator) {
+ return jjtTypeMap.get(operator);
+ }
}
Modified: incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryEvaluator.java
URL: http://svn.apache.org/viewvc/incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryEvaluator.java?rev=1210600&r1=1210599&r2=1210600&view=diff
==============================================================================
--- incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryEvaluator.java (original)
+++ incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryEvaluator.java Mon Dec 5 20:05:49 2011
@@ -1,19 +1,19 @@
/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements. See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package parser;
import java.util.Collection;
@@ -43,252 +43,250 @@ import com.google.common.collect.Multima
import function.QueryFunctions;
/**
- * This class evaluates events against a query. The query is passed to the constructor and then
- * parsed. It is evaluated against an event in the evaluate method.
+ * This class evaluates events against a query. The query is passed to the constructor and then parsed. It is evaluated against an event in the evaluate method.
*/
public class QueryEvaluator {
-
- private static Logger log = Logger.getLogger(QueryEvaluator.class);
- //According to the JEXL 2.0 docs, the engine is thread-safe. Let's create 1 engine per VM and
- //cache 128 expressions
- private static JexlEngine engine = new JexlEngine(null, new Arithmetic(false), null, null);
-
- static {
- engine.setSilent(false);
- engine.setCache(128);
- Map<String, Object> functions = new HashMap<String, Object>();
- functions.put("f", QueryFunctions.class);
- engine.setFunctions(functions);
- }
- private String query = null;
- private Set<String> literals = null;
- private Multimap<String, QueryTerm> terms = null;
- private String modifiedQuery = null;
- private JexlContext ctx = new MapContext();
- private boolean caseInsensitive = true;
-
- public QueryEvaluator(String query) throws ParseException {
- this.caseInsensitive = true; // default case insensitive matching.
- if(caseInsensitive){
- query = query.toLowerCase();
- }
- this.query = query;
- QueryParser parser = new QueryParser();
- parser.execute(query);
- this.terms = parser.getQueryTerms();
- if(caseInsensitive){
- literals = new HashSet<String>();
- for(String lit : parser.getQueryIdentifiers()){
- literals.add(lit.toLowerCase());
- }
- }else{
- this.literals = parser.getQueryIdentifiers();
- }
+
+ private static Logger log = Logger.getLogger(QueryEvaluator.class);
+ // According to the JEXL 2.0 docs, the engine is thread-safe. Let's create 1 engine per VM and
+ // cache 128 expressions
+ private static JexlEngine engine = new JexlEngine(null, new Arithmetic(false), null, null);
+
+ static {
+ engine.setSilent(false);
+ engine.setCache(128);
+ Map<String,Object> functions = new HashMap<String,Object>();
+ functions.put("f", QueryFunctions.class);
+ engine.setFunctions(functions);
+ }
+ private String query = null;
+ private Set<String> literals = null;
+ private Multimap<String,QueryTerm> terms = null;
+ private String modifiedQuery = null;
+ private JexlContext ctx = new MapContext();
+ private boolean caseInsensitive = true;
+
+ public QueryEvaluator(String query) throws ParseException {
+ this.caseInsensitive = true; // default case insensitive matching.
+ if (caseInsensitive) {
+ query = query.toLowerCase();
}
-
- public QueryEvaluator(String query, boolean insensitive) throws ParseException {
- this.caseInsensitive = insensitive;
- if(this.caseInsensitive){
- query = query.toLowerCase();
- }
- this.query = query;
- QueryParser parser = new QueryParser();
- parser.execute(query);
- this.terms = parser.getQueryTerms();
-
- if(caseInsensitive){
- literals = new HashSet<String>();
- for(String lit : parser.getQueryIdentifiers()){
- literals.add(lit.toLowerCase());
- }
- }else{
- this.literals = parser.getQueryIdentifiers();
- }
+ this.query = query;
+ QueryParser parser = new QueryParser();
+ parser.execute(query);
+ this.terms = parser.getQueryTerms();
+ if (caseInsensitive) {
+ literals = new HashSet<String>();
+ for (String lit : parser.getQueryIdentifiers()) {
+ literals.add(lit.toLowerCase());
+ }
+ } else {
+ this.literals = parser.getQueryIdentifiers();
}
-
- public String getQuery() {
- return this.query;
+ }
+
+ public QueryEvaluator(String query, boolean insensitive) throws ParseException {
+ this.caseInsensitive = insensitive;
+ if (this.caseInsensitive) {
+ query = query.toLowerCase();
}
-
- public void printLiterals() {
- for (String s : literals) {
- System.out.println("literal: " + s);
- }
+ this.query = query;
+ QueryParser parser = new QueryParser();
+ parser.execute(query);
+ this.terms = parser.getQueryTerms();
+
+ if (caseInsensitive) {
+ literals = new HashSet<String>();
+ for (String lit : parser.getQueryIdentifiers()) {
+ literals.add(lit.toLowerCase());
+ }
+ } else {
+ this.literals = parser.getQueryIdentifiers();
}
-
- public void setLevel(Level lev){
- log.setLevel(lev);
+ }
+
+ public String getQuery() {
+ return this.query;
+ }
+
+ public void printLiterals() {
+ for (String s : literals) {
+ System.out.println("literal: " + s);
+ }
+ }
+
+ public void setLevel(Level lev) {
+ log.setLevel(lev);
+ }
+
+ public StringBuilder rewriteQuery(StringBuilder query, String fieldName, Collection<FieldValue> fieldValues) {
+ if (log.isDebugEnabled()) {
+ log.debug("rewriteQuery");
+ }
+ // Here we have a field that has multiple values. In this case we need to put
+ // all values into the jexl context as an array and rewrite the query to account for all
+ // of the fields.
+ if (caseInsensitive) {
+ fieldName = fieldName.toLowerCase();
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Modifying original query: " + query);
}
+ // Pull the values out of the FieldValue object
+ String[] values = new String[fieldValues.size()];
+ int idx = 0;
+ for (FieldValue fv : fieldValues) {
+ if (caseInsensitive) {
+ values[idx] = (new String(fv.getValue())).toLowerCase();
+ } else {
+ values[idx] = new String(fv.getValue());
+ }
+ idx++;
+ }
+ // Add the array to the context
+ ctx.set(fieldName, values);
- public StringBuilder rewriteQuery(StringBuilder query, String fieldName, Collection<FieldValue> fieldValues) {
- if(log.isDebugEnabled()){
- log.debug("rewriteQuery");
- }
- //Here we have a field that has multiple values. In this case we need to put
- //all values into the jexl context as an array and rewrite the query to account for all
- //of the fields.
- if (caseInsensitive) {
- fieldName = fieldName.toLowerCase();
- }
- if (log.isDebugEnabled()) {
- log.debug("Modifying original query: " + query);
- }
- //Pull the values out of the FieldValue object
- String[] values = new String[fieldValues.size()];
- int idx = 0;
- for (FieldValue fv : fieldValues) {
- if(caseInsensitive){
- values[idx] = (new String(fv.getValue())).toLowerCase();
- }else{
- values[idx] = new String(fv.getValue());
- }
- idx++;
- }
- //Add the array to the context
- ctx.set(fieldName, values);
-
- Collection<QueryTerm> qt = terms.get(fieldName);
-
- //Add a script to the beginning of the query for this multi-valued field
- StringBuilder script = new StringBuilder();
- script.append("_").append(fieldName).append(" = false;\n");
- script.append("for (field : ").append(fieldName).append(") {\n");
-
- for (QueryTerm t : qt) {
- if (!t.getOperator().equals(JexlOperatorConstants.getOperator(ParserTreeConstants.JJTFUNCTIONNODE))) {
- script.append("\tif (_").append(fieldName).append(" == false && field ").append(t.getOperator()).append(" ").append(t.getValue()).append(") { \n");
- } else {
- script.append("\tif (_").append(fieldName).append(" == false && ").append(t.getValue().toString().replace(fieldName, "field")).append(") { \n");
- }
- script.append("\t\t_").append(fieldName).append(" = true;\n");
- script.append("\t}\n");
- }
- script.append("}\n");
-
- //Add the script to the beginning of the query
- query.insert(0, script.toString());
-
- StringBuilder newPredicate = new StringBuilder();
- newPredicate.append("_").append(fieldName).append(" == true");
-
- for (QueryTerm t : qt) {
- //Find the location of this term in the query
- StringBuilder predicate = new StringBuilder();
- int start = 0;
- if (!t.getOperator().equals(JexlOperatorConstants.getOperator(ParserTreeConstants.JJTFUNCTIONNODE))) {
- predicate.append(fieldName).append(" ").append(t.getOperator()).append(" ").append(t.getValue());
- start = query.indexOf(predicate.toString());
- } else {
- predicate.append(t.getValue().toString());
- //need to find the second occurence of the string.
- start = query.indexOf(predicate.toString());
- }
- if (-1 == start) {
- log.warn("Unable to find predicate: " + predicate.toString() +" in rewritten query: " + query.toString());
- }
- int length = predicate.length();
-
- //Now modify the query to check the value of my.fieldName
- query.replace(start, start + length, newPredicate.toString());
- }
-
- if(log.isDebugEnabled()){
- log.debug("leaving rewriteQuery with: "+query.toString());
- }
- return query;
+ Collection<QueryTerm> qt = terms.get(fieldName);
+
+ // Add a script to the beginning of the query for this multi-valued field
+ StringBuilder script = new StringBuilder();
+ script.append("_").append(fieldName).append(" = false;\n");
+ script.append("for (field : ").append(fieldName).append(") {\n");
+
+ for (QueryTerm t : qt) {
+ if (!t.getOperator().equals(JexlOperatorConstants.getOperator(ParserTreeConstants.JJTFUNCTIONNODE))) {
+ script.append("\tif (_").append(fieldName).append(" == false && field ").append(t.getOperator()).append(" ").append(t.getValue()).append(") { \n");
+ } else {
+ script.append("\tif (_").append(fieldName).append(" == false && ").append(t.getValue().toString().replace(fieldName, "field")).append(") { \n");
+ }
+ script.append("\t\t_").append(fieldName).append(" = true;\n");
+ script.append("\t}\n");
}
-
- /**
- * Evaluates the query against an event.
- *
- * @param eventFields
- * @return
- */
- public boolean evaluate(EventFields eventFields) {
-
- this.modifiedQuery = null;
- boolean rewritten = false;
-
- //Copy the query
- StringBuilder q = new StringBuilder(query);
- //Copy the literals, we are going to remove elements from this set
- //when they are added to the JEXL context. This will allow us to
- //determine which items in the query where *NOT* in the data.
- HashSet<String> literalsCopy = new HashSet<String>(literals);
-
- //Loop through the event fields and add them to the JexlContext.
- for (Entry<String, Collection<FieldValue>> field : eventFields.asMap().entrySet()) {
- String fName = field.getKey();
- if(caseInsensitive){
- fName = fName.toLowerCase();
- }
- //If this field is not part of the expression, then skip it.
- if (!literals.contains(fName)) {
- continue;
- } else {
- literalsCopy.remove(fName);
- }
-
- //This field may have multiple values.
- if (field.getValue().size() == 0) {
- continue;
- } else if (field.getValue().size() == 1) {
- //We are explicitly converting these bytes to a String.
- if(caseInsensitive){
- ctx.set(field.getKey().toLowerCase(), (new String(field.getValue().iterator().next().getValue())).toLowerCase());
- }else{
- ctx.set(field.getKey(), new String(field.getValue().iterator().next().getValue()));
- }
-
- } else {
- //q = queryRewrite(q, field.getKey(), field.getValue());
- q = rewriteQuery(q, field.getKey(), field.getValue());
- rewritten = true;
- }//End of if
-
- }//End of loop
-
- //For any literals in the query that were not found in the data, add them to the context
- //with a null value.
- for (String lit : literalsCopy) {
- ctx.set(lit, null);
- }
-
- if (log.isDebugEnabled()) {
- log.debug("Evaluating query: " + q.toString());
- }
-
- this.modifiedQuery = q.toString();
-
-
- Boolean result = null;
- if (rewritten) {
- Script script = engine.createScript(this.modifiedQuery);
- try {
- result = (Boolean) script.execute(ctx);
- } catch (Exception e) {
- log.error("Error evaluating script: " + this.modifiedQuery + " against event" + eventFields.toString(), e);
- }
- } else {
- Expression expr = engine.createExpression(this.modifiedQuery);
- try {
- result = (Boolean) expr.evaluate(ctx);
- } catch (Exception e) {
- log.error("Error evaluating expression: " + this.modifiedQuery + " against event" + eventFields.toString(), e);
- }
- }
- if (null != result && result) {
- return true;
+ script.append("}\n");
+
+ // Add the script to the beginning of the query
+ query.insert(0, script.toString());
+
+ StringBuilder newPredicate = new StringBuilder();
+ newPredicate.append("_").append(fieldName).append(" == true");
+
+ for (QueryTerm t : qt) {
+ // Find the location of this term in the query
+ StringBuilder predicate = new StringBuilder();
+ int start = 0;
+ if (!t.getOperator().equals(JexlOperatorConstants.getOperator(ParserTreeConstants.JJTFUNCTIONNODE))) {
+ predicate.append(fieldName).append(" ").append(t.getOperator()).append(" ").append(t.getValue());
+ start = query.indexOf(predicate.toString());
+ } else {
+ predicate.append(t.getValue().toString());
+ // need to find the second occurence of the string.
+ start = query.indexOf(predicate.toString());
+ }
+ if (-1 == start) {
+ log.warn("Unable to find predicate: " + predicate.toString() + " in rewritten query: " + query.toString());
+ }
+ int length = predicate.length();
+
+ // Now modify the query to check the value of my.fieldName
+ query.replace(start, start + length, newPredicate.toString());
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("leaving rewriteQuery with: " + query.toString());
+ }
+ return query;
+ }
+
+ /**
+ * Evaluates the query against an event.
+ *
+ * @param eventFields
+ * @return
+ */
+ public boolean evaluate(EventFields eventFields) {
+
+ this.modifiedQuery = null;
+ boolean rewritten = false;
+
+ // Copy the query
+ StringBuilder q = new StringBuilder(query);
+ // Copy the literals, we are going to remove elements from this set
+ // when they are added to the JEXL context. This will allow us to
+ // determine which items in the query where *NOT* in the data.
+ HashSet<String> literalsCopy = new HashSet<String>(literals);
+
+ // Loop through the event fields and add them to the JexlContext.
+ for (Entry<String,Collection<FieldValue>> field : eventFields.asMap().entrySet()) {
+ String fName = field.getKey();
+ if (caseInsensitive) {
+ fName = fName.toLowerCase();
+ }
+ // If this field is not part of the expression, then skip it.
+ if (!literals.contains(fName)) {
+ continue;
+ } else {
+ literalsCopy.remove(fName);
+ }
+
+ // This field may have multiple values.
+ if (field.getValue().size() == 0) {
+ continue;
+ } else if (field.getValue().size() == 1) {
+ // We are explicitly converting these bytes to a String.
+ if (caseInsensitive) {
+ ctx.set(field.getKey().toLowerCase(), (new String(field.getValue().iterator().next().getValue())).toLowerCase());
} else {
- return false;
+ ctx.set(field.getKey(), new String(field.getValue().iterator().next().getValue()));
}
- } //End of method
-
- /**
- *
- * @return rewritten query that was evaluated against the most recent event
- */
- public String getModifiedQuery() {
- return this.modifiedQuery;
+
+ } else {
+ // q = queryRewrite(q, field.getKey(), field.getValue());
+ q = rewriteQuery(q, field.getKey(), field.getValue());
+ rewritten = true;
+ }// End of if
+
+ }// End of loop
+
+ // For any literals in the query that were not found in the data, add them to the context
+ // with a null value.
+ for (String lit : literalsCopy) {
+ ctx.set(lit, null);
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Evaluating query: " + q.toString());
+ }
+
+ this.modifiedQuery = q.toString();
+
+ Boolean result = null;
+ if (rewritten) {
+ Script script = engine.createScript(this.modifiedQuery);
+ try {
+ result = (Boolean) script.execute(ctx);
+ } catch (Exception e) {
+ log.error("Error evaluating script: " + this.modifiedQuery + " against event" + eventFields.toString(), e);
+ }
+ } else {
+ Expression expr = engine.createExpression(this.modifiedQuery);
+ try {
+ result = (Boolean) expr.evaluate(ctx);
+ } catch (Exception e) {
+ log.error("Error evaluating expression: " + this.modifiedQuery + " against event" + eventFields.toString(), e);
+ }
+ }
+ if (null != result && result) {
+ return true;
+ } else {
+ return false;
}
+ } // End of method
+
+ /**
+ *
+ * @return rewritten query that was evaluated against the most recent event
+ */
+ public String getModifiedQuery() {
+ return this.modifiedQuery;
+ }
}
Modified: incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryParser.java
URL: http://svn.apache.org/viewvc/incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryParser.java?rev=1210600&r1=1210599&r2=1210600&view=diff
==============================================================================
--- incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryParser.java (original)
+++ incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/QueryParser.java Mon Dec 5 20:05:49 2011
@@ -1,19 +1,19 @@
/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements. See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package parser;
import java.io.StringReader;
@@ -80,751 +80,765 @@ import org.apache.hadoop.util.hash.Murmu
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
-
/**
- * Parses the query for the purposes of extracting terms, operators, and literals for
- * query optimization. This class does not necessarily understand how to parse all of the
- * possible combinations of the JEXL syntax, but that does not mean that the query will
- * not evaluate against the event objects. It means that the unsupported operators will not
- * be parsed and included in the optimization step.
- *
+ * Parses the query for the purposes of extracting terms, operators, and literals for query optimization. This class does not necessarily understand how to
+ * parse all of the possible combinations of the JEXL syntax, but that does not mean that the query will not evaluate against the event objects. It means that
+ * the unsupported operators will not be parsed and included in the optimization step.
+ *
*/
public class QueryParser implements ParserVisitor {
-
- public static class QueryTerm {
- private boolean negated = false;
- private String operator = null;
- private Object value = null;
- public QueryTerm(boolean negated, String operator,Object value) {
- super();
- this.negated = negated;
- this.operator = operator;
- this.value = value;
- }
- public boolean isNegated() {
- return negated;
- }
- public String getOperator() {
- return operator;
- }
- public Object getValue() {
- return value;
- }
- public void setNegated(boolean negated) {
- this.negated = negated;
- }
- public void setOperator(String operator) {
- this.operator = operator;
- }
- public void setValue(Object value) {
- this.value = value;
- }
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append("negated: ").append(negated).append(", operator: ").append(operator).append(", value: ").append(value);
- return buf.toString();
- }
- }
-
- /**
- * Holder object
- */
- static class ObjectHolder {
- Object object;
- public Object getObject() {
- return object;
- }
- public void setObject(Object object) {
- this.object = object;
- }
- }
-
- static class FunctionResult {
- private List<TermResult> terms = new ArrayList<TermResult>();
-
- public List<TermResult> getTerms() {
- return terms;
- }
- }
-
- /**
- * Holder object for a term (i.e. field name)
- */
- static class TermResult {
- Object value;
- public TermResult(Object value) { this.value = value; }
- }
-
- /**
- * Holder object for a literal (integer, float, string, or null literal) value
- */
- static class LiteralResult {
- Object value;
- public LiteralResult(Object value) { this.value = value; }
- }
-
- /**
- * Object used to store context information as the AST is being iterated over.
- */
- static class EvaluationContext {
- boolean inOrContext = false;
- boolean inNotContext = false;
- boolean inAndContext = false;
- }
-
- /**
- * Object to store information from previously parsed queries.
- */
- private static class CacheEntry {
- private Set<String> negatedTerms = null;
- private Set<String> andTerms = null;
- private Set<String> orTerms = null;
- private Set<Object> literals = null;
- private Multimap<String,QueryTerm> terms = null;
- private ASTJexlScript rootNode = null;
- private TreeNode tree = null;
- public CacheEntry(Set<String> negatedTerms, Set<String> andTerms,
- Set<String> orTerms, Set<Object> literals,
- Multimap<String, QueryTerm> terms, ASTJexlScript rootNode,
- TreeNode tree) {
- super();
- this.negatedTerms = negatedTerms;
- this.andTerms = andTerms;
- this.orTerms = orTerms;
- this.literals = literals;
- this.terms = terms;
- this.rootNode = rootNode;
- this.tree = tree;
- }
- public Set<String> getNegatedTerms() {
- return negatedTerms;
- }
- public Set<String> getAndTerms() {
- return andTerms;
- }
- public Set<String> getOrTerms() {
- return orTerms;
- }
- public Set<Object> getLiterals() {
- return literals;
- }
- public Multimap<String, QueryTerm> getTerms() {
- return terms;
- }
- public ASTJexlScript getRootNode() {
- return rootNode;
- }
- public TreeNode getTree() {
- return tree;
- }
- }
-
- private static final int SEED = 650567;
-
- private static LRUMap cache = new LRUMap();
-
- protected Set<String> negatedTerms = new HashSet<String>();
-
- private Set<String> andTerms = new HashSet<String>();
-
- private Set<String> orTerms = new HashSet<String>();
-
- /**
- * List of String, Integer, Float, etc literals that were passed in the query
- */
- private Set<Object> literals = new HashSet<Object>();
-
- /**
- * Map of terms (field names) to QueryTerm objects.
- */
- private Multimap<String,QueryTerm> terms = HashMultimap.create();
-
- private ASTJexlScript rootNode = null;
-
- private TreeNode tree = null;
-
- private int hashVal = 0;
-
- public QueryParser() {
- }
-
- private void reset() {
- this.negatedTerms.clear();
- this.andTerms.clear();
- this.orTerms.clear();
- this.literals.clear();
- this.terms = HashMultimap.create();
- }
-
- public void execute(String query) throws ParseException {
- reset();
- query = query.replaceAll("\\s+AND\\s+", " and ");
- query = query.replaceAll("\\s+OR\\s+", " or ");
- query = query.replaceAll("\\s+NOT\\s+", " not ");
-
- //Check to see if its in the cache
- Hash hash = MurmurHash.getInstance();
- this.hashVal = hash.hash(query.getBytes(), SEED);
- if (cache.containsKey(hashVal)) {
- CacheEntry entry = (CacheEntry) cache.get(hashVal);
- this.negatedTerms = entry.getNegatedTerms();
- this.andTerms = entry.getAndTerms();
- this.orTerms = entry.getOrTerms();
- this.literals = entry.getLiterals();
- this.terms = entry.getTerms();
- this.rootNode = entry.getRootNode();
- this.tree = entry.getTree();
- } else {
- Parser p = new Parser(new StringReader(";"));
- rootNode = p.parse(new StringReader(query), null);
- rootNode.childrenAccept(this, null);
- TreeBuilder builder = new TreeBuilder(rootNode);
- tree = builder.getRootNode();
- CacheEntry entry = new CacheEntry(this.negatedTerms, this.andTerms, this.orTerms, this.literals, this.terms, rootNode, tree);
- cache.put(hashVal, entry);
- }
-
- }
-
- public static void clearCache() {
- cache.clear();
- }
-
- /**
- *
- * @return this queries hash value
- */
- public int getHashValue() {
- return this.hashVal;
- }
-
- public TreeNode getIteratorTree() {
- return this.tree;
- }
-
- /**
- *
- * @return JEXL abstract syntax tree
- */
- public ASTJexlScript getAST() {
- return this.rootNode;
- }
-
- /**
- *
- * @return Set of field names to use in the optimizer for nots. As a general
- * rule none of these terms should be used to find an event and should they
- * should be evaluated on each event after being found.
- */
- public Set<String> getNegatedTermsForOptimizer() {
- return negatedTerms;
- }
-
- /**
- *
- * @return Set of field names to use in the optimizer for ands. As a general
- * rule any one term of an and clause can be used to find associated events.
- */
- public Set<String> getAndTermsForOptimizer() {
- return andTerms;
- }
-
-
- /**
- *
- * @return Set of field names to use in the optimizer for ors. As a general rule
- * any terms that are part of an or clause need to be searched to find the
- * associated events.
- */
- public Set<String> getOrTermsForOptimizer() {
- return orTerms;
- }
-
- /**
- *
- * @return String, Integer, and Float literals used in the query.
- */
- public Set<Object> getQueryLiterals() {
- return literals;
- }
-
- /**
- *
- * @return Set of all identifiers (field names) in the query.
- */
- public Set<String> getQueryIdentifiers() {
- return terms.keySet();
- }
-
- /**
- *
- * @return map of term (field name) to QueryTerm object
- */
- public Multimap<String,QueryTerm> getQueryTerms() {
- return terms;
- }
-
- public Object visit(SimpleNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTJexlScript node, Object data) {
- return null;
- }
-
- public Object visit(ASTBlock node, Object data) {
- return null;
- }
-
- public Object visit(ASTAmbiguous node, Object data) {
- return null;
- }
-
- public Object visit(ASTIfStatement node, Object data) {
- return null;
- }
-
- public Object visit(ASTWhileStatement node, Object data) {
- return null;
- }
-
- public Object visit(ASTForeachStatement node, Object data) {
- return null;
- }
-
- public Object visit(ASTAssignment node, Object data) {
- return null;
- }
-
- public Object visit(ASTTernaryNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTOrNode node, Object data) {
- boolean previouslyInOrContext = false;
- EvaluationContext ctx = null;
- if (null != data && data instanceof EvaluationContext) {
- ctx = (EvaluationContext) data;
- previouslyInOrContext = ctx.inOrContext;
- } else {
- ctx = new EvaluationContext();
- }
- ctx.inOrContext = true;
- //Process both sides of this node.
- node.jjtGetChild(0).jjtAccept(this, ctx);
- node.jjtGetChild(1).jjtAccept(this, ctx);
- //reset the state
- if (null != data && !previouslyInOrContext)
- ctx.inOrContext = false;
- return null;
- }
-
- public Object visit(ASTAndNode node, Object data) {
- boolean previouslyInAndContext = false;
- EvaluationContext ctx = null;
- if (null != data && data instanceof EvaluationContext) {
- ctx = (EvaluationContext) data;
- previouslyInAndContext = ctx.inAndContext;
- } else {
- ctx = new EvaluationContext();
- }
- ctx.inAndContext = true;
- //Process both sides of this node.
- node.jjtGetChild(0).jjtAccept(this, ctx);
- node.jjtGetChild(1).jjtAccept(this, ctx);
- //reset the state
- if (null != data && !previouslyInAndContext)
- ctx.inAndContext = false;
- return null;
- }
-
- public Object visit(ASTBitwiseOrNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTBitwiseXorNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTBitwiseAndNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTEQNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTNENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = true;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- if (negated)
- negatedTerms.add(fieldName.toString());
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTLTNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTGTNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTLENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTGENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTERNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTNRNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = true;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- if (negated)
- negatedTerms.add(fieldName.toString());
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- terms.put(fieldName.toString(), term);
- return null;
- }
-
- public Object visit(ASTAdditiveNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTAdditiveOperator node, Object data) {
- return null;
- }
-
- public Object visit(ASTMulNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTDivNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTModNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTUnaryMinusNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTBitwiseComplNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTNotNode node, Object data) {
- boolean previouslyInNotContext = false;
- EvaluationContext ctx = null;
- if (null != data && data instanceof EvaluationContext) {
- ctx = (EvaluationContext) data;
- previouslyInNotContext = ctx.inNotContext;
- } else {
- ctx = new EvaluationContext();
- }
- ctx.inNotContext = true;
- //Process both sides of this node.
- node.jjtGetChild(0).jjtAccept(this, ctx);
- //reset the state
- if (null != data && !previouslyInNotContext)
- ctx.inNotContext = false;
- return null;
- }
-
- public Object visit(ASTIdentifier node, Object data) {
- if (data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inAndContext)
- andTerms.add(node.image);
- if (ctx.inNotContext)
- negatedTerms.add(node.image);
- if (ctx.inOrContext)
- orTerms.add(node.image);
- }
- return new TermResult(node.image);
- }
-
- public Object visit(ASTNullLiteral node, Object data) {
- literals.add(node.image);
- return new LiteralResult(node.image);
- }
-
- public Object visit(ASTTrueNode node, Object data) {
- return new LiteralResult(node.image);
- }
-
- public Object visit(ASTFalseNode node, Object data) {
- return new LiteralResult(node.image);
- }
-
- public Object visit(ASTIntegerLiteral node, Object data) {
- literals.add(node.image);
- return new LiteralResult(node.image);
- }
-
- public Object visit(ASTFloatLiteral node, Object data) {
- literals.add(node.image);
- return new LiteralResult(node.image);
- }
-
- public Object visit(ASTStringLiteral node, Object data) {
- literals.add("'"+node.image+"'");
- return new LiteralResult("'"+node.image+"'");
- }
-
- public Object visit(ASTArrayLiteral node, Object data) {
- return null;
- }
-
- public Object visit(ASTMapLiteral node, Object data) {
- return null;
- }
-
- public Object visit(ASTMapEntry node, Object data) {
- return null;
- }
-
- public Object visit(ASTEmptyFunction node, Object data) {
- return null;
- }
-
- public Object visit(ASTSizeFunction node, Object data) {
- return null;
- }
-
- public Object visit(ASTFunctionNode node, Object data) {
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = true;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- //used to rebuild function call from the AST
- StringBuilder buf = new StringBuilder();
- String sep = "";
- // objectNode 0 is the prefix
- buf.append(node.jjtGetChild(0).image).append(":");
- // objectNode 1 is the identifier , the others are parameters.
- buf.append(node.jjtGetChild(1).image).append("(");
- // process the remaining arguments
- FunctionResult fr = new FunctionResult();
- int argc = node.jjtGetNumChildren() - 2;
- for (int i = 0; i < argc; i++) {
- //Process both sides of this node.
- Object result = node.jjtGetChild(i + 2).jjtAccept(this, data);
- if (result instanceof TermResult) {
- TermResult tr = (TermResult) result;
- fr.getTerms().add(tr);
- buf.append(sep).append(tr.value);
- sep = ", ";
- } else {
- buf.append(sep).append(node.jjtGetChild(i + 2).image);
- sep = ", ";
- }
- }
- buf.append(")");
- //Capture the entire function call for each function parameter
- for (TermResult tr : fr.terms)
- terms.put((String) tr.value, new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), buf.toString()));
- return fr;
- }
-
- public Object visit(ASTMethodNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTSizeMethod node, Object data) {
- return null;
- }
-
- public Object visit(ASTConstructorNode node, Object data) {
- return null;
- }
-
- public Object visit(ASTArrayAccess node, Object data) {
- return null;
- }
-
- public Object visit(ASTReference node, Object data) {
- return node.jjtGetChild(0).jjtAccept(this, data);
- }
-
- protected void decodeResults(Object left, Object right, StringBuilder fieldName, ObjectHolder holder) {
- if (left instanceof TermResult) {
- TermResult tr = (TermResult) left;
- fieldName.append((String) tr.value);
- //Then the right has to be the value
- if (right instanceof LiteralResult) {
- holder.setObject(((LiteralResult) right).value);
- } else {
- throw new IllegalArgumentException("Object mismatch");
- }
- } else if (right instanceof TermResult) {
- TermResult tr = (TermResult) right;
- fieldName.append((String) tr.value);
- if (left instanceof LiteralResult) {
- holder.setObject(((LiteralResult) left).value);
- } else {
- throw new IllegalArgumentException("Object mismatch");
- }
-
- } else {
- throw new IllegalArgumentException("No Term specified in query");
- }
- }
+
+ public static class QueryTerm {
+ private boolean negated = false;
+ private String operator = null;
+ private Object value = null;
+
+ public QueryTerm(boolean negated, String operator, Object value) {
+ super();
+ this.negated = negated;
+ this.operator = operator;
+ this.value = value;
+ }
+
+ public boolean isNegated() {
+ return negated;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setNegated(boolean negated) {
+ this.negated = negated;
+ }
+
+ public void setOperator(String operator) {
+ this.operator = operator;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("negated: ").append(negated).append(", operator: ").append(operator).append(", value: ").append(value);
+ return buf.toString();
+ }
+ }
+
+ /**
+ * Holder object
+ */
+ static class ObjectHolder {
+ Object object;
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+ }
+
+ static class FunctionResult {
+ private List<TermResult> terms = new ArrayList<TermResult>();
+
+ public List<TermResult> getTerms() {
+ return terms;
+ }
+ }
+
+ /**
+ * Holder object for a term (i.e. field name)
+ */
+ static class TermResult {
+ Object value;
+
+ public TermResult(Object value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Holder object for a literal (integer, float, string, or null literal) value
+ */
+ static class LiteralResult {
+ Object value;
+
+ public LiteralResult(Object value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Object used to store context information as the AST is being iterated over.
+ */
+ static class EvaluationContext {
+ boolean inOrContext = false;
+ boolean inNotContext = false;
+ boolean inAndContext = false;
+ }
+
+ /**
+ * Object to store information from previously parsed queries.
+ */
+ private static class CacheEntry {
+ private Set<String> negatedTerms = null;
+ private Set<String> andTerms = null;
+ private Set<String> orTerms = null;
+ private Set<Object> literals = null;
+ private Multimap<String,QueryTerm> terms = null;
+ private ASTJexlScript rootNode = null;
+ private TreeNode tree = null;
+
+ public CacheEntry(Set<String> negatedTerms, Set<String> andTerms, Set<String> orTerms, Set<Object> literals, Multimap<String,QueryTerm> terms,
+ ASTJexlScript rootNode, TreeNode tree) {
+ super();
+ this.negatedTerms = negatedTerms;
+ this.andTerms = andTerms;
+ this.orTerms = orTerms;
+ this.literals = literals;
+ this.terms = terms;
+ this.rootNode = rootNode;
+ this.tree = tree;
+ }
+
+ public Set<String> getNegatedTerms() {
+ return negatedTerms;
+ }
+
+ public Set<String> getAndTerms() {
+ return andTerms;
+ }
+
+ public Set<String> getOrTerms() {
+ return orTerms;
+ }
+
+ public Set<Object> getLiterals() {
+ return literals;
+ }
+
+ public Multimap<String,QueryTerm> getTerms() {
+ return terms;
+ }
+
+ public ASTJexlScript getRootNode() {
+ return rootNode;
+ }
+
+ public TreeNode getTree() {
+ return tree;
+ }
+ }
+
+ private static final int SEED = 650567;
+
+ private static LRUMap cache = new LRUMap();
+
+ protected Set<String> negatedTerms = new HashSet<String>();
+
+ private Set<String> andTerms = new HashSet<String>();
+
+ private Set<String> orTerms = new HashSet<String>();
+
+ /**
+ * List of String, Integer, Float, etc literals that were passed in the query
+ */
+ private Set<Object> literals = new HashSet<Object>();
+
+ /**
+ * Map of terms (field names) to QueryTerm objects.
+ */
+ private Multimap<String,QueryTerm> terms = HashMultimap.create();
+
+ private ASTJexlScript rootNode = null;
+
+ private TreeNode tree = null;
+
+ private int hashVal = 0;
+
+ public QueryParser() {}
+
+ private void reset() {
+ this.negatedTerms.clear();
+ this.andTerms.clear();
+ this.orTerms.clear();
+ this.literals.clear();
+ this.terms = HashMultimap.create();
+ }
+
+ public void execute(String query) throws ParseException {
+ reset();
+ query = query.replaceAll("\\s+AND\\s+", " and ");
+ query = query.replaceAll("\\s+OR\\s+", " or ");
+ query = query.replaceAll("\\s+NOT\\s+", " not ");
+
+ // Check to see if its in the cache
+ Hash hash = MurmurHash.getInstance();
+ this.hashVal = hash.hash(query.getBytes(), SEED);
+ if (cache.containsKey(hashVal)) {
+ CacheEntry entry = (CacheEntry) cache.get(hashVal);
+ this.negatedTerms = entry.getNegatedTerms();
+ this.andTerms = entry.getAndTerms();
+ this.orTerms = entry.getOrTerms();
+ this.literals = entry.getLiterals();
+ this.terms = entry.getTerms();
+ this.rootNode = entry.getRootNode();
+ this.tree = entry.getTree();
+ } else {
+ Parser p = new Parser(new StringReader(";"));
+ rootNode = p.parse(new StringReader(query), null);
+ rootNode.childrenAccept(this, null);
+ TreeBuilder builder = new TreeBuilder(rootNode);
+ tree = builder.getRootNode();
+ CacheEntry entry = new CacheEntry(this.negatedTerms, this.andTerms, this.orTerms, this.literals, this.terms, rootNode, tree);
+ cache.put(hashVal, entry);
+ }
+
+ }
+
+ public static void clearCache() {
+ cache.clear();
+ }
+
+ /**
+ *
+ * @return this queries hash value
+ */
+ public int getHashValue() {
+ return this.hashVal;
+ }
+
+ public TreeNode getIteratorTree() {
+ return this.tree;
+ }
+
+ /**
+ *
+ * @return JEXL abstract syntax tree
+ */
+ public ASTJexlScript getAST() {
+ return this.rootNode;
+ }
+
+ /**
+ *
+ * @return Set of field names to use in the optimizer for nots. As a general rule none of these terms should be used to find an event and should they should
+ * be evaluated on each event after being found.
+ */
+ public Set<String> getNegatedTermsForOptimizer() {
+ return negatedTerms;
+ }
+
+ /**
+ *
+ * @return Set of field names to use in the optimizer for ands. As a general rule any one term of an and clause can be used to find associated events.
+ */
+ public Set<String> getAndTermsForOptimizer() {
+ return andTerms;
+ }
+
+ /**
+ *
+ * @return Set of field names to use in the optimizer for ors. As a general rule any terms that are part of an or clause need to be searched to find the
+ * associated events.
+ */
+ public Set<String> getOrTermsForOptimizer() {
+ return orTerms;
+ }
+
+ /**
+ *
+ * @return String, Integer, and Float literals used in the query.
+ */
+ public Set<Object> getQueryLiterals() {
+ return literals;
+ }
+
+ /**
+ *
+ * @return Set of all identifiers (field names) in the query.
+ */
+ public Set<String> getQueryIdentifiers() {
+ return terms.keySet();
+ }
+
+ /**
+ *
+ * @return map of term (field name) to QueryTerm object
+ */
+ public Multimap<String,QueryTerm> getQueryTerms() {
+ return terms;
+ }
+
+ public Object visit(SimpleNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTJexlScript node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTBlock node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTAmbiguous node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTIfStatement node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTWhileStatement node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTForeachStatement node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTAssignment node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTTernaryNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTOrNode node, Object data) {
+ boolean previouslyInOrContext = false;
+ EvaluationContext ctx = null;
+ if (null != data && data instanceof EvaluationContext) {
+ ctx = (EvaluationContext) data;
+ previouslyInOrContext = ctx.inOrContext;
+ } else {
+ ctx = new EvaluationContext();
+ }
+ ctx.inOrContext = true;
+ // Process both sides of this node.
+ node.jjtGetChild(0).jjtAccept(this, ctx);
+ node.jjtGetChild(1).jjtAccept(this, ctx);
+ // reset the state
+ if (null != data && !previouslyInOrContext)
+ ctx.inOrContext = false;
+ return null;
+ }
+
+ public Object visit(ASTAndNode node, Object data) {
+ boolean previouslyInAndContext = false;
+ EvaluationContext ctx = null;
+ if (null != data && data instanceof EvaluationContext) {
+ ctx = (EvaluationContext) data;
+ previouslyInAndContext = ctx.inAndContext;
+ } else {
+ ctx = new EvaluationContext();
+ }
+ ctx.inAndContext = true;
+ // Process both sides of this node.
+ node.jjtGetChild(0).jjtAccept(this, ctx);
+ node.jjtGetChild(1).jjtAccept(this, ctx);
+ // reset the state
+ if (null != data && !previouslyInAndContext)
+ ctx.inAndContext = false;
+ return null;
+ }
+
+ public Object visit(ASTBitwiseOrNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTBitwiseXorNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTBitwiseAndNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTEQNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTNENode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = true;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ if (negated)
+ negatedTerms.add(fieldName.toString());
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTLTNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTGTNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTLENode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTGENode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTERNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTNRNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = true;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ if (negated)
+ negatedTerms.add(fieldName.toString());
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ terms.put(fieldName.toString(), term);
+ return null;
+ }
+
+ public Object visit(ASTAdditiveNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTAdditiveOperator node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTMulNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTDivNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTModNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTUnaryMinusNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTBitwiseComplNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTNotNode node, Object data) {
+ boolean previouslyInNotContext = false;
+ EvaluationContext ctx = null;
+ if (null != data && data instanceof EvaluationContext) {
+ ctx = (EvaluationContext) data;
+ previouslyInNotContext = ctx.inNotContext;
+ } else {
+ ctx = new EvaluationContext();
+ }
+ ctx.inNotContext = true;
+ // Process both sides of this node.
+ node.jjtGetChild(0).jjtAccept(this, ctx);
+ // reset the state
+ if (null != data && !previouslyInNotContext)
+ ctx.inNotContext = false;
+ return null;
+ }
+
+ public Object visit(ASTIdentifier node, Object data) {
+ if (data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inAndContext)
+ andTerms.add(node.image);
+ if (ctx.inNotContext)
+ negatedTerms.add(node.image);
+ if (ctx.inOrContext)
+ orTerms.add(node.image);
+ }
+ return new TermResult(node.image);
+ }
+
+ public Object visit(ASTNullLiteral node, Object data) {
+ literals.add(node.image);
+ return new LiteralResult(node.image);
+ }
+
+ public Object visit(ASTTrueNode node, Object data) {
+ return new LiteralResult(node.image);
+ }
+
+ public Object visit(ASTFalseNode node, Object data) {
+ return new LiteralResult(node.image);
+ }
+
+ public Object visit(ASTIntegerLiteral node, Object data) {
+ literals.add(node.image);
+ return new LiteralResult(node.image);
+ }
+
+ public Object visit(ASTFloatLiteral node, Object data) {
+ literals.add(node.image);
+ return new LiteralResult(node.image);
+ }
+
+ public Object visit(ASTStringLiteral node, Object data) {
+ literals.add("'" + node.image + "'");
+ return new LiteralResult("'" + node.image + "'");
+ }
+
+ public Object visit(ASTArrayLiteral node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTMapLiteral node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTMapEntry node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTEmptyFunction node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTSizeFunction node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTFunctionNode node, Object data) {
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = true;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ // used to rebuild function call from the AST
+ StringBuilder buf = new StringBuilder();
+ String sep = "";
+ // objectNode 0 is the prefix
+ buf.append(node.jjtGetChild(0).image).append(":");
+ // objectNode 1 is the identifier , the others are parameters.
+ buf.append(node.jjtGetChild(1).image).append("(");
+ // process the remaining arguments
+ FunctionResult fr = new FunctionResult();
+ int argc = node.jjtGetNumChildren() - 2;
+ for (int i = 0; i < argc; i++) {
+ // Process both sides of this node.
+ Object result = node.jjtGetChild(i + 2).jjtAccept(this, data);
+ if (result instanceof TermResult) {
+ TermResult tr = (TermResult) result;
+ fr.getTerms().add(tr);
+ buf.append(sep).append(tr.value);
+ sep = ", ";
+ } else {
+ buf.append(sep).append(node.jjtGetChild(i + 2).image);
+ sep = ", ";
+ }
+ }
+ buf.append(")");
+ // Capture the entire function call for each function parameter
+ for (TermResult tr : fr.terms)
+ terms.put((String) tr.value, new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), buf.toString()));
+ return fr;
+ }
+
+ public Object visit(ASTMethodNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTSizeMethod node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTConstructorNode node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTArrayAccess node, Object data) {
+ return null;
+ }
+
+ public Object visit(ASTReference node, Object data) {
+ return node.jjtGetChild(0).jjtAccept(this, data);
+ }
+
+ protected void decodeResults(Object left, Object right, StringBuilder fieldName, ObjectHolder holder) {
+ if (left instanceof TermResult) {
+ TermResult tr = (TermResult) left;
+ fieldName.append((String) tr.value);
+ // Then the right has to be the value
+ if (right instanceof LiteralResult) {
+ holder.setObject(((LiteralResult) right).value);
+ } else {
+ throw new IllegalArgumentException("Object mismatch");
+ }
+ } else if (right instanceof TermResult) {
+ TermResult tr = (TermResult) right;
+ fieldName.append((String) tr.value);
+ if (left instanceof LiteralResult) {
+ holder.setObject(((LiteralResult) left).value);
+ } else {
+ throw new IllegalArgumentException("Object mismatch");
+ }
+
+ } else {
+ throw new IllegalArgumentException("No Term specified in query");
+ }
+ }
}