IKExpression简易表达式解析器详细讲解

1. IK表达式介绍(IK Expression Introduction)
IK Expression是一个开源的(OpenSource),可扩展的(Extensible),基于java语言开发的一个超轻量级(Super lightweight)的公式化语言解析执行工具包。
IK ExpressionV2.0不依赖于任何第三方的java库。它做为一个简单的jar,可以集成于任意的Java应用中。这包括了JavaEE应用(基于应用服务器的), Java桌面应用以及Java WebStart方式的应用。
IK Expression最初诞生的原因是为了能增强工作流引擎,如jBPM等对流程配置的灵活度。使其能在流程运行期获得同配置期一样灵活地对执行逻辑条件进行变更。经过扩展后的IK Expression还可以适用于各种常规业务系统的动态条件配置,如需要图形化配置应用的场合,或是模拟Excel电子表格的公式运算的场景。

同EL和BeanScript不同,IK Expression的设计目标是面向最终用户的,因此它被设计成语法简单(像数学算式),通俗易懂(支持中文变量及函数名)但功能有限的解析引擎。如果你需要一个功能强大的表达式引擎,也许IK Expression并不是最好的选择。

1.1 概要(OverView)

IK Expression是一个采用逆波兰式算法结合指针栈优化的公式解析引擎,它由表达式编译、、表达式执行、变量容器、以及函数配置管理四部分构成。它具有以下特点:

  • 支持基础运算符+ - × / % 逻辑运算符! && || 三元运算符?:以及特有的#集合运算。支持括号优先级,对&&,||,?:有短路优化处理。
  • 支持函数执行,函数扩展,支持变量定义。
  • 完整Jar包大小90K,API简单易学,超轻量级,无第三方类库依赖。

2. 快速入门(Quick Start)

2.1 下载IKExpression2.1.2.jar

2.2 创建2.1.2版的xml:名称为:IKExpression.cfg.xml(注意:版本不一样xml命名不一样)

<?xml version="1.0" encoding="UTF-8"?>

<function-configuration>
    <!-- 系统函数默认配置 -->
    <bean class="org.wltea.expression.function.SystemFunctions">
        <function name="CONTAINS" method="contains">
            <parameter-type>java.lang.String</parameter-type>
            <parameter-type>java.lang.String</parameter-type>
        </function>

        <function name="STARTSWITH" method="startsWith">
            <parameter-type>java.lang.String</parameter-type>
            <parameter-type>java.lang.String</parameter-type>
        </function>

        <function name="ENDSWITH" method="endsWith">
            <parameter-type>java.lang.String</parameter-type>
            <parameter-type>java.lang.String</parameter-type>
        </function>

        <function name="CALCDATE" method="calcDate">
            <parameter-type>java.util.Date</parameter-type>
            <parameter-type>int</parameter-type>
            <parameter-type>int</parameter-type>
            <parameter-type>int</parameter-type>
            <parameter-type>int</parameter-type>
            <parameter-type>int</parameter-type>
            <parameter-type>int</parameter-type>
        </function>

        <function name="SYSDATE" method="sysDate" />
        <function name="DAYEQUALS" method="dayEquals">
            <parameter-type>java.util.Date</parameter-type>
            <parameter-type>java.util.Date</parameter-type>
        </function>
    </bean>

    <!-- 自定义函数配置-->
<!--    <bean class="com.cn.data.util.Functions">
        <function name="TestDemo" method="test">
            <parameter-type>java.lang.String</parameter-type>
        </function>
    </bean>-->


</function-configuration>

2.3 API简易操作

java代码

/** 
 * Hello World Example 
 * @param args 
 */  
public static void main(String[] args){  

    arg= "IK Expression";  
   
    //定义表达式  
    String expression = "\"Hello World \" + 用户名";  
    //给表达式中的变量 "用户名" 付上下文的值  
    List<Variable> variables = new ArrayList<Variable>();  
    variables.add(Variable.createVariable("用户名", arg));  
    //执行表达式  
    Object result = ExpressionEvaluator.evaluate(expression, variables);  
    System.out.println("Result = " + result);         
}   

执行结果: Hello World IK Expression

