注:需要低代码平台版本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类型的返回值。