项目中遇到了动态配置条件触发相应事件的需求,需要根据String类型的逻辑语句,以及动态获取的数据,计算数据对应的结果,java实现。解决思路及代码实现如下,有需要的同学请自取。


一、需求提取

  根据需求,抛开业务部分,我们可以将需求简化成以下核心逻辑。输入String类型的逻辑字符串,支持的逻辑符号包括 > ,  < , <= ,>= ,== ,() 。 例如: "(a>1&&b<2)||(c>=3&&d==4)" ,动态解析该字符串,并对输入的任意json类数据做出正确的逻辑判断。如{“b” : 10 , "a" : 9 , "c":"error" }。

二、设计思路

  因为每一个最小的逻辑点。如 “a>1” 都只有两个结果:成功或者失败,并且成功或者失败后,往往需要执行下一个逻辑,所以该逻辑模型可以转换成一个二叉树的结构。据此我们先画出  "(a>1&&b<2)||(c>=3&&d==4)"  的逻辑图

  每一个逻辑根据成功或者失败,指向了另外的逻辑,或者指向了最终结果,这里我们可以把指向的这个操作等价成指针,该指针指向了另外一个逻辑实体,或者指向了最终结果,又因为java中的指针,或者说引用都是需要事先指定数据类型的,如果我们使用逻辑实体和布尔类型的两种数据对象,那我们就只能将引用声明为两种数据对象的统一父类Object。但是因为Object在使用过程中涉及到了类型的判断及转化,很不方便,所以我们直接使用逻辑实体表示 逻辑,用 null表示可以直接返回最终结果。

  除了两个引用以外,该逻辑实体应该还包括三个关键字,三个关键字有数据对应的key值"a" , 数据对应的逻辑符号 “>”,数据对应的阈值"1"。

  据此,我们可以确定该逻辑实体的五个字段 ,建出以下实体

 public class Logic {
//值对应的key值
private String key;
//逻辑符号 包括 > < >= <= ==
private double symbol;
//阈值
private double value;
//成功是返回,为null时表示最终结果为true
private Logic sucLogic;
//失败是返回,为null时表示最终结果为false
private Logic failLogic; //后面会用到的两个方法
//在logic树的每一层失败分支子树上都加一个成功时调用的对象
public void setSucLogicEveryFail(Logic logic){
Logic logic1 = this;
while (true){
logic1.setSucLogic( logic );
if ( logic1.getFailLogic != null ){
logic1 = logic1.getFailLogic();
}else {
return;
}
}
}
//在logic树的每一层成功分支上子树上都加一个失败时调用的对象
public void setFailLogicEverySuc(Logic logic){
Logic logic1 = this;
while (true){
logic1.setFailLogic( logic );
if ( logic1.getSucLogic != null ){
logic1 = logic1.getSucLogic ();
}else {
return;
}
}
}
}

  使用该实体的原因如下:

  1) 可以很清楚的表明逻辑关系

  2) 增加了处理时的开销,减少了使用时的开销,更好的支持大批量的数据判断