API说明
*类org.wltea.expression.ExpressionEvaluator
方法1:
public static Object evaluate(String expression, Collection<Variable> variables)
说明:传入表达式和表达式上下文的变量,执行表达式返回结果
参数1 :String expression, 要传入执行的表达式
参数2 :Collection<Variable> variables 表达式上下文的变量集合(详细请看类org.wltea.expression.datameta.Variable的说明)。
返回值:表达式执行结果,可能是以下类型的java对象中的一种:
Int、Long、Float、Double、Boolean、String、Date、List、Object。


方法2:
public static Object evaluate(String expression)
说明:对方法1的重载,执行简单的没有变量的表达式。请参考方法1说明.

*类org.wltea.expression.datameta.Variable
       该类是用来表示表达式的上下文变量的,上面的例子中用到了别名为“用户名”的上下文变量,这是也是表达式最有用的地方。例如,在jBPM的流程定义中,我们需要定义一个报销审批流程中,用来决定流程分支走向的表达式:   

Java代码 

 收藏代码

  1. (申请金额 > 10000)?“总经理审批”:“部门经理审批”  


这里需要定义一个别名为“申请金额“变量。变量通过evaluate(String expression, Collection<Variable> variables)方法中的variables参数传入表达式中。而Variable类型变量的构造十分的简单,它是标准的POJO。


方法1:
public static Variable createVariable(String varName , Object varValue)
说明:根据参数别名和参数值,构造 Variable 实例
参数1 :String varName, 参数的别名,可以是中文别名
参数2 :Object varValue,参数的值 , 可以是下类型的java对象中的一种:
Int、Long、Float、Double、Boolean、String、Date、List、Object。
返回值:org.wltea.expression.datameta.Variable类的实例

方法2:(直接使用构造函数)
public Variable(String varName , DataType varDataType , Object varValue)
说明:根据指定的参数类型、参数别名和参数值,构造 Variable 实例
参数1 :String varName, 参数的别名,可以是中文别名
参数2 :DataType varDataType, 变量类型,它是
org.wltea.expression.datameta. BaseDataMeta.DataType枚举类型,包括的枚举值有:

  • //NULL类型 DATATYPE_NULL ,
  • //字符窜 DATATYPE_STRING ,
  • //布尔类 DATATYPE_BOOLEAN ,
  • //整型数 DATATYPE_INT ,
  • //长整型数 DATATYPE_LONG ,
  • //浮点数 DATATYPE_FLOAT ,
  • //双精度浮点 DATATYPE_DOUBLE ,
  • //日期时间 DATATYPE_DATE ,
  • //集合对象 DATATYPE_LIST,
  • //通用对象类型         DATATYPE_OBJECT,


参数3 :Object varValue,参数的值 , 可以是下类型的java对象中的一种:
Int、Long、Float、Double、Boolean、String、Date、List、Object。
返回值:org.wltea.expression.datameta.Variable类的实例



3. 表达式公式规范(Expression Formula Specification)
3.1 数据类型(Types, Values, and Variables)
a) 数字型 :
i. 整形 integer   : -2321 , 34234
ii. 长整型 long    :3245235235L
iii. 单精度浮点 float : 342.555F
iv. 双精度浮点 double: 234234.3423
b) 字符型:“a-zA-Z012456789”
c) 布尔型:true、false
d) 日期时间型:[2008-08-08] 或 [2009-01-01 12:33:14]
e) 扩展类型:List对象集合 (该类型不支持表达式字面定义,由操作符或函数运算结果生成)
f) 通用对象:Object类型 (该类型不支持表达式字面定义,由操作符或函数运算结果生成)

3.2 运算符(Operators)

 

运算符简易API:

    public static void main(String[] args) {
        Map<String,String> map = new HashMap<String,String>();
        map.put("arg","IK Expression");
        //定义表达式
        String expression = "arg==\"IK Expression\"";
        //给表达式中的变量 "用户名" 付上下文的值
        List<Variable> variables = new ArrayList<Variable>();
        for (Map.Entry<String,String> entrys:map.entrySet()) {
            variables.add(Variable.createVariable(entrys.getKey(), entrys.getValue()));
        }

        //执行表达式
        Object result = ExpressionEvaluator.evaluate(expression, variables);
        System.out.println("Result = " + result);
    }

执行结果:Result = true

