以前写过一篇博客,是关于如何解析相似sql之类的解析器实现参考:http://www.javashuo.com/article/p-bqxebcca-nu.htmlhtml
以前的解析器,更多的是是作语言的翻译转换工做,并不涉及具体的数据运算。并且抛弃了许多上下文关联语法处理,因此相对仍是简单的。java
那么,若是咱们想作一下数据运算呢?好比我给你一些值,而后给你一个表达式,你能够给出其运算结果吗?sql
好比,已知表达式为, field1 > 0 and field2 > 0, 而后已知道值 field1 = 1, field2 = 2; 那么,此运算结果必当为true。这很理所固然!apache
但以上,仅为人工处理,本身用大脑作了下运算,获得结果。若是转换为代码,又当如何?mvc
我想,咱们至少要作这么几件事:app
1. 解析出全部字段有field1, field2;
2. 解析出比较运算符 >;
3. 解析出右边具体的比较值;
4. 解析出链接运算符and;
5. 作全部的比较运算;
6. 关联优先级获得最终结果;ide
怎么样?如今还以为很简单吗?若是是,请收下个人膝盖!单元测试
可是,若是真要作这种泛化的场景,那就至关至关复杂了,要知道相似于HIVE之类的重量级产品,语法解析都是其中重要的组成部分。实际上,这可能涉及到至关多的语言规范须要作了。因此,必然超出咱们的简化理解范围。测试
因此,我这里仅挑一个简单场景作解析:即如题所说,case..when..的解析。this
因此,咱们能够范围缩减为,给定表达式:case when field1 > 0 then 'f1' else 'fn' end; 的判断解析。好比给定值 field1=1, 则应获得结果 f1, 若是给定值 field1=0, 则应获得结果 fn.
在划定范围以后,好像更有了目标感了。可是问题真的简单了吗?实际上,仍是有至关多的分支须要处理的,由于case..when..中能够嵌套其余语法。因此,咱们只能尽力而为了。
命题确立以后,咱们能够开始着手如何实现了。如上描述,咱们有两个已知条件:表达式和基础值。
基于上一篇文章的解析,咱们基本能够快速获得全部组成case when 的元素token信息了。这就为咱们省去了很多事。这里,我着重给一个如何获取整个case..when..词句的实现,使其可造成一个独立的词组。
// 将case..when.. 归结为sql类关键词的实现中 public SqlKeywordAstHandler(TokenDescriptor masterToken, Iterator<TokenDescriptor> candidates, TokenTypeEnum tokenType) { super(masterToken, candidates, tokenType); String word = masterToken.getRawWord().toLowerCase(); if("case".equals(word)) { completeCaseWhenTokens(candidates); } } /** * 实例case...when... 词汇列表 * * @param candidates 待用词汇 */ private void completeCaseWhenTokens(Iterator<TokenDescriptor> candidates) { boolean syntaxClosed = false; while (candidates.hasNext()) { TokenDescriptor token = candidates.next(); addExtendToken(token); if("end".equalsIgnoreCase(token.getRawWord())) { syntaxClosed = true; break; } } if(!syntaxClosed) { throw new SyntaxException("语法错误:case..when..未半闭合"); } }
以上,就是获取case..when..词组的方法了,主要就是从case开始,到end结束,中间的全部词根,都被划做其范围。固然,还有一个重要的点,是将数据字段找出来,放到可取到的地方。
有了一个个独立的元素,咱们就能够进行语义分析了。该分析能够放在该解析器中,但也许并不会太通用,因此,此处我将其抽象为一个单独的值运算类。在须要的地方,再实例化该运算类,便可。核心问题如上一节中描述,具体实现代码以下:
import com.my.mvc.app.common.exception.SyntaxException; import com.my.mvc.app.common.helper.parser.SyntaxStatement; import com.my.mvc.app.common.helper.parser.TokenDescriptor; import com.my.mvc.app.common.helper.parser.TokenTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * 功能描述: case..when.. 真实数据运算帮助类 * */ @Slf4j public class CaseWhenElDataCalcHelper { /** * case when 完整语法 */ private SyntaxStatement caseWhenStmt; public CaseWhenElDataCalcHelper(SyntaxStatement caseWhenStmt) { this.caseWhenStmt = caseWhenStmt; } /** * 计算case..when的结果 * * @param suppliers 原始全部值 * @return 最终计算出的值 */ public String calcCaseWhenData(Map<String, String> suppliers) { List<TokenDescriptor> allTokens = caseWhenStmt.getAllTokens(); TokenDescriptor masterToken = allTokens.get(0); if(!"case".equalsIgnoreCase(masterToken.getRawWord())) { throw new SyntaxException("不是case..when..表达式"); } int tokenLen = allTokens.size(); if(tokenLen < 3) { throw new SyntaxException("case..when..表达式语法错误"); } TokenDescriptor closureToken = allTokens.get(tokenLen - 1); if(!"end".equalsIgnoreCase(closureToken.getRawWord())) { throw new SyntaxException("case..when..表达式未闭合"); } // 暂只支持 case when xxx then xxx... end 语法 // 不支持 case field_name when 1 then '1'... end, 即单字段断定不支持 List<TokenDescriptor> whenExpressionCandidates; for (int i = 1; i < tokenLen - 1; i++) { whenExpressionCandidates = new ArrayList<>(); TokenDescriptor currentToken = allTokens.get(i); if("when".equalsIgnoreCase(currentToken.getRawWord())) { // 需走各分支逻辑 while (i + 1 < tokenLen) { TokenDescriptor nextToken = allTokens.get(i + 1); if("then".equalsIgnoreCase(nextToken.getRawWord())) { break; } whenExpressionCandidates.add(nextToken); ++i; } if(judgeWhenExpression(whenExpressionCandidates, suppliers)) { List<TokenDescriptor> resultCandidates = scrapeCaseWhenResultCandidates(allTokens, i + 1); return calcExpressionData(resultCandidates, suppliers); } // 直接进入下一轮迭代,then后面为空迭代 } if("else".equalsIgnoreCase(currentToken.getRawWord())) { List<TokenDescriptor> resultCandidates = scrapeCaseWhenResultCandidates(allTokens, i); return calcExpressionData(resultCandidates, suppliers); } } return null; } /** * 捞出全部的结果运算token列表 * * @param allTokens 全局token表 * @param start 偏移量 * @return 获取到的全部结果运算token */ private List<TokenDescriptor> scrapeCaseWhenResultCandidates(List<TokenDescriptor> allTokens, int start) { List<TokenDescriptor> resultCandidates = new ArrayList<>(); while (start + 1 < allTokens.size()) { TokenDescriptor nextToken = allTokens.get(start + 1); String word = nextToken.getRawWord(); if("when".equalsIgnoreCase(word) || "else".equalsIgnoreCase(word) || "end".equalsIgnoreCase(word)) { break; } resultCandidates.add(nextToken); ++start; } return resultCandidates; } /** * 判断when条件是否成立 * * @param operatorCandidates 可供运算的表达式token列表 * @param suppliers 原始字段取值来源 * @return true:符合该断定,false:断定失败 */ private boolean judgeWhenExpression(List<TokenDescriptor> operatorCandidates, Map<String, String> suppliers) { List<AndOrOperatorSupervisor> supervisors = partitionByPriority(operatorCandidates); boolean prevJudgeSuccess = false; for (AndOrOperatorSupervisor calc1 : supervisors) { Map<String, List<TokenDescriptor>> unitGroup = calc1.getUnitGroupTokens(); String leftValue = calcExpressionData(unitGroup.get("LEFT"), suppliers); String op = unitGroup.get("OP").get(0).getRawWord(); TokenTypeEnum resultType = getPriorDataTypeByTokenList(unitGroup.get("RIGHT")); boolean myJudgeSuccess; if("in".equals(op)) { myJudgeSuccess = checkExistsIn(leftValue, unitGroup.get("RIGHT"), resultType); } else if("notin".equals(op)) { myJudgeSuccess = !checkExistsIn(leftValue, unitGroup.get("RIGHT"), resultType); } else { String rightValue = calcExpressionData(unitGroup.get("RIGHT"), suppliers); myJudgeSuccess = checkCompareTrue(leftValue, op, rightValue, resultType); } TokenDescriptor prevType = calc1.getPrevType(); TokenDescriptor nextType = calc1.getNextType(); // 单条件断定 if(prevType == null && nextType == null) { return myJudgeSuccess; } if(nextType == null) { return myJudgeSuccess; } prevJudgeSuccess = myJudgeSuccess; if("and".equalsIgnoreCase(nextType.getRawWord())) { if(!myJudgeSuccess) { return false; } continue; } if("or".equalsIgnoreCase(nextType.getRawWord())) { if(myJudgeSuccess) { return true; } continue; } log.warn("解析到未知的next链接断定符:{}", nextType); throw new SyntaxException("语法解析错误"); } log.warn("未断定出结果,使用默认返回,请检查"); return false; } /** * 根据值信息推断运算数据类型 * * @param tokenList 结果列表(待运算) * @return 计算的数据类型,数字或字符串 */ private TokenTypeEnum getPriorDataTypeByTokenList(List<TokenDescriptor> tokenList) { for (TokenDescriptor token : tokenList) { if(token.getTokenType() == TokenTypeEnum.WORD_STRING) { return TokenTypeEnum.WORD_STRING; } } return TokenTypeEnum.WORD_NUMBER; } /** * 运算返回具体的 断定值 * * @param resultCandidates 结果表达式token列表 * @param suppliers 原始字段取值来源 * @return true:符合该断定,false:断定失败 */ private String calcExpressionData(List<TokenDescriptor> resultCandidates, Map<String, String> suppliers) { // 暂时假设结果中再也不提供运算处理 TokenDescriptor first = resultCandidates.get(0); if(first.getTokenType() == TokenTypeEnum.WORD_NORMAL) { if("null".equalsIgnoreCase(first.getRawWord())) { return null; } return suppliers.get(first.getRawWord()); } return unwrapStringToken(first.getRawWord()); } /** * 判断给定值是否在列表中 * * @param aValue 要断定的值 * @param itemList 范围表 * @return true:成立, false:不在其中 */ private boolean checkExistsIn(String aValue, List<TokenDescriptor> itemList, TokenTypeEnum valueType) { if(aValue == null) { return false; } BigDecimal aValueNumber = null; for (TokenDescriptor tk1 : itemList) { if(valueType == TokenTypeEnum.WORD_NUMBER) { if(aValueNumber == null) { aValueNumber = new BigDecimal(aValue); } if(aValueNumber.compareTo( new BigDecimal(tk1.getRawWord())) == 0) { return true; } continue; } if(aValue.equals(unwrapStringToken(tk1.getRawWord()))) { return true; } } return false; } /** * 将字符串两边的引号去除,保持字符串属性 * * @param wrappedStr 含引号的字符串,如 'abc',"abc" * @return abc 无引号包裹的字符串 */ private String unwrapStringToken(String wrappedStr) { if(wrappedStr == null || wrappedStr.length() == 0) { return null; } char[] values = wrappedStr.toCharArray(); int i = 0; while (i < values.length - 1 && (values[i] == '"' || values[i] == '\'')) { i++; } int j = values.length - 1; while (j > 0 && (values[j] == '"' || values[j] == '\'')) { j--; } return new String(values, i, j - i + 1); } /** * 比较两个值ab是否基于op成立 * * @param aValue 左值 * @param op 比较运算符 * @param bValue 右值 * @param valueType 值类型, 主要是区分数字与字符 * @return 是否等式成立, true:成立, false:不成立 */ private boolean checkCompareTrue(String aValue, String op, String bValue, TokenTypeEnum valueType) { // 首先进行相生性断定 if("null".equals(bValue)) { bValue = null; } switch(op) { case "=": if(bValue == null) { return aValue == null; } return bValue.equals(aValue); case "!=": case "<>": if(bValue == null) { return aValue != null; } return !bValue.equals(aValue); } if(bValue == null) { log.warn("非null值不能用比较符号运算"); throw new SyntaxException("语法错误"); } // >=,<=,>,< 断定 int compareResult = compareTwoData(aValue, bValue, valueType); switch(op) { case ">": return compareResult > 0; case ">=": return compareResult >= 0; case "<=": return compareResult <= 0; case "<": return compareResult < 0; } throw new SyntaxException("未知的运算符"); } // 比较两个值大小ab private int compareTwoData(String aValue, String bValue, TokenTypeEnum tokenType) { bValue = unwrapStringToken(bValue); if(bValue == null) { // 按任意值大于null 规则处理 return aValue == null ? 0 : 1; } if(tokenType == TokenTypeEnum.WORD_NUMBER) { return new BigDecimal(aValue).compareTo( new BigDecimal(bValue)); } return aValue.compareTo(unwrapStringToken(bValue)); } // 将token从新分组,以即可以作原子运算 private List<AndOrOperatorSupervisor> partitionByPriority(List<TokenDescriptor> tokens) { // 1. 取左等式token列表 // 2. 取等式表达式 // 3. 取右等式token列表 // 4. 构建一个表达式,作最小分组 // 5. 检查是否有下一运算符,若有则一定为and|or|( // 6. 保存上一链接断定符,新开一个分组 // 7. 重复步骤1-6,直到取完全部token // 前置运算符,决定是否要运算本节点,以及结果的合并方式 // 好比 and, 则当前点必须参与运算,若是前节点结果为false,则直接返回false // 不然先计算本节点 TokenDescriptor preType = null; // 当前节点计算完成后,判断下一运算是否有必要触发 // 为and时则当前为true时必须触发,为or时当前为false触发 TokenDescriptor nextType = null; // key 为 left, op, right, 各value为细分tks Map<String, List<TokenDescriptor>> unitGroup = new HashMap<>(); String currentReadPos = "LEFT"; List<TokenDescriptor> smallGroupTokenList = new ArrayList<>(); // 以上为描述单个运算的字符,使用一个list就能够描述无括号的表达式了 List<AndOrOperatorSupervisor> bracketGroup = new ArrayList<>(); AndOrOperatorSupervisor supervisor = new AndOrOperatorSupervisor(null, unitGroup); bracketGroup.add(supervisor); for (int i = 0; i < tokens.size(); i++) { TokenDescriptor token = tokens.get(i); String word = token.getRawWord().toLowerCase(); TokenTypeEnum tokenType = token.getTokenType(); // 忽略分隔符,假设只有一级运算,忽略空格带来的复杂优先级问题 if(tokenType == TokenTypeEnum.CLAUSE_SEPARATOR) { continue; } // 字段直接断定 if(tokenType == TokenTypeEnum.COMPARE_OPERATOR && !",".equals(word)) { unitGroup.put("OP", Collections.singletonList(token)); currentReadPos = "RIGHT"; continue; } // is null, is not null 解析 if("is".equals(word)) { while (i + 1 < tokens.size()) { TokenDescriptor nextToken = tokens.get(i + 1); if("null".equalsIgnoreCase(nextToken.getRawWord())) { TokenDescriptor opToken = new TokenDescriptor("=", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(nextToken); // 跳过1个token i += 1; break; } if("not".equalsIgnoreCase(nextToken.getRawWord())) { if(i + 2 >= tokens.size()) { throw new SyntaxException("语法错误3: is"); } nextToken = tokens.get(i + 2); if(!"null".equalsIgnoreCase(nextToken.getRawWord())) { throw new SyntaxException("语法错误4: is"); } TokenDescriptor opToken = new TokenDescriptor("!=", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(nextToken); // 跳过2个token i += 2; break; } } continue; } // in (x,x,xx) 语法解析 if("in".equals(word)) { TokenDescriptor opToken = new TokenDescriptor("in", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); i = parseInItems(tokens, curTokenList, i); continue; } // not in (x,xxx,xx) 语法解析 if("not".equals(word)) { if(i + 1 > tokens.size()) { throw new SyntaxException("语法错误:not"); } TokenDescriptor nextToken = tokens.get(i + 1); // 暂不支持 not exists 等语法 if(!"in".equalsIgnoreCase(nextToken.getRawWord())) { throw new SyntaxException("不支持的语法:not"); } TokenDescriptor opToken = new TokenDescriptor("notin", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); i = parseInItems(tokens, curTokenList, i + 1); continue; } // 暂只解析一级,无括号状况 if("and".equals(word) || "or".equals(word)) { supervisor.setNextType(token); // 滚动到下一运算分支 unitGroup = new HashMap<>(); supervisor = new AndOrOperatorSupervisor(token, unitGroup); bracketGroup.add(supervisor); currentReadPos = "LEFT"; continue; } List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(token); } return bracketGroup; } /** * 解析in中的全部元素到结果中 * * @param tokens 全部token * @param curTokenList 当前结果表 * @param start in 开始的地方 * @return in 语法结束位置 */ private int parseInItems(List<TokenDescriptor> tokens, List<TokenDescriptor> curTokenList, int start) { while (start + 1 < tokens.size()) { TokenDescriptor nextToken = tokens.get(++start); String nextWord = nextToken.getRawWord(); if("(".equals(nextWord) || ",".equals(nextWord)) { // in 开始 continue; } if(")".equals(nextWord)) { break; } curTokenList.add(nextToken); } return start; } /** * 最小运算单元描述符 */ private class AndOrOperatorSupervisor { // 前置运算符,决定是否要运算本节点,以及结果的合并方式 // 好比 and, 则当前点必须参与运算,若是前节点结果为false,则直接返回false // 不然先计算本节点 TokenDescriptor prevType; // 当前节点计算完成后,判断下一运算是否有必要触发 // 为and时则当前为true时必须触发,为or时当前为false触发 TokenDescriptor nextType; // key 为 left, op, right, 各value为细分tks Map<String, List<TokenDescriptor>> unitGroupTokens; public AndOrOperatorSupervisor(TokenDescriptor prevType, Map<String, List<TokenDescriptor>> unitGroupTokens) { this.prevType = prevType; this.unitGroupTokens = unitGroupTokens; } public void setNextType(TokenDescriptor nextType) { this.nextType = nextType; } public TokenDescriptor getPrevType() { return prevType; } public TokenDescriptor getNextType() { return nextType; } public Map<String, List<TokenDescriptor>> getUnitGroupTokens() { return unitGroupTokens; } @Override public String toString() { return StringUtils.join( unitGroupTokens.get("LEFT").stream() .map(TokenDescriptor::getRawWord) .collect(Collectors.toList()), ' ') + unitGroupTokens.get("OP").get(0).getRawWord() + StringUtils.join( unitGroupTokens.get("RIGHT").stream() .map(TokenDescriptor::getRawWord) .collect(Collectors.toList()), ' ') + ", prev=" + prevType + ", next=" + nextType ; } } }
每使用时,传入case..when..的语句构造出一个新的计算实例,而后调用 calcCaseWhenData(rawData), 带入已知参数信息,便可运算出最终的case..when..值。
为使处理简单起见,这里并无深刻各类逻辑嵌套处理,直接忽略掉括号的处理了。另外,对于数值类的运算也暂时被忽略,如 field1 > 1+1 这种运算,并不会计算出2来。这些东西,须要的同窗,彻底能够稍加完善,便可支持处理这些逻辑。
因 case when 的语法仍是比较清晰的,因此咱们只是作了顺序地读取,断定即得出结果。另外对于 case when 的单值断定并不支持,因此实现并不复杂。但这彻底不影响咱们理解整个语法处理的思想。相信须要的同窗定能有所启发。
以上仅实现代码,须要附加上各类场景测试,才算能够work的东西。主要就是针对种 and/or, in, is null 等的处理。以下:
import com.my.mvc.app.common.helper.CaseWhenElDataCalcHelper; import com.my.mvc.app.common.helper.SimpleSyntaxParser; import com.my.mvc.app.common.helper.parser.ParsedClauseAst; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import java.util.HashMap; import java.util.Map; @Slf4j public class CaseWhenElDataCalcHelperTest { @Test public void testCaseWhenSimple1() { String condition; ParsedClauseAst parsedClause; CaseWhenElDataCalcHelper helper; Map<String, String> rawData; condition = "case \n" + "\twhen (kehu_phone is null or field1 != 'c') then m_phone \n" + "\telse kehu_phone\n" + "end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("kehu_phone", "kehu_phone_v1"); rawData.put("field1", "field1_v"); rawData.put("m_phone", "m_phone_v"); Assert.assertEquals("case..when..中解析字段信息不正确", 3, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..解析结果错误", rawData.get("m_phone"), helper.calcCaseWhenData(rawData)); condition = "case \n" + "\twhen (kehu_phone is null) then m_phone \n" + "\telse kehu_phone\n" + "end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("kehu_phone", "kehu_phone_v1"); rawData.put("field1", "field1_v"); rawData.put("m_phone", "m_phone_v"); Assert.assertEquals("case..when..中解析字段信息不正确", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..解析结果错误", rawData.get("kehu_phone"), helper.calcCaseWhenData(rawData)); rawData.remove("kehu_phone"); Assert.assertEquals("case..when..解析结果错误", rawData.get("m_phone"), helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen is_sx_emp='Y' then 'Y1' \n" + " \twhen is_sx_new_custom!='Y' then 'Y2' \n" + " \twhen is_sx_fort_promot_custom='Y' then 'Y3' \n" + " \twhen promotion_role_chn in ('10','11') and first_tenthousand_dt is not null then 'Y4' \n" + " \telse 'N' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("promotion_role_chn", "10"); rawData.put("first_tenthousand_dt", "10"); Assert.assertEquals("case..when..中解析字段信息不正确", 5, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..in解析结果错误", "Y4", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..else解析结果错误", "N", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); rawData.put("is_sx_emp", "Y"); Assert.assertEquals("case..when..=解析结果错误", "Y1", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "N"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..!=解析结果错误", "Y2", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); // rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..in+and+null解析结果错误", "N", helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen is_sx_emp='Y' then 'Y1' \n" + " \twhen or_emp != null or or_emp2 > 3 then 'Y2_OR' \n" + " \twhen and_emp != null and and_emp2 > 3 or or_tmp3 <= 10 then 'Y3_OR' \n" + " \twhen promotion_role_chn not in ('10','11') and first_tenthousand_dt is not null then 'Y4' \n" + " \twhen promotion_role_chn not in ('10') then 'Y5_NOTIN' \n" + " \telse 'N_ELSE' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正确", 8, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..in解析结果错误", "Y2_OR", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); // rawData.put("or_emp", "Y"); rawData.put("or_emp2", "2"); Assert.assertEquals("case..when..or>2解析结果错误", "Y5_NOTIN", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); // rawData.put("or_emp", "Y"); rawData.put("or_emp2", "2"); rawData.put("promotion_role_chn", "10"); Assert.assertEquals("case..when..notin解析结果错误", "N_ELSE", helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen (is_sx_emp='Y' or a_field=3) then 'Y1' \n" + " \telse 'N_ELSE' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正确", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..()号解析结果错误", "N_ELSE", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "Y"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正确", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..()号解析结果错误2", "Y1", helper.calcCaseWhenData(rawData)); } }
若是有更多场景,咱们只需添加测试,而后完善相应逻辑便可。这里全部的测试,均可以基于sql协议进行,若有空缺则应弥补相应功能,而非要求用户按本身的标准来,毕竟标准是个好东西。
实际上,对表达式计算这东西,咱们也许不必定非要本身去实现。毕竟太费力。有开源产品支持的,好比:aviator: https://www.oschina.net/p/aviator?hmsr=aladdin1e1 https://www.jianshu.com/p/02403dd1f4c4
若是该语法不支持,则能够先转换成支持的语法,再使用其引擎计算便可。
本质上,咱们都是在作翻译工做!