Skip to content

Computing Functions AddOwnFunction ru

anymaker edited this page Apr 4, 2021 · 2 revisions

Add Own Function

Для создания своей собственной функции вы должны:

  1. Создать класс функции.
  2. Реализовать метод функции.
  3. Определить входящие параметры функции.
  4. Зарегистрировать новую функцию в ObjCalcEngine.

Теперь более подробно.

1. Создать класс функции

Класс для функции должен быть унаследован от a2u.tn.utils.computer.calculator.Function.
Это может быть любой класс внутренний или внешний.

2. Реализовать метод функции

В созданном классе необходимо реализовать метод run, который будет выполнять задуманное действие функции.

    /**
     * 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) {
    ...

В параметре calculator придёт текущий калькулятор, который выполняет запрос.
В параметре paramValues придут значения настроенных входящие параметров, приведённые к запрашиваемому типу. В параметре params придут все входящие значения переданные в функцию. В сыром виде как есть. Для получения значений из этих параметров необходимо произвести расчёт.
Про настройку параметров смотрите в п.3
В параметре ctx придёт контекст выполнения.

Если на вход калькулятора поступает объект содержащий список значений, то он начинает применять формулы для каждой строки отдельно.
Узнать весь список значений входящего объекта можно методом ctx.getAllRows(), текущее обрабатываемо значение из списка можно получить методом ctx.getRowData(), а позицию текущего обрабатываемого значения методом ctx.getRowIndex().

3. Определить входящие параметры функции

Определение входящих параметров позволяет калькулятору проверять какие парамеры поступили на вход, конвертировать их в запрашиваемый тип, и выдавать понятные сообщения об ошибках в параметрах.
Если функция принимает параметры, то нужно переопределить метод initParameters.

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

В этом методе вы можете настроить каждый параметр, задать следующие характеристики: type Тип значения параметра. Калькулятор автоматически сконвертирует входящее значение в этот тип.
name Имя параметра. Параметр можно будет получить из мапы params.
required Если параметр обязательный, то true.
defaultValue Значение параметра по умолчанию. Используйте если параметр не обязательный, но должен иметь значение.

    @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;
    }

Если у вас параметр обязательный, то можно применить сокращённую версию конструктора

    @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;
    }

Если функция не принимает параметры, то этот метод можно не переопределять.

Если функция принимает параметры, но вы не хотите чтобы калькулятор о них заботился, то переопределите этот метод и верните пустой массив.

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

4. Зарегистрировать новую функцию

Для того, чтобы функция работала, необходимо сказать калькулятору о наличии новой функции

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

Здесь Concat - это новая функция, а cnct имя новой функции. Имя функции не чувствительно к регистру, поэтому внутри калькулятора они хранятся приведёнными к нижнему регистру.

Если не задать имя новой функции, то будет взято имя класса реализующего эту функцию.
Вы можете создавать синонимы функций, просто указывая разные имена.

Полный пример

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')"));

  }

}

Здесь внутри тестового класса определяется новая функция Concat, которая в методе countTest() регистрируется в калькуляторе.

Clone this wiki locally