05.仿简道云公式函数实战-扩展和自定义操作符和函数

1. 前言

在上一篇文章中我们学习了QLExpress的基础操作符和java对象的操作,通过大量的测试用例,我们学习了QLExpress的基础语法与使用,本篇文章,我们介绍使用QLExpress的进阶内容,主要知识点是扩展操作符和自定义操作符。

2. 扩展操作符

需求:实现一个操作符"加",它的功能具备与操作符"+"的功能一样。你是不是想到要用addOperatorWithAlias方法?

实现如下:

/***
     * 需求:扩展+操作符 使用"加"汉字替代+的功能
     * @throws Exception
     */
    @Test
    public void ext() throws Exception{
        ExpressRunner runner = new ExpressRunner(false, true);
//        String expressStr = "20 + 5";
        String expressStr = "20 加 5";
        runner.addOperatorWithAlias("加","+","取模操作符定义异常!");
        // isTrace
        Object rst = runner.execute(expressStr, null, null, true, true);
        System.out.println(rst);
    }

05.仿简道云公式函数实战-扩展和自定义操作符和函数_第1张图片

QLExpress是如何实现"加"替换"+"的这个功能呢?首先我们进入addOperatorWithAlias方法的源码

public void addOperatorWithAlias(String aliasName, String name, String errorInfo) throws Exception {
    if (!this.operatorMap.containsKey(name)) {
        throw new QLException(name + " 不是系统级别的操作符号,不能设置别名");
    } else {
        OperatorBase originalOperator = this.operatorMap.get(name);
        if (originalOperator == null) {
            throw new QLException(name + " 不能被设置别名");
        }
        OperatorBase destOperator;
        if (originalOperator instanceof CanClone) {
            destOperator = ((CanClone)originalOperator).cloneMe(aliasName, errorInfo);
        } else {
            Class opClass = (Class)originalOperator.getClass();
            Constructor constructor;
            try {
                constructor = opClass.getConstructor(String.class, String.class, String.class);
            } catch (Exception e) {
                throw new QLException(name + " 不能被设置别名:" + e.getMessage());
            }
            if (constructor == null) {
                throw new QLException(name + " 不能被设置别名");
            }
            destOperator = constructor.newInstance(aliasName, name, errorInfo);
        }
        if (this.operatorMap.containsKey(aliasName)) {
            throw new RuntimeException("操作符号:\"" + aliasName + "\" 已经存在");
        }
        this.addOperator(aliasName, destOperator);
    }
}

通过这个逻辑我们可知,首先QLExpress根据"+"符号操作符找到OperatorAdd作为originalOperator,然后通过originalOperator.getClass();获取opClass,根据opClass.getConstructor(String.class, String.class, String.class);找到构造函数,根据constructor.newInstance(aliasName, name, errorInfo);反射生成destOperator,最后调用this.addOperator(aliasName, destOperator)等同于addOperator("加",newOperatorAdd("+"));

3.自定义操作符

尽管QLExpress表达式引擎封装了好多操作符,常规的使用没有问题,但是总归项目或者产品上有个性化的操作符。那么这个时候我们应该怎么使用QLExpress来满足需求,这也是我想强调的一个地方就是QLExpress引擎的扩展性很强,我们可以借助QLExpress引擎扩展实现自定义操作符。

首先,我们还是模拟一个需求,比如我们想实现一个操作符union,该操作符功能为所有以参数形式给出的值拼接结果返回。

/***
 * 自定义操作符 
 * @throws Exception
 */
@Test
public void union() throws Exception{
    ExpressRunner runner = new ExpressRunner(false, true);
    String expressStr = "'a' union 'b' union 3";
    runner.addOperator("union",new OperatorUnion());
    Object rst = runner.execute(expressStr, null, null, true, true);
    System.out.println(rst);
}

05.仿简道云公式函数实战-扩展和自定义操作符和函数_第2张图片

4. 自定义函数

使用QLExpress如何自定义函数以及QLExpress相关API。在这里我首先给一个简单的列子,通过这个例子我们引出知识点。

需求:实现一个sum函数,通过sum函数计算求和。

/***
 * 自定义函数
 * @throws Exception
 */
@Test
public void sum() throws Exception{
    ExpressRunner runner = new ExpressRunner(false, true);
    String expressStr = "sum(1,2,3)";
    runner.addFunction("sum",new OperatorSum("sum"));
    Object rst = runner.execute(expressStr, null, null, true, true);
    System.out.println(rst);
}

runner.addFunction("sum",new OperatorSum("sum"));我们自定义的函数,要添加到ExpressRunner中,这样在使用sum函数的时候,就能执行我们在OperatorSum这个类中的逻辑。

OperatorSum类的代码就是自定义函数的内容

import com.ql.util.express.Operator;
import com.ql.util.express.OperatorOfNumber;
import com.ql.util.express.exception.QLException;

/**
 * 类描述: 类描述: SUM(number1, [number2], …)
           函数使所有以参数形式给出的数字相加并返回和。
 * @author admin
 * @version 1.0.0
 * @date 2023/11/20 15:39
 */
public class OperatorSum extends Operator {
    public OperatorSum(String name) {
        this.name = name;
    }
    @Override
    public Object executeInner(Object[] lists) throws Exception {

        if (lists.length == 0) {
            throw new QLException("操作数异常");
        }
        Object result = 0;
        for (int i = 0; i < lists.length; i++) {
            result = OperatorOfNumber.add(result, lists[i], isPrecise);
        }
        return result;
    }
}

我们在来看QLExpress中addFunction做了什么处理。以下为addFunction方法代码

/**
 * 添加函数定义
 *
 * @param name 函数名称
 * @param op   对应的操作实现类
 */
public void addFunction(String name, OperatorBase op) {
    // 操作符的管理器
    this.operatorManager.addOperator(name, op);
    // 语法定义的管理器
    this.manager.addFunctionName(name);
}

此类位于

语法分析和计算的入口类ExpressRunner中。

咱们暂且先理解到这,就是自定义的函数最后通过addFunction交给了ExpressRunner类中,这样ExpressRunner中调用execute方法就能执行到我们自定义的逻辑。

5.总结

本篇文章主要介绍了使用QLExpress扩展操作符,自定义操作符和自定义函数。从侧面也能反映出来QLExpress的代码扩展性很好。

最近笔者创建了一个圈子纷传

欢迎大家加入一起交流学习。

你可能感兴趣的:(仿简道云公式函数实战,java,算法,数据结构,后端)