这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

表达式自定义函数自定义语法校验规则

介绍如何自定义表达式后端函数的语法校验规则。

注:需要低代码平台版本2.8.0以上或表达式组件版本2.8.0以上支持

在表达式组件自定义函数时,某些场景下我们需要对函数定义特定的语法规则,比如,虽然函数的入参都是Long, 但是在V8平台中关联实体、枚举的实际类型都是Long ,如果我们要求入参只能是关联实体的Long , 那么就需要自定义语法规则限制参数的类型。

再比如,我们定义的函数的返回值类型可能是不固定的,比如绝对值函数,如果入参是整数,那么返回结果就是整数,如果入参是小数,返回结果则是小数,此时我们的静态函数的入参和返回值可能都是Object类型。自定义语法校验不仅可以校验参数的类型,还可以定义返回结果的类型。

1.代码示例

自定义校验示例代码:

@Slf4j
@FunctionSupporter(functions = {FunctionConstant.ABS_FUNCTION })
public class AbsFunctionSemanticAnalyzer implements FunctionSemanticAnalyzer {

    @Override
    public ExpressionDataType analyze(ASTNode astNode, SemanticAnalyzer semanticAnalyzer) {
        ASTNode paramNode = astNode.getChild(0);
        ExpressionDataType paramExpressionDataType = semanticAnalyzer.analyze(paramNode);
        DataType originDataType = paramExpressionDataType.getOriginDataType();

        if(DataType.INTEGER != originDataType && DataType.BIGINTEGER != originDataType &&
           DataType.DECIMAL != originDataType ){
            throw BusinessException.message("绝对值函数参数必须为数字类型!");
        }
        //注意:这里返回当前AST节点预期返回的类型,不是入参的类型
        return paramExpressionDataType;
    }


}

绝对值函数的自定义函数代码如下(自定义函数的规范参考《表达式组件自定义函数手册》 ):

@Function( category = FormulaFunctionCategoryEnum.Constants.MATH, displayName = ABS_DISPLAYNAME ,
            description = ABS_DESCRIPTION ,
            sort = 1 )
    @Parameters( value = {@Parameter(displayName = ABS_PARAM0_DISPLAYNAME, description = ABS_PARAM0_DESCRIPTION)})
    @ReturnValue( dataType = FormulaDataTypeEnum.Constants.DECIMAL, description = ABS_RESULT_DESCRIPTION )
    public static Object abs(Object number) {
        return number;
    }

2.定义规范

上述代码定义了绝对值函数abs(xxx) 的语法校验规则, 首页我们看到自定义函数abs的入参和返回值都是Object类型,这是为了同时兼容整数、长整数、小数这几种类型。在自定义语法校验逻辑中,我们定义一个类并实现FunctionSemanticAnalyzer接口 , 并在该类上打上**@FunctionSupporter注解, 其属性functions**表示该语法校验规则支持的函数,该属性为字符串数组 ,您可以将多个参数和返回值相似的函数定义为同一个语法校验规则,functions的值为自定义函数的方法名,比如这里的:abs 。

除了实现FunctionSemanticAnalyzer接口外,还需要将该类的全包名注册到项目工程的

META-INF/spring.factories 文件中, 这是因为自定义函数校验规则使用了spring-boot的SPI机制。如下所示:

com.seeyon.boot.starter.formula.parser.semantic.function.FunctionSemanticAnalyzer=\
com.seeyon.boot.starter.formula.parser.semantic.function.AbsFunctionSemanticAnalyzer

3.校验实现

我们可以看到FunctionSemanticAnalyzer接口有两个参数 , astNode 和 semanticAnalyzer 。

astNode: 表达式当前自定义函数的抽象语法树节点对象,您可以通过astNode.getLexeme().getValue()和astNode.getLexeme().getDesc() 获取到函数名称(abs)和函数的显示名称(取绝对值) 。

astNode.getChild(index)可以获取到函数对应下标的参数节点, 比如这里的abs函数只有一个参数,所以可以通过 astNode.getChild(0)的方式获取到参数节点,其也为一个ASTNode对象。

semanticAnalyzer: 如果需要分析参数的类型,可以通过semanticAnalyzer.analyze(paramNode)的方式,获取到参数的类型ExpressionDataType , 通过paramExpressionDataType.getOriginDataType()可以获取参数的实际类型。

analyze方法的返回值类型即为该自定义函数的实际返回类型,比如我们已经通过semanticAnalyzer.analyze分析到了abs函数的入参类型,这里绝对值函数的返回类型就应该是入参的类型,所以直接返回paramExpressionDataType。

此外,如果返回值的类型需要自行定义,可以使用ExpressionDataType的构造方法返回一个新的数据类型,示例如下:

@Override
public ExpressionDataType analyze(ASTNode astNode, SemanticAnalyzer semanticAnalyzer) {
    ASTNode paramNode = astNode.getChild(0);
    ExpressionDataType paramNodeDataType = semanticAnalyzer.analyze(paramNode);
    if(paramNodeDataType.isMultiSelect()){
        return ExpressionDataType.ofDataType(DataType.STRING);
    }else{
        return ExpressionDataType.ofDataType(DataType.BIGINTEGER);
    }
}
在上面的示例中,我们判断参数是否是多选(枚举、关联实体)类型,如果是多选我们返回了一个String类型的返回值,如果是单选我们返回了一个Long类型的返回值。