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     */
017    package org.apache.activemq.filter;
018    
019    import java.util.HashMap;
020    import java.util.List;
021    import 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    
030    public 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    }