三、编码实现

  1. 根据string生成Logic对象的代码

 public class CreateLogic {

     private static String[] symbol = {">=","<=",">","<","=="};
private static String[] backSymbol = {"<=",">=","<",">","=="}; public static void main(String[] args) {
CreateLogic createLogic = new CreateLogic();
Logic logic = createLogic.handleContentLogic("(a>1&&b<2)||(c>=3&&d==4)");
System.out.println( logic);
} private Logic handleContentLogic(String content) {
//1.去除掉首位的无效括号
content = this.removeNoUseContent( content );
//2.将content拆成小的逻辑块。
List<String> blockContents = new ArrayList<>();
int point = 0;
int flag = 0;
for (int i = 0; i < content.length(); i++) {
char c = content.charAt(i);
if( '(' == c){
flag++;
continue;
}else if( ')' == c){
flag--;
if( flag == 0 ){
blockContents.add( content.substring( point , i + 1) );
point = i + 1;
}
}else if( flag == 0 && ('|' == content.charAt(i) || '&' == content.charAt(i)) ){
if( i - point > 1){
blockContents.add( content.substring( point , i ) );
point = i;
}
}else if( i == content.length() - 1){
blockContents.add( content.substring( point , i + 1 ) );
}
}
//3.遍历获取最终逻辑
Logic logic = null;
for (int i = 0; i < blockContents.size(); i++) {
String blockContent = blockContents.get(i);
if( blockContent.startsWith("||(") ){
Logic logic1 = this.handleContentLogic(blockContent.substring(2));
logic.setFailLogicEverySuc(logic1);
}else if( blockContent.startsWith("&&(") ){
Logic logic1 = this.handleContentLogic(blockContent.substring(2));
logic.setSucLogicEveryFail(logic1);
}else if( blockContent.startsWith("&&") ) {
Logic logic1 = this.getLogicBySimpleContent(blockContent.substring(2));
logic1.setSucLogicEveryFail(logic);
logic = logic1;
}else if( blockContent.startsWith("||") ) {
Logic logic1 = this.getLogicBySimpleContent(blockContent.substring(2));
logic1.setFailLogicEverySuc(logic);
logic = logic1;
}else {
logic = this.getLogicBySimpleContent(blockContent);
}
}
return logic;
} /**
* 去除掉首位的无效括号
* @param content
* @return
*/
public String removeNoUseContent( String content ){
List<String> list = new ArrayList<>(Arrays.asList(content.split(""))) ;
//1.首位的小括号为无效的小括号,先去除掉
int flag1 = 0;
int flag2 = 0;
while (true){
if( "(".equals(list.get(0) )){
flag1++;
list.remove(0);
}else {
break;
}
}
if( flag1 > 0 ){
for (int i = 0; i < list.size(); i++) {
if( flag1 == 0 ){
break;
}
if( "(".equals(list.get(i) ) ){
flag2++;
}else if( ")".equals( list.get(i) ) ){
if(flag2 > 0){
flag2--;
continue;
}else {
flag1--;
list.remove(i);
i--;
}
}
}
}
return StringUtils.join(list.toArray());
} /**
* 简单的逻辑文本直接转换成一个逻辑实体
* @param blockContent
* @return
*/
private Logic getLogicBySimpleContent(String blockContent) {
Logic logic = new Logic();
for (int i = 0; i < symbol.length; i++) {
if( blockContent.indexOf( symbol[i] ) != -1 ){
String value1 = blockContent.substring(0 , blockContent.indexOf( symbol[i] ));
String value2 = blockContent.substring( blockContent.indexOf( symbol[i] ) + symbol[i].length());
try {
double b = Double.valueOf(value2);
logic.setKey(value1);
logic.setValue(b);
logic.setSymbol(symbol[i]);
}catch (Exception e){
double b = Double.valueOf(value1);
logic.setKey(value2);
logic.setValue(b);
logic.setSymbol(backSymbol[i]);
}
return logic;
}
}
return logic;
}
}

  2. 根据Logic和json判断最终结果

  public class HandleLogic {
/**
* 根据逻辑树,递归获取最终的逻辑结果s
*/
public boolean handleMessageByLogicCore(Logic logic , JSONObject object ) {
boolean bool = false;
String key = logic.getKey();
if( object.get(key) == null ){
return this.getLogicByResult(logic , bool , object);
}
double value = logic.getValue();
double realValue = object.getDoubleValue( key );
switch ( logic.getSymbol() ){
case ">=":
bool = realValue >= value;
break;
case "<=":
bool = realValue <= value;
break;
case "==":
bool = realValue == value;
break;
case "<":
bool = realValue < value;
break;
case ">":
bool = realValue > value;
break;
}
return this.getLogicByResult(logic , bool , object);
} /**
* 根据逻辑的结果,获取逻辑的成功/失败的子逻辑树,不存在则直接返回成功/失败
* @param logic 当前逻辑树
* @param b 当前逻辑树的执行结果
* @param object 当前逻辑树的处理对象
* @return
*/
private boolean getLogicByResult(Logic logic, boolean b, JSONObject object) {
if( b ){
if( logic.getSucLogic() == null ){
return true;
}else {
return handleMessageByLogicCore( logic.getSucLogic() , object );
}
}else {
if( logic.getFailLogic() == null ){
return false;
}else {
return handleMessageByLogicCore( logic.getFailLogic() , object );
}
}
}
}

  以上就是逻辑语句编译的总结,原创不易,转载请注明出处。

最新文章

  1. 【leetcode】House Robber
  2. 关于linx中man命令内容中第一行数字的含义
  3. 孙鑫MFC学习笔记18:ActiveX
  4. lucene-查询query-&gt;QueryParser
  5. 导航代码position:relative
  6. zxing实现二维码生成和解析
  7. 【转】TableLayout(表格布局)
  8. Java中List、Collections实现梭哈游戏
  9. [platform]新旧内核的device设备注册对比
  10. centos6下安装部署hadoop2.2
  11. Cookie 路径在本机测试及服务器部署,在浏览器处理方式上的不同
  12. oracle数据库一些用户管理语句
  13. Uni-app事件处理
  14. Manacher算法 (马拉车算法)
  15. 京东饭粒捡漏V1.0.8
  16. mysql 多表管理修改
  17. iOS UI基础-10.0 QQ聊天布局之键盘及文本使用
  18. SQL注入漏洞的原理
  19. angularjs学习第二天笔记---过滤器
  20. 输入框提示文字跨浏览器的placeholder-jQuery版

热门文章

  1. php数组转换成js可用的数组的两种方式
  2. SpringBoot_03_依赖本地jar
  3. Convolutional Neural Networks for Visual Recognition 5
  4. 【leetcode刷题笔记】Pascal&#39;s Triangle II
  5. 【遍历二叉树】04二叉树的层次遍历【Binary Tree Level Order Traversal】
  6. [转]sscanf函数具体用法
  7. Agc017_E Jigsaw
  8. BZOJ5314: [Jsoi2018]潜入行动
  9. BZOJ2548:[CTSC2002]灭鼠行动
  10. BZOJ1206:[HNOI2005]虚拟内存