3.3 分割符(Separators)

  • a) 括号 "("  ")" —— 标识优先级
  • b) 逗号   "," —— 分隔函数的参数
  • c) 方括号 "["  "]" —— 标识日期型常量
  • d) 双引号 """ —— 标识字符型常量
  • e) 美元号   "$" —— 函数标识前缀
  • f) 转义符   "\" —— 字符串转义,支持\\ , \”, \r , \n , \t



3.4 内部函数(Inner Functions)
内置函数是目前解析器已经实现的一些非常简单、实用的函数



3.5 语法约束(Lexical Structure)

  • 变量命名遵循java变量命名规范(如,不能以数字打头,不能用系统操作符打头等)。
  • 函数声明以“$”符号打头,自定义函数命名遵循java方法命名规范。
  • 日期型常量使用“[]”符号界定,格式为 [yyyy-MM-dd 24h-mm-ss],不支持毫秒。
  • 用户自定义函数别名不能重复。(详细请参阅本文4.1章节)
  • 用户自定义函数的参数和返回值类型限定于3.1章节描述的数据类型。(详细请参阅本文4.1章节)



3.6 公式样例(Formula Example)
1. +、- 、* 、/ 、%(取模) 常规的算术运算,支持括号优先级 :
如:常见的OA中用于年休假工资计算公式
3000 / 21.5 *(12 - 转正月份)/ 2 
其中,“转正月份”可以是上下文变量

2. 不同数据类型的字符串连接 :
“ABC”+(123+10) 运算结果 “ABC133”
“ABC”+ 123 + 10 运算结果“ABC12310”
“[2009-08-08] + false + 123 + \"a String\" + null”
运算结果 “2009-08-08 00:00:00false123a String” (PS:忽略 null型变量)

3. > >= < <= == !=逻辑比较运算,返回布尔值 :

  • 3-1.数值大小比较 : 1234>223 运算结果 true
  • 3-2.字符大小比较 : “1234”>“223” 运算结果 fasle
  • 3-3.日期大小比较 : [2008-12-23] >= [2008-08-08] —— true
  • 3-4.同null的 == 与 != 比较 : 申请人!=null  (其中,“申请人”为执行上下文的变量)



4. 逻辑与、逻辑或、逻辑非运算:
true  &&  $DAYEQUALS([2008-01-01] , [2008-11-01]) ——false
true  ||  $DAYEQUALS([2008-01-01] , [2008-11-01]) —— true
true  && !$DAYEQUALS([2008-01-01] , [2008-11-01]) —— true

5. 结果连接运算“#”:
1000/10  #  [2008-12-23]>$SYSDATE()  # “ABC”+123
运算结果 包含 100 , false , “ABC123”三种不同类型对象的List

6. 函数与操作符混合、嵌套调用,如:

Java代码 

 收藏代码

  1. $DAYEQUALS(  
  2.     $CALCDATE(  
  3.         $SYSDATE() , 0 , 0 ,   
  4.         (8+11-5*(6/3)) * (2- 59 % 7) ,  
  5.         0 ,0,0 ),  
  6.     [2009-10-01]  
  7. )    


运算结果 false

4. 高级特性(Advance)
4.1 函数定制(Functions Customize)
      IK-Expression最吸引人的特性莫过于它允许你以非常简单的方式扩展你的自定义函数。IK-Expression带有一个xml配置文件functionConfig.xml。在使用IK-Expression时,该配置文件应放置于class的根目录中(如同spring和hibernate等的配置文件一样)。配置文件内部格式如下:
functionConfig.xml

Xml代码 

 收藏代码

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <function-configuration>  
  3.     <!-- 系统函数默认配置 -->  
  4.     <bean class="org.wltea.expression.function.SystemFunctions">  
  5.         <function name="CONTAINS" method="contains">  
  6.             <parameter-type>java.lang.String</parameter-type>  
  7.             <parameter-type>java.lang.String</parameter-type>  
  8.         </function>  
  9.         <function name="STARTSWITH" method="startsWith">  
  10.             <parameter-type>java.lang.String</parameter-type>  
  11.             <parameter-type>java.lang.String</parameter-type>  
  12.         </function>  
  13.         <function name="ENDSWITH" method="endsWith">  
  14.             <parameter-type>java.lang.String</parameter-type>  
  15.             <parameter-type>java.lang.String</parameter-type>  
  16.         </function>  
  17.         <function name="CALCDATE" method="calcDate">  
  18.             <parameter-type>java.util.Date</parameter-type>  
  19.             <parameter-type>int</parameter-type>  
  20.             <parameter-type>int</parameter-type>  
  21.             <parameter-type>int</parameter-type>  
  22.             <parameter-type>int</parameter-type>  
  23.             <parameter-type>int</parameter-type>  
  24.             <parameter-type>int</parameter-type>  
  25.         </function>  
  26.         <function name="SYSDATE" method="sysDate" />  
  27.         <function name="DAYEQUALS" method="dayEquals">  
  28.             <parameter-type>java.util.Date</parameter-type>  
  29.             <parameter-type>java.util.Date</parameter-type>  
  30.         </function>  
  31.     </bean>  
  32.       
  33.     <!-- 用户函数配置  ,请在这里定制您自己的函数-->  
  34.   
  35. </function-configuration>  


配置文件中默认配置了系统内部函数定义(在没有绝对必要的原因下,不建议修改系统默认函数配置)。在默认配置的下方,用户可以定义自己的函数,格式如下:
用户自定义函数配置

Xml代码 

 收藏代码

  1. <bean class="org.wltea.expression.test.TestFunctions">  
  2. <constructor-args>  
  3.     <constructor-arg type="java.lang.Integer">123</constructor-arg>  
  4.     <constructor-arg type="java.lang.String">aa</constructor-arg>  
  5. </constructor-args>  
  6. <function name="问好" method="sayHello">  
  7.     <parameter-type>java.lang.String</parameter-type>  
  8. </function>  
  9. </bean>  



这里自定义了一个名称为“问好”的函数,它有一个String类型的参数。该函数映射对应于org.wltea.expression.test.TestFunctions类的sayHello方法,而类org.wltea.expression.test.TestFunctions具有一个构造函数,构造函数带有Integer型和String型的参数。配置中给出了构造函数的初始化参数“123”和“aa”。通过上述定义,用户就可以在表达式中使用该函数,如:
   $问好(当前用户) , 其中“当前用户”为表达式的上下文变量。
上述例子直观的展示了用户函数自定义的过程。下面,我们将系统的了解一下IK-Expression的函数扩展定义规则和约束:

1. 在IK-Expression中,函数直接对应于java的一个类的一个明确的方法。如:$CONTAINS对应org.wltea.expression.function.SystemFunctions类的contains方法;

2. 所有的函数定义前,必须先定义对应的java类。如果该类使用带参数的构造函数,则必须提供明确的构造参数,如:

Xml代码 

 收藏代码

  1. <bean class="org.wltea.expression.test.TestFunctions">  
  2.     <constructor-args>  
  3.         <constructor-arg type="java.lang.Integer">123</constructor-arg>  
  4.         <constructor-arg type="java.lang.String">aa</constructor-arg>  
  5.     </constructor-args>  
  6.     <function name="问好" method="sayHello">  
  7.      <parameter-type>java.lang.String</parameter-type>  
  8.     </function>  
  9. </bean>  



3. java类的加载和实例化在初始化阶段一次性完成。目前IK-Expression仅支持单例形式的加载,即对一个java类仅实例化一次。

4. 定义函数时,必须明确定义函数对应java方法,以及java方法的参数类型和顺序,如:

Xml代码 

 收藏代码

  1. <function name="CONTAINS" method="contains">  
  2.     <parameter-type>java.lang.String</parameter-type>  
  3.     <parameter-type>java.lang.String</parameter-type>  
  4. </function>  


你可以使用不同的函数名(英文的和中文的),对应相同的java方法,但对java中的方法重载必须使用不同的函数名对应(即,IK-Expression不支持函数名重载)
5. 函数的参数和返回值只能是IK-Expression支持的数据类型(请参考3.1数据类型 章节).
6. 为了增加灵活性,IK-Expression给出了一个通过编码方式添加自定义函数的static方法。

函数扩展API说明
*类org.wltea.expression.function.FunctionLoader
方法1:
public static void addFunction(String functionName, Object instance, Method method)
参数1 :String functionName, 要定义的函数名称(中英文皆可)。
参数2 :Object instance 函数要映射的java 类的实例。
参数3 :Method method 函数要映射的java 类的方法对象。

感谢博主:https://www.iteye.com/blog/linliangyi2007-337069

 

 

 

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任错错

如果对您有帮助我很开心

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值