项目中遇到了动态配置条件触发相应事件的需求,需要根据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 );
}
}
}
}

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

(JAVA)String类型的逻辑语句编译的更多相关文章

  1. java string类型的初始化

    以下基本上是java string类型最常用的三种方法 new string()就不介绍了  基本等同于第三种 String a;  申明一个string类型的 a,即没有在申请内存地址,更没有在内存 ...

  2. Java——string类型与date类型之间的转化

    String类型转化为Date类型 方法一 Date date=new Date("2019-01-25"); 方法二 String =(new SimpleDateFormat( ...

  3. JAVA String类型和原型模式

    如上例所述,变量a,b和它们的值10,20都是存在栈里面,声明的所以String类型的引用也都是存在栈里.而字符串abc是存在字符串常量池中,new出来的String对象则是存在堆里. String ...

  4. Java String类型数据的字节长度

    问题描述: 向Oracle数据库中一varchar2(64)类型字段中插入一条String类型数据,程序使用String.length()来进行数据的长度校验,如果数据是纯英文,没有问题,但是如果数据 ...

  5. JAVA String类型的一些小操作

    String类型是否包含某个String类型的函数:源字符串.contains(包含字符串)  返回值为:boolean类型(true或false) String类型把某个字符替换成另一个字符:源字符 ...

  6. java String 类型总结

    java中String是个对象,是引用类型?,基础类型与引用类型的区别是,基础类型只表示简单的字符或数字,引用类型可以是任何复杂的数据结构,基本类型仅表示简单的数据类型,引用类型可以表示复杂的数据类型 ...

  7. 为什么说Java String 类型的值是不可改变的?

    String对象是不可变的,它的内容是不能改变的.下列代码会改变字符串的内容吗? 1 2 String s = "Java"; s = "HTML"; 答案是不 ...

  8. java String类型转 java.sql.time类型

    String[] timePhase = reservationRuleInDTO.getTimePhase().split(",");List<ReservationTim ...

  9. Java String的相关性质分析

    引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...

随机推荐

  1. linux命令学习笔记(41):ps命令

    Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前 那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程 ...

  2. linux下导入导出oracle的dmp文件

    1.导出dmp件 命令:exp QGTG/\"QGTG@orcl\" file=/usr/fuck.dmp exp QGTG/\"QGTG@orcl\" fil ...

  3. 2017-2018-1 20179203 《Linux内核原理与分析》第九周作业

    攥写人:李鹏举 学号:20179203 ( 原创作品转载请注明出处) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/US ...

  4. Metasploit的使用下篇——漏洞攻击

    一.上文总结 上篇当中主要通过开启的端口对目标主机进行攻击,最后成功做到了连接不过却不能查看到telnet的登陆用户名和密码,目前来看有两个原因:第一.我对于靶机telnet的设置有问题,没有设置相应 ...

  5. BestCoder Round #92 比赛记录

    上午考完试后看到了晚上的BestCoder比赛,全机房都来参加 感觉压力好大啊QAQ,要被虐了. 7:00 比赛开始了,迅速点进了T1 大呼这好水啊!告诉了同桌怎么看中文题面 然后就开始码码码,4分1 ...

  6. html之canvas

    canvas代码片段: <canvas id="testCanvas" width="400" height="150" style= ...

  7. 孤独地、凄惨地AK

    一个\(OIer\)要写多少\(for\) 才能被称为一个\(OIer\) 一位巨佬要爆过多少次零 才能在省选逆袭 手指要多少次掠过键盘 才能安心地休息 \(OI\)啊 我的朋友 在风中\(AK\) ...

  8. Sublime Text2中Evernote 插件的使用

    Sublime Text2是个强大的编辑器, 有好多插件供我们使用, 其中有个插件SublimeEvernote, 可以把代码发送到Evernote里. 但是没找见使用说明, 今天看了下Sublime ...

  9. Linux统计文件夹占用空间大小--du命令基本用法

    命令行环境下要知道linux系统里一个文件夹以及其包含的文件实际所占用的空间大小,linux自带的命令 du可以很好地满足需求. 其他的用法我就不一一写出来了,就列本人觉得会用得最多的,直接上: $ ...

  10. 外置式与增量式PID模板程序(51单片机c语言)

    外置式PID模板 #define MuBiaoCS 0 //目标常数 #define CHang_aCS 0 //比例常数 #define CHang_bCS 0 //积分常数 #define CHa ...