Skip to content

Computing Functions AddOwnFunction

anymaker edited this page Apr 4, 2021 · 4 revisions

Add Own Function

To create your own function, you must:

  1. Create function class.
  2. Implement a function method.
  3. Define incoming function parameters.
  4. Register a new function in ObjCalcEngine.

Now in more detail.

1. Create function class

The class for the function must inherit from a2u.tn.utils.computer.calculator.Function.
It can be any class, internal or external.

2. Implement a function method

In the created class, you need to implement the run method. This method will perform the intended action of this function.

    /**
     * Invoke function to execution
     *
     * @param calculator  Calculator for executing or type conversion
     * @param params      Incoming params
     * @param paramValues Prepared values of parameters
     * @param ctx         Data for calculating
     * @return Result of execution function
     */
    @Override
    public Object run(Calculator calculator, List<FormulaPart> params, Map<String, Object> paramValues, CalcContext ctx) {
    ...

The parameter calculator will contain the current calculator that executes this request.
The parameter params will contain all input values passed to the function, in a raw type as it is. You must calculate these parameters to get their values.
The parameter paramValues will contain the values of the configured input parameters, cast to the requested type. For setting parameters, see p. 3.
The parameter ctx will contain the execution context.

If an object containing a list of values arrives at the calculator input, then he starts applying formula for each row separately.
From the execution context ctx, you can find out the entire list of values of the incoming object using the ctx.getAllRows () method, the currently processed row from the list can be obtained using the ctx.getRowData () method, and the index of the currently processed row can be obtained using the ctx.getRowIndex () method.

3. Define incoming function parameters

Determining the input parameters allows the calculator to check which parameters were received at the input, convert them to the requested type, and give clear messages about errors in the call parameters.

If the function takes parameters, then you need to override the initParameters method.

  protected List<Parameter> initParameters() {
  ...

In this method, you can adjust each parameter, set the following characteristics:
type The type of the parameter value. The calculator will automatically convert the input value to this type.
name Parameter name. The parameter can be obtained from the params map.
required If the parameter is required, then true.
defaultValue The default value of the parameter. Use if the parameter is not required, but must have a value.

    @Override
    protected List<Parameter> initParameters() {
      List<Parameter> parameters = new ArrayList<>();
      parameters.add(new Parameter(String.class, "stringA", false, null));
      parameters.add(new Parameter(String.class, "stringB", false, null));
      return parameters;
    }

If your parameter is required, then you can use the shorthand version of the constructor

    @Override
    protected List<Parameter> initParameters() {
      List<Parameter> parameters = new ArrayList<>();
      parameters.add(new Parameter(String.class, "stringA"));
      parameters.add(new Parameter(String.class, "stringB"));
      return parameters;
    }

If the function does not accept parameters, then this method does not need to be overridden.

If the function takes parameters, but you don't want the calculator to take care of them, then override this method and return an empty array.

    @Override
    protected List<Parameter> initParameters() {
      return new ArrayList<>();
    }

4. Register a new function

In order for the function to work, you need to tell the calculator that there is a new function.

ObjCalcEngine engine = new ObjCalcEngine();
engine.addFunction(new Concat(), "cnct");

Here Concat is the new function, and cnct is the name of the new function. The function name is not case-sensitive, so they are stored in lower case inside the calculator.

If you do not specify the name of the new function, then the name of the class that implements this function will be taken.
You can create synonyms for functions simply by specifying different names.

Complete example

package a2u.tn.utils.computer.calcobj.functions;

import a2u.tn.utils.computer.calcobj.ObjCalcEngine;
import a2u.tn.utils.computer.calculator.CalcContext;
import a2u.tn.utils.computer.calculator.Calculator;
import a2u.tn.utils.computer.calculator.Function;
import a2u.tn.utils.computer.formula.FormulaPart;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;

public class OwnFunctionTest {

  private static class Concat extends Function {

    /**
     * Return descriptors for incoming parameters
     *
     * @return descriptors for incoming parameters
     */
    @Override
    protected List<Parameter> initParameters() {
      List<Parameter> parameters = new ArrayList<>();
      parameters.add(new Parameter(String.class, "stringA"));
      parameters.add(new Parameter(String.class, "stringB"));
      return parameters;
    }

    /**
     * Invoke function to execution
     *
     * @param calculator  Calculator for executing or type conversion
     * @param params      Other params
     * @param paramValues Prepared values of parameters
     * @param ctx         Data for calculating
     * @return Result of execution function
     */
    @Override
    public Object run(Calculator calculator, List<FormulaPart> params, Map<String, Object> paramValues, CalcContext ctx) {
      String stringA = (String) paramValues.get("stringA");
      String stringB = (String) paramValues.get("stringB");

      return stringA+stringB;
    }

  }


  @Test
  public void countTest() {
    ObjCalcEngine engine = new ObjCalcEngine();

    Function fn = new Concat();

    engine.addFunction(fn);
    engine.addFunction(fn, "cnct");

    Function fn1 = engine.getFunction(Concat.class.getSimpleName().toLowerCase());
    Function fn2 = engine.getFunction("cnct");
    assertEquals(fn, fn1);
    assertEquals(fn, fn2);

    assertEquals("AB", engine.calc("cnct('A', 'B')"));

  }

}

Here, inside the test class, a new function Concat is defined, which is registered in the calculator in the method countTest() .