mirror of https://github.com/apache/activemq.git
https://issues.apache.org/jira/browse/AMQ-3097 - apply patch with some additional unit test, with thanks
git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1478973 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3a8eb74a9f
commit
bf071376b5
|
@ -56,6 +56,7 @@ import java.util.*;
|
|||
import javax.jms.InvalidSelectorException;
|
||||
|
||||
import org.apache.activemq.filter.*;
|
||||
import org.apache.activemq.filter.FunctionCallExpression.invalidFunctionExpressionException;
|
||||
import org.apache.activemq.util.LRUCache;
|
||||
|
||||
/**
|
||||
|
@ -130,7 +131,6 @@ public class SelectorParser {
|
|||
throw new ParseException("Expression will not result in a boolean value: " + value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
PARSER_END(SelectorParser)
|
||||
|
@ -490,6 +490,9 @@ Expression unaryExpr() :
|
|||
left = UnaryExpression.createXQuery( s );
|
||||
}
|
||||
|
|
||||
LOOKAHEAD( <ID> "(" )
|
||||
left = functionCallExpr()
|
||||
|
|
||||
left = primaryExpr()
|
||||
)
|
||||
{
|
||||
|
@ -498,6 +501,42 @@ Expression unaryExpr() :
|
|||
|
||||
}
|
||||
|
||||
Expression functionCallExpr () :
|
||||
{
|
||||
Token func_name;
|
||||
FunctionCallExpression func_call = null;
|
||||
Expression arg = null;
|
||||
ArrayList arg_list = new ArrayList();
|
||||
}
|
||||
{
|
||||
func_name = <ID> "("
|
||||
(
|
||||
arg = unaryExpr()
|
||||
{
|
||||
arg_list.add(arg);
|
||||
}
|
||||
(
|
||||
","
|
||||
arg = unaryExpr()
|
||||
{
|
||||
arg_list.add(arg);
|
||||
}
|
||||
) *
|
||||
)
|
||||
")"
|
||||
{
|
||||
try
|
||||
{
|
||||
return FunctionCallExpression.createFunctionCall(func_name.image, arg_list);
|
||||
}
|
||||
catch ( invalidFunctionExpressionException inv_exc )
|
||||
{
|
||||
// Re-throw as an error to avoid the need to propogate the throws declaration.
|
||||
throw new Error("invalid function call expression", inv_exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expression primaryExpr() :
|
||||
{
|
||||
Expression left=null;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Function call expression that evaluates to a boolean value. Selector parsing requires BooleanExpression objects for
|
||||
* Boolean expressions, such as operands to AND, and as the final result of a selector. This provides that interface
|
||||
* for function call expressions that resolve to Boolean values.
|
||||
* <p/>
|
||||
* If a function can return different types at evaluation-time, the function implementation needs to decide whether it
|
||||
* supports casting to Boolean at parse-time.
|
||||
*
|
||||
* @see FunctionCallExpression#createFunctionCall
|
||||
*/
|
||||
|
||||
public class BooleanFunctionCallExpr extends FunctionCallExpression implements BooleanExpression {
|
||||
/**
|
||||
* Constructs a function call expression with the named filter function and arguments, which returns a boolean
|
||||
* result.
|
||||
*
|
||||
* @param func_name - Name of the filter function to be called when evaluated.
|
||||
* @param args - List of argument expressions passed to the function.
|
||||
*/
|
||||
|
||||
public BooleanFunctionCallExpr(String func_name, List<Expression> args)
|
||||
throws invalidFunctionExpressionException {
|
||||
super(func_name, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate the function call expression, in the given context, and return an indication of whether the
|
||||
* expression "matches" (i.e. evaluates to true).
|
||||
*
|
||||
* @param message_ctx - message context against which the expression will be evaluated.
|
||||
* @return the boolean evaluation of the function call expression.
|
||||
*/
|
||||
|
||||
public boolean matches(MessageEvaluationContext message_ctx) throws javax.jms.JMSException {
|
||||
Boolean result;
|
||||
|
||||
result = (Boolean) evaluate(message_ctx);
|
||||
|
||||
if (result != null)
|
||||
return result.booleanValue();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.activemq.filter.function.FilterFunction;
|
||||
|
||||
/**
|
||||
* Function call expression for use in selector expressions. Includes an extensible interface to allow custom
|
||||
* functions to be added without changes to the core.
|
||||
* <p/>
|
||||
* Use registerFunction() to register new function implementations for use in selectors.
|
||||
*/
|
||||
|
||||
public class FunctionCallExpression implements Expression {
|
||||
protected static final HashMap<String, functionRegistration> functionRegistry = new HashMap();
|
||||
|
||||
protected String functionName;
|
||||
protected java.util.ArrayList arguments;
|
||||
protected FilterFunction filterFunc;
|
||||
|
||||
static {
|
||||
// Register the built-in functions. It would be nice to just have each function class register
|
||||
// itself, but that only works when the classes are loaded, which may be never.
|
||||
|
||||
org.apache.activemq.filter.function.BuiltinFunctionRegistry.register();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the function with the specified name.
|
||||
*
|
||||
* @param name - the function name, as used in selector expressions. Case Sensitive.
|
||||
* @param impl - class which implements the function interface, including parse-time and evaluation-time
|
||||
* operations.
|
||||
* @return true - if the function is successfully registered; false - if a function with the same name is
|
||||
* already registered.
|
||||
*/
|
||||
|
||||
public static boolean registerFunction(String name, FilterFunction impl) {
|
||||
boolean result;
|
||||
|
||||
result = true;
|
||||
|
||||
synchronized (functionRegistry) {
|
||||
if (functionRegistry.containsKey(name))
|
||||
result = false;
|
||||
else
|
||||
functionRegistry.put(name, new functionRegistration(impl));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the registration of the function with the specified name.
|
||||
* <p/>
|
||||
* Note that parsed expressions using this function will still access its implementation after this call.
|
||||
*
|
||||
* @param name - name of the function to remove.
|
||||
*/
|
||||
|
||||
public static void deregisterFunction(String name) {
|
||||
synchronized (functionRegistry) {
|
||||
functionRegistry.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a function call expression with the named function and argument list.
|
||||
* <p/>
|
||||
* Use createFunctionCall() to create instances.
|
||||
*
|
||||
* @exception invalidFunctionExpressionException - if the function name is not valid.
|
||||
*/
|
||||
|
||||
protected FunctionCallExpression(String func_name, List<Expression> args)
|
||||
throws invalidFunctionExpressionException {
|
||||
functionRegistration func_reg;
|
||||
|
||||
synchronized (functionRegistry) {
|
||||
func_reg = functionRegistry.get(func_name);
|
||||
}
|
||||
|
||||
if (func_reg != null) {
|
||||
this.arguments = new java.util.ArrayList();
|
||||
this.arguments.addAll(args);
|
||||
this.functionName = func_name;
|
||||
this.filterFunc = func_reg.getFilterFunction();
|
||||
} else {
|
||||
throw new invalidFunctionExpressionException("invalid function name, \"" + func_name + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a function call expression for the named function and argument list, returning a Boolean function
|
||||
* call expression if the function returns a boolean value so that it may be used in boolean contexts.
|
||||
* Used by the parser when a function call is identified. Note that the function call is created after all
|
||||
* argument expressions so that the function call can properly detect whether it evaluates to a Boolean value.
|
||||
*
|
||||
* @param func_name - name of the function, as used in selectors.
|
||||
* @param args - list of argument expressions passed to the function.
|
||||
* @return an instance of a BooleanFunctionCallExpr if the function returns a boolean value in this call,
|
||||
* or a FunctionCallExpression otherwise.
|
||||
* @exception invalidFunctionExpression - if the function name is not valid, or the given argument list is
|
||||
* not valid for the function.
|
||||
*/
|
||||
|
||||
public static FunctionCallExpression createFunctionCall(String func_name, List<Expression> args)
|
||||
throws invalidFunctionExpressionException {
|
||||
FunctionCallExpression result;
|
||||
|
||||
//
|
||||
// Create a function call expression by default to use with validating the function call
|
||||
// expression and checking whether it returns a boolean result.
|
||||
//
|
||||
|
||||
result = new FunctionCallExpression(func_name, args);
|
||||
|
||||
|
||||
//
|
||||
// Check wether the function accepts this expression. I.E. are the arguments valid?
|
||||
//
|
||||
|
||||
if (result.filterFunc.isValid(result)) {
|
||||
//
|
||||
// If the result of the call is known to alwyas return a boolean value, wrap this
|
||||
// expression as a valid BooleanExpression so it will be accepted as a boolean result
|
||||
// by the selector grammar.
|
||||
//
|
||||
|
||||
if (result.filterFunc.returnsBoolean(result))
|
||||
result = new BooleanFunctionCallExpr(func_name, args);
|
||||
} else {
|
||||
//
|
||||
// Function does not like this expression.
|
||||
//
|
||||
|
||||
throw new invalidFunctionExpressionException("invalid call of function " + func_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the number of arguments for the function call defined in this expression.
|
||||
*
|
||||
* @return the number of arguments being passed to the function.
|
||||
*/
|
||||
|
||||
public int getNumArguments() {
|
||||
return arguments.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the argument at the specified index; the first argument is index 0. Used by implementations of
|
||||
* FilterFunction objects to check arguments and evaluate them, as needed.
|
||||
*
|
||||
* @param which - number of the argument to retrieve; the first is 0.
|
||||
*/
|
||||
|
||||
public Expression getArgument(int which) {
|
||||
return (Expression) arguments.get(which);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate the function call expression in the context given.
|
||||
*
|
||||
* @see Expression#evaluate
|
||||
*/
|
||||
|
||||
public Object evaluate(MessageEvaluationContext message_ctx)
|
||||
throws javax.jms.JMSException {
|
||||
return this.filterFunc.evaluate(this, message_ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translate the expression back into text in a form similar to the input to the selector parser.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result;
|
||||
boolean first_f;
|
||||
|
||||
result = new StringBuilder();
|
||||
|
||||
result.append(functionName);
|
||||
result.append("(");
|
||||
first_f = true;
|
||||
|
||||
for (Object arg : arguments) {
|
||||
if (first_f)
|
||||
first_f = false;
|
||||
else
|
||||
result.append(", ");
|
||||
|
||||
result.append(arg.toString());
|
||||
}
|
||||
|
||||
result.append(")");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
//// ////
|
||||
//// FUNCTION REGISTRATION ////
|
||||
//// ////
|
||||
|
||||
/**
|
||||
* Maintain a single function registration.
|
||||
*/
|
||||
|
||||
protected static class functionRegistration {
|
||||
protected FilterFunction filterFunction;
|
||||
|
||||
/**
|
||||
* Constructs a function registration for the given function implementation.
|
||||
*/
|
||||
|
||||
public functionRegistration(FilterFunction func) {
|
||||
this.filterFunction = func;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the filter function implementation.
|
||||
*/
|
||||
|
||||
public FilterFunction getFilterFunction() {
|
||||
return filterFunction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the filter function implementation for this registration.
|
||||
*/
|
||||
|
||||
public void setFilterFunction(FilterFunction func) {
|
||||
filterFunction = func;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exception indicating that an invalid function call expression was created, usually by the selector parser.
|
||||
* Conditions include invalid function names and invalid function arguments.
|
||||
*/
|
||||
|
||||
public static class invalidFunctionExpressionException extends java.lang.Exception {
|
||||
public invalidFunctionExpressionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public invalidFunctionExpressionException(String msg, java.lang.Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
|
||||
/**
|
||||
* Registry of built-in functions. Add built-in functions to this list to make sure they are registered at startup.
|
||||
* <p/>
|
||||
* Custom add-ons that are not built-in to the core ActiveMQ should not be listed here.
|
||||
* Use FunctionCallExpression.registerFunction() directly.
|
||||
*/
|
||||
|
||||
public class BuiltinFunctionRegistry {
|
||||
public static void register() {
|
||||
FunctionCallExpression.registerFunction("INLIST", new inListFunction());
|
||||
FunctionCallExpression.registerFunction("MAKELIST", new makeListFunction());
|
||||
FunctionCallExpression.registerFunction("REGEX", new regexMatchFunction());
|
||||
FunctionCallExpression.registerFunction("REPLACE", new replaceFunction());
|
||||
FunctionCallExpression.registerFunction("SPLIT", new splitFunction());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
|
||||
/**
|
||||
* Interface required for objects that will be registered as functions for use in selectors. Handles parse-
|
||||
* time and evaluation-time operations.
|
||||
*/
|
||||
|
||||
public interface FilterFunction {
|
||||
/**
|
||||
* Check whether the function, as it is used, is valid. Checking arguments here will return errors
|
||||
* to clients at the time invalid selectors are initially specified, rather than waiting until the selector is
|
||||
* applied to a message.
|
||||
*
|
||||
* @param FunctionCallExpression expr - the full expression of the function call, as used.
|
||||
* @return true - if the function call is valid; false - otherwise.
|
||||
*/
|
||||
public boolean isValid(FunctionCallExpression expr);
|
||||
|
||||
/**
|
||||
* Determine whether the function, as it will be called, returns a boolean value. Called during
|
||||
* expression parsing after the full expression for the function call, including its arguments, has
|
||||
* been parsed. This allows functions with variable return types to function as boolean expressions in
|
||||
* selectors without sacrificing parse-time checking.
|
||||
*
|
||||
* @param FunctionCallExpression expr - the full expression of the function call, as used.
|
||||
* @return true - if the function returns a boolean value for its use in the given expression;
|
||||
* false - otherwise.
|
||||
*/
|
||||
public boolean returnsBoolean(FunctionCallExpression expr);
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate the function call in the given context. The arguments must be evaluated, as-needed, by the
|
||||
* function. Note that boolean expressions must return Boolean objects.
|
||||
*
|
||||
* @param FunctionCallExpression expr - the full expression of the function call, as used.
|
||||
* @param MessageEvaluationContext message - the context within which to evaluate the call.
|
||||
*/
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message)
|
||||
throws javax.jms.JMSException;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
|
||||
/**
|
||||
* Filter function that matches a value against a list of values and evaluates to an indicator of membership in the
|
||||
* list. For example:
|
||||
* <p/>
|
||||
* <p style="margin-left: 4em">
|
||||
* INLIST( SPLIT('1,2,3', ',') , '2' )
|
||||
* </p>
|
||||
* <p/>
|
||||
* Note that the first argument must be a List. Strings containing lists are not acceptable; for example,
|
||||
* INLIST('1,2,3', '1'), will cause an exception to be thrown at evaluation-time.
|
||||
*/
|
||||
|
||||
public class inListFunction implements FilterFunction {
|
||||
/**
|
||||
* Check whether the given expression is a valid call of this function. Two arguments are required. Note that
|
||||
* the evaluated results of the arguments will be compared with Object#equals().
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if the expression is valid; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean isValid(FunctionCallExpression expr) {
|
||||
if (expr.getNumArguments() != 2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given expression, which consists of a call to this function, evaluates to a Boolean.
|
||||
* If the function can return different more than one type of value at evaluation-time, it must decide whether
|
||||
* to cast the result to a Boolean at this time.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if the expression is valid; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean returnsBoolean(FunctionCallExpression expr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evalutate the given expression, which consists of a call to this function, in the context given. Checks
|
||||
* whether the second argument is a member of the list in the first argument.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @param message_ctx - the context in which the call is being evaluated.
|
||||
* @return Boolean - the result of the evaluation.
|
||||
*/
|
||||
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message_ctx)
|
||||
throws javax.jms.JMSException {
|
||||
java.util.List arr;
|
||||
int cur;
|
||||
Object cand;
|
||||
boolean found_f;
|
||||
|
||||
arr = (java.util.List) expr.getArgument(0).evaluate(message_ctx);
|
||||
cand = expr.getArgument(1).evaluate(message_ctx);
|
||||
|
||||
cur = 0;
|
||||
found_f = false;
|
||||
while ((cur < arr.size()) && (!found_f)) {
|
||||
found_f = arr.get(cur).equals(cand);
|
||||
cur++;
|
||||
}
|
||||
|
||||
return Boolean.valueOf(found_f);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
|
||||
/**
|
||||
* Filter function that creates a list with each argument being one element in the list.
|
||||
* For example:
|
||||
* <p/>
|
||||
* <p style="margin-left: 4em">
|
||||
* MAKELIST( '1', '2', '3' )
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public class makeListFunction implements FilterFunction {
|
||||
/**
|
||||
* Check whether the given expression is a valid call of this function. Any number of arguments is accepted.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if the expression is valid; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean isValid(FunctionCallExpression expr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate that this function never evaluates to a Boolean result.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return false - this Filter Function never evaluates to a Boolean.
|
||||
*/
|
||||
|
||||
public boolean returnsBoolean(FunctionCallExpression expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evalutate the given expression, which consists of a call to this function, in the context given. Creates
|
||||
* a list containing the evaluated results of its argument expressions.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @param message_ctx - the context in which the call is being evaluated.
|
||||
* @return java.util.List - the result of the evaluation.
|
||||
*/
|
||||
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message)
|
||||
throws javax.jms.JMSException {
|
||||
java.util.ArrayList ele_arr;
|
||||
int num_arg;
|
||||
int cur;
|
||||
|
||||
num_arg = expr.getNumArguments();
|
||||
ele_arr = new java.util.ArrayList(num_arg);
|
||||
|
||||
cur = 0;
|
||||
while (cur < num_arg) {
|
||||
ele_arr.add(expr.getArgument(cur).evaluate(message));
|
||||
cur++;
|
||||
}
|
||||
|
||||
return (java.util.List) ele_arr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Filter Function implementations for JMS Selectors.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
import org.apache.activemq.util.LRUCache;
|
||||
|
||||
/**
|
||||
* Filter function that matches a value against a regular expression.
|
||||
* <p/>
|
||||
* <p style="margin-left: 4em">
|
||||
* REGEX( 'A.B', 'A-B' )
|
||||
* </p>
|
||||
* <p/>
|
||||
* Note that the regular expression is not anchored; use the anchor characters, ^ and $, as-needed. For example,
|
||||
* REGEX( 'AA', 'XAAX' ) evaluates to true while REGEX( '^AA$' , 'XAAX' ) evaluates to false.
|
||||
*/
|
||||
|
||||
public class regexMatchFunction implements FilterFunction {
|
||||
protected static final LRUCache<String, Pattern> compiledExprCache = new LRUCache(100);
|
||||
|
||||
/**
|
||||
* Check whether the given expression is a valid call of this function. Two arguments are required. When
|
||||
* evaluated, the arguments are converted to strings if they are not already strings.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if the expression is valid; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean isValid(FunctionCallExpression expr) {
|
||||
if (expr.getNumArguments() == 2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate that this Filter Function evaluates to a Boolean result.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - this function always evaluates to a Boolean result.
|
||||
*/
|
||||
|
||||
public boolean returnsBoolean(FunctionCallExpression expr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evalutate the given expression, which consists of a call to this function, in the context given. Returns
|
||||
* an indication of whether the second argument matches the regular expression in the first argument.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @param message_ctx - the context in which the call is being evaluated.
|
||||
* @return true - if the value matches the regular expression; false - otherwise.
|
||||
*/
|
||||
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message)
|
||||
throws javax.jms.JMSException {
|
||||
Object reg;
|
||||
Object cand;
|
||||
String reg_str;
|
||||
String cand_str;
|
||||
Pattern pat;
|
||||
Matcher match_eng;
|
||||
|
||||
//
|
||||
// Evaluate the first argument (the regular expression).
|
||||
//
|
||||
reg = expr.getArgument(0).evaluate(message);
|
||||
|
||||
if (reg != null) {
|
||||
// Convert to a string, if it's not already a string.
|
||||
if (reg instanceof String)
|
||||
reg_str = (String) reg;
|
||||
else
|
||||
reg_str = reg.toString();
|
||||
|
||||
|
||||
//
|
||||
// Evaluate the second argument (the candidate to match against the regular
|
||||
// expression).
|
||||
//
|
||||
cand = expr.getArgument(1).evaluate(message);
|
||||
|
||||
if (cand != null) {
|
||||
// Convert to a string, if it's not already a string.
|
||||
if (cand instanceof String)
|
||||
cand_str = (String) cand;
|
||||
else
|
||||
cand_str = cand.toString();
|
||||
|
||||
|
||||
//
|
||||
// Obtain the compiled regular expression and match it.
|
||||
//
|
||||
|
||||
pat = getCompiledPattern(reg_str);
|
||||
match_eng = pat.matcher(cand_str);
|
||||
|
||||
|
||||
//
|
||||
// Return an indication of whether the regular expression matches at any
|
||||
// point in the candidate (see Matcher#find()).
|
||||
//
|
||||
|
||||
return Boolean.valueOf(match_eng.find());
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a compiled pattern for the given pattern string. A cache of recently used strings is maintained to
|
||||
* improve performance.
|
||||
*
|
||||
* @param reg_ex_str - the string specifying the regular expression.
|
||||
* @return Pattern - compiled form of the regular expression.
|
||||
*/
|
||||
|
||||
protected Pattern getCompiledPattern(String reg_ex_str) {
|
||||
Pattern result;
|
||||
|
||||
//
|
||||
// Look for the compiled pattern in the cache.
|
||||
//
|
||||
|
||||
synchronized (compiledExprCache) {
|
||||
result = compiledExprCache.get(reg_ex_str);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// If it was not found, compile it and add it to the cache.
|
||||
//
|
||||
|
||||
if (result == null) {
|
||||
result = Pattern.compile(reg_ex_str);
|
||||
|
||||
synchronized (compiledExprCache) {
|
||||
compiledExprCache.put(reg_ex_str, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
|
||||
/**
|
||||
* Function which replaces regular expression matches in a source string to a replacement literal.
|
||||
* <p/>
|
||||
* For Example:
|
||||
* REPLACE('1,2/3', '[,/]', ';') returns '1;2;3'
|
||||
*/
|
||||
|
||||
public class replaceFunction implements FilterFunction {
|
||||
/**
|
||||
* Check whether the given expression is valid for this function.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if three arguments are passed to the function; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean isValid(FunctionCallExpression expr) {
|
||||
if (expr.getNumArguments() == 3)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate that this function does not return a boolean value.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return false - this filter function always evaluates to a string.
|
||||
*/
|
||||
|
||||
public boolean returnsBoolean(FunctionCallExpression expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate the given expression for this function in the given context. The result of the evaluation is a
|
||||
* string with all matches of the regular expression, from the evaluation of the second argument, replaced by
|
||||
* the string result from the evaluation of the third argument. Replacement is performed by
|
||||
* String#replaceAll().
|
||||
* <p/>
|
||||
* Note that all three arguments must be Strings.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return String - the result of the replacement.
|
||||
*/
|
||||
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message_ctx)
|
||||
throws javax.jms.JMSException {
|
||||
String src;
|
||||
String match_regex;
|
||||
String repl_lit;
|
||||
String result;
|
||||
|
||||
src = (String) expr.getArgument(0).evaluate(message_ctx);
|
||||
match_regex = (String) expr.getArgument(1).evaluate(message_ctx);
|
||||
repl_lit = (String) expr.getArgument(2).evaluate(message_ctx);
|
||||
|
||||
result = src.replaceAll(match_regex, repl_lit);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* 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 org.apache.activemq.filter.function;
|
||||
|
||||
import org.apache.activemq.filter.FunctionCallExpression;
|
||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||
|
||||
/**
|
||||
* Function which splits a string into a list of strings given a regular expression for the separator.
|
||||
*/
|
||||
|
||||
public class splitFunction implements FilterFunction {
|
||||
/**
|
||||
* Check whether the given expression is valid for this function.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return true - if two or three arguments are passed to the function; false - otherwise.
|
||||
*/
|
||||
|
||||
public boolean isValid(FunctionCallExpression expr) {
|
||||
if ((expr.getNumArguments() >= 2) && (expr.getNumArguments() <= 3))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate that this function does not return a boolean value.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return false - indicating this filter function never evaluates to a boolean result.
|
||||
*/
|
||||
|
||||
public boolean returnsBoolean(FunctionCallExpression expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate the given expression for this function in the given context. A list of zero or more strings
|
||||
* results from the evaluation. The result of the evaluation of the first argument is split with the regular
|
||||
* expression which results from the evaluation of the second argument. If a third argument is given, it
|
||||
* is an integer which limits the split. String#split() performs the split.
|
||||
* <p/>
|
||||
* The first two arguments must be Strings. If a third is given, it must be an Integer.
|
||||
*
|
||||
* @param expr - the expression consisting of a call to this function.
|
||||
* @return List - a list of Strings resulting from the split.
|
||||
*/
|
||||
|
||||
public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message_ctx)
|
||||
throws javax.jms.JMSException {
|
||||
String src;
|
||||
String split_pat;
|
||||
String[] result;
|
||||
|
||||
src = (String) expr.getArgument(0).evaluate(message_ctx);
|
||||
split_pat = (String) expr.getArgument(1).evaluate(message_ctx);
|
||||
|
||||
if (expr.getNumArguments() > 2) {
|
||||
Integer limit;
|
||||
|
||||
limit = (Integer) expr.getArgument(2).evaluate(message_ctx);
|
||||
|
||||
result = src.split(split_pat, limit.intValue());
|
||||
} else {
|
||||
result = src.split(split_pat);
|
||||
}
|
||||
|
||||
return java.util.Arrays.asList(result);
|
||||
}
|
||||
}
|
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
package org.apache.activemq.selector;
|
||||
|
||||
import javax.jms.InvalidSelectorException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.activemq.filter.BooleanExpression;
|
||||
import org.apache.activemq.filter.BooleanFunctionCallExpr;
|
||||
import org.apache.activemq.filter.ComparisonExpression;
|
||||
import org.apache.activemq.filter.Expression;
|
||||
import org.apache.activemq.filter.LogicExpression;
|
||||
|
@ -33,6 +35,19 @@ import org.slf4j.LoggerFactory;
|
|||
public class SelectorParserTest extends TestCase {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SelectorParserTest.class);
|
||||
|
||||
public void testFunctionCall() throws Exception {
|
||||
Object filter = parse("REGEX('sales.*', group)");
|
||||
assertTrue("expected type", filter instanceof BooleanFunctionCallExpr);
|
||||
LOG.info("function exp:" + filter);
|
||||
|
||||
// non existent function
|
||||
try {
|
||||
parse("DoesNotExist('sales.*', group)");
|
||||
fail("expect ex on non existent function");
|
||||
} catch (InvalidSelectorException expected) {}
|
||||
|
||||
}
|
||||
|
||||
public void testParseXPath() throws Exception {
|
||||
BooleanExpression filter = parse("XPATH '//title[@lang=''eng'']'");
|
||||
assertTrue("Created XPath expression", filter instanceof XPathExpression);
|
||||
|
|
|
@ -329,6 +329,24 @@ public class SelectorTest extends TestCase {
|
|||
assertInvalidSelector(message, "=TEST 'test'");
|
||||
}
|
||||
|
||||
public void testFunctionSelector() throws Exception {
|
||||
Message message = createMessage();
|
||||
assertSelector(message, "REGEX('1870414179', SessionserverId)", false);
|
||||
message.setLongProperty("SessionserverId", 1870414179);
|
||||
assertSelector(message, "REGEX('1870414179', SessionserverId)", true);
|
||||
assertSelector(message, "REGEX('[0-9]*', SessionserverId)", true);
|
||||
assertSelector(message, "REGEX('^[1-8]*$', SessionserverId)", false);
|
||||
assertSelector(message, "REGEX('^[1-8]*$', SessionserverId)", false);
|
||||
|
||||
assertSelector(message, "INLIST(SPLIT('Tom,Dick,George',','), name)", false);
|
||||
assertSelector(message, "INLIST(SPLIT('Tom,James,George',','), name)", true);
|
||||
|
||||
assertSelector(message, "INLIST(MAKELIST('Tom','Dick','George'), name)", false);
|
||||
assertSelector(message, "INLIST(MAKELIST('Tom','James','George'), name)", true);
|
||||
|
||||
assertSelector(message, "REGEX('connection1111', REPLACE(JMSMessageID,':',''))", true);
|
||||
}
|
||||
|
||||
protected Message createMessage() throws JMSException {
|
||||
Message message = createMessage("FOO.BAR");
|
||||
message.setJMSType("selector-test");
|
||||
|
|
Loading…
Reference in New Issue