Drools 文件语法检查
一, 背景
当前自己开发的 Android 项目是一个智能推荐系统, 用到 drools 规则引擎, 于我来说是一个新知识点, 以前都没听说过的东东, 不过用起来也不算太难, 经过一段时间学习, 基本掌握. 关于 drools 规则引擎的内容, 后面再整理 JBoss 官网上面有详细的文档, 网上资料也比较多. 学习 drools 规则引擎的传送门:
Drools 官网首页: https://www.drools.org/ https://www.drools.org/
Drools 官方文档: https://docs.jboss.org/drools/release/7.12.0.Final/drools-docs/html_single/index.HTML
这里主要是因为自己使用 Android Studio 在编写 drools 文件时, 没有了智能提示, IDE 不对语法进行检查了, 出现了两次多写 ) 的错误. 这就跟用记事本写东西程序一样, 慌的不行, 所以自己写一个简单的语法检查的脚本. 对 drools 文件进行一个初步的判断.
二, Drools 规则引擎简单介绍
Drools 规则引擎的使用场景
对于某些企业级应用, 经常会有大量的, 错综复杂的业务规则配置, 用程序语言来描述, 就形如: if-else 或者 switch-case 等. 像这种不同的条件, 做不同的处理就是一种规则. 用通俗易懂的结构来表示: 当 XXX 的时候, 做 XXX 的事. 理论上这样的问题都可以用规则引擎来解决. 但是我们也不是说为了使用规则引擎去使用它, 我们视具体业务逻辑而定, 一般来说, 条件 (规则) 比较复杂, 情况种类比较多, 条件可能会经常变化等, 这时候, 选择规则引擎去解决问题是比较明智的.
譬如随着企业管理者的决策变化, 某些业务规则也会随之发生更改. 对于我们开发人员来说, 我们不得不一直处理软件中的各种复杂问题, 需要将所有数据进行关联, 还要尽可能快地一次性处理更多的数据, 甚至还需要以快速的方式更新相关机制.
Drools 规则引擎的优点
Drools 规则引擎实现了将业务决策从应用程序中分离出来.
优点:
1, 简化系统架构, 优化应用
2, 方便系统的整合, 它们是独立的, 允许不同背景的人进行合作
3, 减少编写 "硬代码" 业务规则的成本和风险, 每个规则控制所需的最小信息量
4, 它们很容易更新, 提高系统的可维护性, 减小维护成本
Drools 的基本工作工程
我们需要传递进去数据, 用于规则的检查, 调用外部接口, 同时还可能获取规则执行完毕之后得到的结果
Fact 对象:
指传递给 drools 脚本的对象, 是一个普通的 javabean, 原来 javaBean 对象的引用, 可以对该对象进行读写操作, 并调用该对象的方法. 当一个 java bean 插入到 working Memory(内存存储)中, 规则使用的是原有对象的引用, 规则通过对 fact 对象的读写, 实现对应用数据的读写, 对其中的属性, 需要提供 get 和 set 方法, 规则中可以动态的前往 working memory 中插入删除新的 fact 对象
Drl 文件内容:
例子:
hello.drl 文件如下:
- package rules.testword
- rule "test001"
- when
- // 这里如果为空, 则表示 eval(true)
- then
- System.out.println("hello Word");
- end
Drools 的基础语法:
包路径, 引用, 规则体 (其中包路径和规则体是必须的)
package:
包路径, 该路径是逻辑路径 (可以随便写, 但是不能不写, 最好和文件目录同名, 以(.) 的方式隔开), 规则文件中永远是第一行.
rule:
规则体, 以 rule 开头, 以 end 结尾, 每个文件可以包含多个 rule , 规则体分为 3 个部分: LHS,RHS, 属性 三大部分.
LHS:
(Left Hand Side), 条件部分, 在一个规则当中 "when" 和 "then" 中间的部分就是 LHS 部分, 在 LHS 当中, 可以包含 0~N 个条件, 如果 LHS 为空的话, 那么引擎会自动添加一个 eval(true)的条件, 由于该条件总是返回 true, 所以 LHS 为空的规则总是返回 true.
RHS:
(Right Hand Side), 在一个规则中 "then" 后面的部分就是 RHS, 只有在 LHS 的所有条件都满足的情况下, RHS 部分才会执行. RHS 部分是规则真正做事情的部分, 满足条件触发动作的操作部分, 在 RHS 可以使用 LHS 部分当中的定义的绑定变量名, 设置的全局变量, 或者是直接编写的 java 代码, 可以使用 import 的类. 不建议有条件判断.
三, drools 文件的形式
Drools 是一款基于 Java 的开源规则引擎, 所以 Drools 文件语法完全兼容 java 语法, 以 .drl 为后缀. 大致形式如下:
- package droolsexample
- // list any import classes here.
- import com.sample.ItemCity;
- import java.math.BigDecimal;
- // declare any global variables here
- dialect "java"
- /*
- 规则 1
- */
- rule "Pune Medicine Item"
- when
- item : ItemCity (purchaseCity == ItemCity.City.PUNE,
- typeofItem == ItemCity.Type.MEDICINES)
- then
- BigDecimal tax = new BigDecimal(0.0);
- item.setLocalTax(tax.multiply(item.getSellPrice()));
- end
- /**
- 规则 2
- */
- rule "Pune Groceries Item"
- when
- item : ItemCity(purchaseCity == ItemCity.City.PUNE,
- typeofItem == ItemCity.Type.GROCERIES)
- then
- BigDecimal tax = new BigDecimal(2.0);
- item.setLocalTax(tax.multiply(item.getSellPrice()));
- end
和 java 文件类似, 先声明包名, 然后导入相关的类. 一个规则由:
- rule "xxx"
- when
- xxx
- then
- xxx
- end
这样的形式构成. 其中单行注释以 // 开头, 多行注释形如 /* */ .
四, Drools 文件语法初步检查
目的
检测 .drl 文件中 ( ) , { } , " " 是否成对出现. 如果出现错误, 指出错误行数.
思路
利用 栈 的数据结构来进行检测.
首先我们需要给出一个空栈, 然后把待检测的代码中的字符一一入栈, 在入栈的过程中, 如果字符是一个开放符号 a(也就是我们的左括号), 则把它压入栈中, 如果是一个封闭符号(右括号)b, 则此时先判断一下栈是否为空, 如果为空的话, 则报错(也就是待检测的代码中的括号不一一对应), 如果栈不为空, 则比较 b 和栈顶元素 a, 如果该封闭字符 b 和 a 字符匹配(也就是他们的括号能够匹配), 则弹出栈顶元素, 如果不匹配, 则报错. 当吧所有待检测的代码全部迭代完后, 此时如果栈不为空, 则报错.
上面的思路中, 我们还要将注释中的符号排除在外.
步骤
1, 读取 drl 文件内容为字符串.
2, 通过正则匹配, 将 :// 替换为其它不受影响的字符或者字符串 , 避免误判断.
3, 通过正则匹配, 将 // 单行注释替换为空格. 正则表达式为 //.*
4, 通过正则匹配, 将 /* 和 */ 替换为不受影响的字符, 如 #.(纯粹是为了计算出错行数)
5, 借助栈的数据结构, 进行判断.
python 脚本实现
- # coding=utf-8
- import re
- def remove_annotation(file_path):
- f = open(file_path, "r", encoding="UTF-8")
- text = f.read()
- re_uri = "://"
- text1 = re.sub(re_uri, "_____", text)
- re_single = "//.*"
- text2 = re.sub(re_single, " ", text1)
- re_multi1 = "/\*"
- text3 = re.sub(re_multi1, "#", text2)
- re_multi2 = "\*/"
- result = re.sub(re_multi2, "#", text3)
- return result
- def check_syntax(text):
- try:
- clist = []
- row_num = 1
- for c in text:
- if c == '\n':
- row_num = row_num + 1
- if not clist or (clist[-2] != '\"' and clist[-2] != '#'):
- if c == '\"':
- clist.append(c)
- clist.append(row_num)
- if c == '#':
- clist.append(c)
- clist.append(row_num)
- if c == '(':
- clist.append(c)
- clist.append(row_num)
- if c == '{':
- clist.append(c)
- clist.append(row_num)
- if c == ')':
- if clist[-2] == '(':
- clist.pop()
- clist.pop()
- else:
- print("多余的 ) , 可能错误行数为" + str(row_num))
- return -1
- if c == '}':
- if clist[-2] == '{':
- clist.pop()
- clist.pop()
- else:
- print("多余的 } , 可能错误行数为" + str(row_num))
- return -1
- else:
- if c == '\"':
- if clist[-2] == '\"':
- clist.pop()
- clist.pop()
- if c == '#':
- if clist[-2] == '#':
- clist.pop()
- clist.pop()
- if clist:
- print("存在多余的 ( 或者 { 或者 \" 可能的错误行数为:" + str(clist[-1]))
- return -2
- else:
- print("语法检查初步正确!")
- return 0
- except IndexError:
- print("存在多余的 ) 或者 } 或者 \" 可能的错误行数为: " + str(row_num))
- return -1
- except Exception as e:
- print(e)
- print("其它错误!")
- drl_path = input("请输入 drools 文件路径, 可拖拽:")
- content = remove_annotation(drl_path)
- check_syntax(content)
演示实例:
drools 文件就取用上面的例子, 命名为 test.drl , 如图:
上面的脚本保存为 check_drl.py 文件.
结果如下:
下面将故意将 drools 文件第 40 行增加一个 ) , 如下图:
再次测试如下图:
结语
本文简单介绍了一下 Drools 规则引擎的使用场景以及 drool 文件的简单语法检测, 主要是利用了栈数据结构后进先出的思想. 本来是计划用 java 来写这个工具的, 后来想了一下, 还是觉得 python 比较实在, 有很多优势, 列举一二:
python 的列表 list 直接可以代替 java 的 Stack<E> ;
python 结合正则表达式, 处理字符串更方便;
python 获取 list 倒数第二个元素, 可以直接使用 list[-2] ,java 可能需要 stack.get(stack.size()-2) ;
...
来源: https://www.cnblogs.com/joy99/p/9758228.html