001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.filter;
018
019import java.util.HashMap;
020import java.util.List;
021import org.apache.activemq.filter.function.FilterFunction;
022
023/**
024 * Function call expression for use in selector expressions.  Includes an extensible interface to allow custom
025 * functions to be added without changes to the core.
026 * <p/>
027 * Use registerFunction() to register new function implementations for use in selectors.
028 */
029
030public class FunctionCallExpression implements Expression {
031    protected static final HashMap<String, functionRegistration> functionRegistry = new HashMap();
032
033    protected String functionName;
034    protected java.util.ArrayList arguments;
035    protected FilterFunction filterFunc;
036
037    static {
038        // Register the built-in functions.  It would be nice to just have each function class register
039        //  itself, but that only works when the classes are loaded, which may be never.
040
041        org.apache.activemq.filter.function.BuiltinFunctionRegistry.register();
042    }
043
044
045    /**
046     * Register the function with the specified name.
047     *
048     * @param    name - the function name, as used in selector expressions.  Case Sensitive.
049     * @param    impl - class which implements the function interface, including parse-time and evaluation-time
050     * operations.
051     * @return true - if the function is successfully registered; false - if a function with the same name is
052     * already registered.
053     */
054
055    public static boolean registerFunction(String name, FilterFunction impl) {
056        boolean result;
057
058        result = true;
059
060        synchronized (functionRegistry) {
061            if (functionRegistry.containsKey(name))
062                result = false;
063            else
064                functionRegistry.put(name, new functionRegistration(impl));
065        }
066
067        return result;
068    }
069
070    /**
071     * Remove the registration of the function with the specified name.
072     * <p/>
073     * Note that parsed expressions using this function will still access its implementation after this call.
074     *
075     * @param    name - name of the function to remove.
076     */
077
078    public static void deregisterFunction(String name) {
079        synchronized (functionRegistry) {
080            functionRegistry.remove(name);
081        }
082    }
083
084
085    /**
086     * Constructs a function call expression with the named function and argument list.
087     * <p/>
088     * Use createFunctionCall() to create instances.
089     *
090     * @exception invalidFunctionExpressionException - if the function name is not valid.
091     */
092
093    protected FunctionCallExpression(String func_name, List<Expression> args)
094            throws invalidFunctionExpressionException {
095        functionRegistration func_reg;
096
097        synchronized (functionRegistry) {
098            func_reg = functionRegistry.get(func_name);
099        }
100
101        if (func_reg != null) {
102            this.arguments = new java.util.ArrayList();
103            this.arguments.addAll(args);
104            this.functionName = func_name;
105            this.filterFunc = func_reg.getFilterFunction();
106        } else {
107            throw new invalidFunctionExpressionException("invalid function name, \"" + func_name + "\"");
108        }
109    }
110
111
112    /**
113     * Create a function call expression for the named function and argument list, returning a Boolean function
114     * call expression if the function returns a boolean value so that it may be used in boolean contexts.
115     * Used by the parser when a function call is identified.  Note that the function call is created after all
116     * argument expressions so that the function call can properly detect whether it evaluates to a Boolean value.
117     *
118     * @param    func_name - name of the function, as used in selectors.
119     * @param    args - list of argument expressions passed to the function.
120     * @return an instance of a BooleanFunctionCallExpr if the function returns a boolean value in this call,
121     * or a FunctionCallExpression otherwise.
122     * @exception invalidFunctionExpression - if the function name is not valid, or the given argument list is
123     * not valid for the function.
124     */
125
126    public static FunctionCallExpression createFunctionCall(String func_name, List<Expression> args)
127            throws invalidFunctionExpressionException {
128        FunctionCallExpression result;
129
130        //
131        // Create a function call expression by default to use with validating the function call
132        //  expression and checking whether it returns a boolean result.
133        //
134
135        result = new FunctionCallExpression(func_name, args);
136
137
138        //
139        // Check wether the function accepts this expression.  I.E. are the arguments valid?
140        //
141
142        if (result.filterFunc.isValid(result)) {
143            //
144            // If the result of the call is known to alwyas return a boolean value, wrap this
145            //  expression as a valid BooleanExpression so it will be accepted as a boolean result
146            //  by the selector grammar.
147            //
148
149            if (result.filterFunc.returnsBoolean(result))
150                result = new BooleanFunctionCallExpr(func_name, args);
151        } else {
152            //
153            // Function does not like this expression.
154            //
155
156            throw new invalidFunctionExpressionException("invalid call of function " + func_name);
157        }
158
159        return result;
160    }
161
162
163    /**
164     * Retrieve the number of arguments for the function call defined in this expression.
165     *
166     * @return the number of arguments being passed to the function.
167     */
168
169    public int getNumArguments() {
170        return arguments.size();
171    }
172
173
174    /**
175     * Retrieve the argument at the specified index; the first argument is index 0.  Used by implementations of
176     * FilterFunction objects to check arguments and evaluate them, as needed.
177     *
178     * @param    which - number of the argument to retrieve; the first is 0.
179     */
180
181    public Expression getArgument(int which) {
182        return (Expression) arguments.get(which);
183    }
184
185
186    /**
187     * Evaluate the function call expression in the context given.
188     *
189     * @see    Expression#evaluate
190     */
191
192    public Object evaluate(MessageEvaluationContext message_ctx)
193            throws javax.jms.JMSException {
194        return this.filterFunc.evaluate(this, message_ctx);
195    }
196
197
198    /**
199     * Translate the expression back into text in a form similar to the input to the selector parser.
200     */
201
202    @Override
203    public String toString() {
204        StringBuilder result;
205        boolean first_f;
206
207        result = new StringBuilder();
208
209        result.append(functionName);
210        result.append("(");
211        first_f = true;
212
213        for (Object arg : arguments) {
214            if (first_f)
215                first_f = false;
216            else
217                result.append(", ");
218
219            result.append(arg.toString());
220        }
221
222        result.append(")");
223
224        return result.toString();
225    }
226
227
228    ////                         ////
229    ////  FUNCTION REGISTRATION  ////
230    ////                         ////
231
232    /**
233     * Maintain a single function registration.
234     */
235
236    protected static class functionRegistration {
237        protected FilterFunction filterFunction;
238
239        /**
240         * Constructs a function registration for the given function implementation.
241         */
242
243        public functionRegistration(FilterFunction func) {
244            this.filterFunction = func;
245        }
246
247
248        /**
249         * Retrieve the filter function implementation.
250         */
251
252        public FilterFunction getFilterFunction() {
253            return filterFunction;
254        }
255
256
257        /**
258         * Set the filter function implementation for this registration.
259         */
260
261        public void setFilterFunction(FilterFunction func) {
262            filterFunction = func;
263        }
264    }
265
266
267    /**
268     * Exception indicating that an invalid function call expression was created, usually by the selector parser.
269     * Conditions include invalid function names and invalid function arguments.
270     */
271
272    public static class invalidFunctionExpressionException extends java.lang.Exception {
273        public invalidFunctionExpressionException(String msg) {
274            super(msg);
275        }
276
277        public invalidFunctionExpressionException(String msg, java.lang.Throwable cause) {
278            super(msg, cause);
279        }
280    }
281}