content: /**
   * 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 jakarta.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);
          }
      }
  }
  