Without further ado, let’s get into the code. The code format is wrong, and for some reason, it can only be copied bit by bit.
public List<ActBPMNSequenceFlowConditionExpression> parseConditionExpressionToList(String conditionExpressionStr, Boolean ifJustExtra) { //1. Initialize variables //1.1 The aggregated conditional expression of the current flow direction to be finally returned List<ActBPMNSequenceFlowConditionExpression> conditionExpressions = new ArrayList<>(); //1.2 Level 1 is the highest level, returning the levels of all expressions and their coordinates LinkedHashMap<Integer, List<ActAtomicCoordinate>> sortedMap = this.getBracketPairs(conditionExpressionStr, ActivitiConst.BRACKET_LEFT_CHAR); //1.3 All English column names that need to be converted to Chinese columns Set<String> colEnNames = new HashSet<>(); //2. Start aggregating conditional expressions // The size<=1 of sortedMap indicates that the Boolean expression contains only basic sign-off judgment conditions or no content, and no processing is required. if (sortedMap.size() > 1) { //2.1 First obtain the lowest level, that is, the most detailed Boolean judgment conditions. These conditions are all single conditions. Integer levelCount = sortedMap.size(); List<ActAtomicCoordinate> basicCoordinates = sortedMap.get(levelCount); List<BoolstrToBoolentity> lowerBoolstrToEntityList = new ArrayList<>(); //The position of )’ in the previous bool expression Integer lastY = null; //The position of (’ in the current bool expression Integer thisX; //The position of )’ in the current bool expression Integer thisY; for (ActAtomicCoordinate basicCoordinate : basicCoordinates) { thisX = basicCoordinate.getX().intValue(); thisY = basicCoordinate.getY().intValue(); String basicBoolExpressionStr = conditionExpressionStr.substring(thisX, thisY + 1); ActBPMNSequenceFlowConditionExpression singleBasicBoolExpressionEntity = this.assembleExpressionEntity(basicBoolExpressionStr, colEnNames); lowerBoolstrToEntityList.add(new BoolstrToBoolentity(basicBoolExpressionStr, singleBasicBoolExpressionEntity)); //2.1.1 Set the connector between conditions lastY = this.setConnecor(conditionExpressionStr, singleBasicBoolExpressionEntity, thisX, thisY, lastY); } /**2.2 Start aggregating the upper expression entities from low to high start **/ for (int i = levelCount - 1; i > 1; i--) { lastY = null; List<ActAtomicCoordinate> upperCoordinates = sortedMap.get(i); for (ActAtomicCoordinate upperCoordinate : upperCoordinates) { //2.2.1 Initialize the bool expression Entity of the current segment of the current layer ActBPMNSequenceFlowConditionExpression singleUpperBoolExpressionEntity = new ActBPMNSequenceFlowConditionExpression(); thisX = upperCoordinate.getX().intValue(); thisY = upperCoordinate.getY().intValue(); List<String> removedList = new ArrayList<>(); List<String> addList = new ArrayList<>(); //2.2.2 Obtain the original text of a bool expression string of the current layer based on x and y coordinates String upperBoolExpressionStr = conditionExpressionStr.substring(thisX, thisY + 1); //2.2.3 If a certain string in the lower layer exists in [original part of the string in the current layer], // Then prove that the boolEntity represented by the lower-level string should be added as a sub-condition to the set of sub-conditions of the boolEntity represented by [the original text of this part of the string in the current layer]. // If the number of bracket levels = 2, then counting the outermost curly bracket, the actual extra Boolean expression has only one level, which is a single condition rather than a compound condition. Iterator it = lowerBoolstrToEntityList.iterator(); while (it.hasNext()) { String key = ((BoolstrToBoolentity) it.next()).getBoolExpressionStr(); if (upperBoolExpressionStr.contains(key)) { ActBPMNSequenceFlowConditionExpression lowerBoolEntity = lowerBoolstrToEntityList.stream().filter(l -> l.getBoolExpressionStr().equals(key)).findFirst().get().getBoolExpressionEntity(); if (singleUpperBoolExpressionEntity.getChildren() == null) { List<ActBPMNSequenceFlowConditionExpression> subBoolExpressionEntities = new ArrayList<>(); subBoolExpressionEntities.add(lowerBoolEntity); singleUpperBoolExpressionEntity.setChildren(subBoolExpressionEntities); } else { if (!addList.contains(key)) { singleUpperBoolExpressionEntity.getChildren().add(lowerBoolEntity); } } if (!addList.contains(key)) { addList.add(key); } if (!removedList.contains(key)) { it.remove(); removedList.add(key); } } } //If [some part of the original string text of the current layer] does not contain any lower-level string, then it means that it is not a compound condition, but a single condition if (singleUpperBoolExpressionEntity.getChildren() == null) { singleUpperBoolExpressionEntity = this.assembleExpressionEntity(upperBoolExpressionStr, colEnNames); } lowerBoolstrToEntityList.add(new BoolstrToBoolentity(upperBoolExpressionStr, singleUpperBoolExpressionEntity)); //2.2.4 Set the connector between conditions lastY = this.setConnecor(conditionExpressionStr, singleUpperBoolExpressionEntity, thisX, thisY, lastY); } } /**2.2 starts to aggregate the upper expression entities in order from low to high end **/ //Aggregation ends and all conditional expressions of the current flow direction are returned in aggregated form conditionExpressions.addAll(lowerBoolstrToEntityList.stream().map(l -> l.getBoolExpressionEntity()).collect(Collectors.toList())); } //Only basic judgment conditions if (!ifJustExtra) { String ss[] = conditionExpressionStr.split(ActivitiConst.VAR_AND); String basicAuditConditionStr = ss[ss.length - 1]; ActBPMNSequenceFlowConditionExpression conditionExpression = new ActBPMNSequenceFlowConditionExpression(); conditionExpression.setConnector(ActivitiConst.VAR_AND); conditionExpression.setBasicAuditCondition(basicAuditConditionStr); conditionExpressions.add(conditionExpression); } //3. Process the Chinese name of the field if (CollectionUtils.isNotEmpty(colEnNames)) { List<ActAtomicField> actAtomicFields = actCompanyLevelBusinesstypeProcdefRelationService.getBusinessFieldsByColEnNames(new ArrayList<>(colEnNames)); processColChnNames(conditionExpressions, actAtomicFields); } return conditionExpressions; }
getBracketPairs method:
/** * Convert the Boolean expression in the picture into: * A set of bracket coordinates descending hierarchically */ private LinkedHashMap<Integer, List<ActAtomicCoordinate>> getBracketPairs(String string, Character symbolLeft) { Map<Character, Character> characterHashMap = new HashMap<>(); characterHashMap.put('(', ')'); characterHashMap.put('[', ']'); characterHashMap.put('{', '}'); Character symbolRight = characterHashMap.get(symbolLeft); if (symbolRight == null || symbolRight.toString().length() < 1) { return new LinkedHashMap<>(); } char[] strArray = string.toCharArray(); //Find all left and right () positions ArrayList<Integer> rightIndexes = new ArrayList<>(); ArrayList<Integer> list = new ArrayList<>(); for (int i = 0; i < strArray.length; i + + ) { if (strArray[i] == symbolLeft) { list.add(i); } else if (strArray[i] == symbolRight) { rightIndexes.add(i); list.add(i); } } LinkedList<Integer> leftIndexList = new LinkedList<>(); Map<Integer, Integer> integerMap = new HashMap<>(); Map<Integer, List<ActAtomicCoordinate>> levelToCoordinates = new LinkedHashMap<>(); Integer maxLevel = 0; for (Integer index : list) { //If a is the left bracket coordinate, linkedList adds a boolean contains = rightIndexes.stream().anyMatch(x -> x == index); if (!contains) { leftIndexList.add(index); maxLevel = leftIndexList.size() > maxLevel ? leftIndexList.size() : maxLevel; } else { //Look from left to right and encounter the right bracket coordinates //Find the coordinates of the closest left bracket to it, forming a pair of brackets if (leftIndexList.size() > 0) { Integer leftIndex = leftIndexList.get(leftIndexList.size() - 1); Integer currentLevel = leftIndexList.size(); ActAtomicCoordinate coordinate = new ActAtomicCoordinate(leftIndex.doubleValue(), index.doubleValue()); if (levelToCoordinates.get(currentLevel) == null) { List<ActAtomicCoordinate> coordinates = new ArrayList<>(); coordinates.add(coordinate); levelToCoordinates.put(leftIndexList.size(), coordinates); } else { levelToCoordinates.get(currentLevel).add(coordinate); } integerMap.put(leftIndex, index); //The left bracket coordinates have been matched and are deleted from the left coordinate list to avoid affecting the next right bracket coordinate matching. leftIndexList.removeLast(); } } } return levelToCoordinates.entrySet().stream() .sorted((o1, o2) -> { int c1 = o1.getKey(); int c2 = o2.getKey(); return Integer.compare(c1, c2); }) .collect(LinkedHashMap::new, (map, entry) -> { map.put(entry.getKey(), entry.getValue()); }, LinkedHashMap::putAll); }
ActBPMNSequenceFlowConditionExpression class:
package com.gree.lyentech.dev3.business.entity.activiti.bpmn; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import java.io.Serializable; import java.util.List; /** * <p> * Workflow: Flowchart definition-connection conditions. Only additional conditions are displayed on the front end, and basic user approval conditions are not included. *</p> * * @author yangk * @since 2021-07-16 */ @ApiModel(value = "Flowchart definition - connection conditions. Only additional conditions are displayed for the front end, and basic user sign-off conditions are not included", description = "Flowchart definition - connection conditions. Only additional conditions are displayed for the front end, not included Basic user sign-in conditions") @Data @Builder @AllArgsConstructor @NoArgsConstructor public class ActBPMNSequenceFlowConditionExpression implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "The first line of connector is left blank") private String connector; @ApiModelProperty(value = "field") private String field; @ApiModelProperty(value = "Field Chinese name") private String fieldCh; @ApiModelProperty(value = "relational operator") private String operator; @ApiModelProperty(value = "Operation value") private String value; @ApiModelProperty(value = "actual value") private String varValue; @ApiModelProperty(value = "User's basic signature determination condition string. Mutually exclusive with all other properties", hidden = true) private String basicAuditCondition; @ApiModelProperty(value = "Single segment operation Boolean value", hidden = true) private Boolean result; @ApiModelProperty(value = "Sub-condition of iteration") private List<ActBPMNSequenceFlowConditionExpression> children; }
After returning the condition, determine whether the expression is ultimately true or false based on the form value submitted in varMap.
/** * Iteratively process the final Boolean value and varMap * @param sf * @param conditionExpressions * @param varMap * @param businessType * @param businessValue * @return */ public Boolean processVarmapAndFinalbool(SequenceFlow sf, List<ActBPMNSequenceFlowConditionExpression> conditionExpressions, Map<String, Object> varMap, String businessType, Object businessValue) { for (ActBPMNSequenceFlowConditionExpression conditionExpression : conditionExpressions) { Boolean result = null; String basicAuditCondition = conditionExpression.getBasicAuditCondition(); if (StringUtils.isBlank(basicAuditCondition)) { //1.1 Determine additional conditions //1.1.1 Get the corresponding parameter value from varMap according to the parameter name in the condition String varName = conditionExpression.getField(); if (StringUtils.isNotBlank(varName)) { //Single field judgment Boolean value and varMap settings: if (!varMap.containsKey(varName)) { throw new BusinessException(String.format(ErrMsg.PARAM_BUSINESSENTITYMAPPING_NOTEXISTS, sf.getName(), varName)); } String operator = conditionExpression.getOperator(); //Submit value Object varValue = varMap.get(varName); Double varValueDouble = null; if (varValue.toString().matches(ActivitiConst.IS_NUMBER)) { varValueDouble = new Double(varValue.toString()); } //Expression value String conValue = conditionExpression.getValue(); Double conValueDouble = null; if (conValue.matches(ActivitiConst.IS_NUMBER)) { conValueDouble = new Double(conValue); } conditionExpression.setVarValue(varValue.toString()); switch (operator) { case ActivitiConst.VAR_EQ: result = varValue.toString().equals(conValue); break; case ActivitiConst.VAR_NEQ: result = !(varValue.toString().equals(conValue)); break; case ActivitiConst.VAR_GT: if (varValueDouble != null & amp; & amp; conValueDouble != null) { result = varValueDouble.compareTo(conValueDouble) > 0; } else { result = varValue.toString().compareTo(conValue) > 0; } break; case ActivitiConst.VAR_LT: if (varValueDouble != null & amp; & amp; conValueDouble != null) { result = varValueDouble.compareTo(conValueDouble) < 0; } else { result = varValue.toString().compareTo(conValue) < 0; } break; case ActivitiConst.VAR_EGT: if (varValueDouble != null & amp; & amp; conValueDouble != null) { result = varValueDouble.compareTo(conValueDouble) >= 0; } else { result = varValue.toString().compareTo(conValue) >= 0; } break; case ActivitiConst.VAR_ELT: if (varValueDouble != null & amp; & amp; conValueDouble != null) { result = varValueDouble.compareTo(conValueDouble) <= 0; } else { result = varValue.toString().compareTo(conValue) <= 0; } break; case ActivitiConst.VAR_CONTAINS: result = varValue.toString().contains(conValue); varMap.put(varName + operator + conValue.replaceAll(ActivitiConst.VAR_COMMA, ActivitiConst.VAR_SPLIT_IN_COLLECTION), result.toString()); break; case ActivitiConst.VAR_NOTCONTAINS: result = !(varValue.toString().contains(conValue)); varMap.put(varName + operator + conValue.replaceAll(ActivitiConst.VAR_COMMA, ActivitiConst.VAR_SPLIT_IN_COLLECTION), result.toString()); break; default: result = false; } } else { //Composite condition judgment Boolean value and varMap settings: if (CollectionUtils.isNotEmpty(conditionExpression.getChildren())) { result = processVarmapAndFinalbool(sf, conditionExpression.getChildren(), varMap, businessType, businessValue); } } } else { //1.2 Determine the user’s basic signing conditions //1.2.1 Verify whether the format of the basic approval conditions defined in the flow chart is correct String[] conditionOrPair = basicAuditCondition.split(ActivitiConst.VAR_OR_REGEX); for (String subCondition : conditionOrPair) { String[] conditionPair = subCondition.split("=="); String conditionBusinessType = conditionPair[0]; String conditionValue = conditionPair[1]; if ((StringUtils.isNotEmpty(conditionBusinessType) & amp; & amp; StringUtils.isEmpty(conditionValue)) || (StringUtils.isEmpty(conditionBusinessType) & amp; & amp; StringUtils.isNotEmpty(conditionValue))) { throw new BusinessException(String.format(ErrMsg.SEQUENCEFLOW_CONDITION_ERROR, sf.getName())); } } //1.2.2 Determine whether the basic approval parameters passed by the user can make the connection judgment condition true. result = basicAuditCondition.contains(businessType + "==" + businessValue.toString()); } conditionExpression.setResult(result); } //2. Organize the above results to obtain the final Boolean value StringBuilder finalBoolStr = new StringBuilder(); Boolean finalBool = null; for (ActBPMNSequenceFlowConditionExpression conditionExpression : conditionExpressions) { finalBoolStr.append(conditionExpression.getField()).append(conditionExpression.getOperator()).append(conditionExpression.getValue()). append(", varValue=").append(conditionExpression.getVarValue()).append(", singleBool=").append(conditionExpression.getResult()).append("\ "); Boolean singleBool = conditionExpression.getResult(); String connector = conditionExpression.getConnector(); if (finalBool == null) { finalBool = singleBool; } if (StringUtils.isNotBlank(connector)) { if (connector.equals(ActivitiConst.VAR_AND)) { finalBool = finalBool & amp; & amp; singleBool; } else if (connector.equals(ActivitiConst.VAR_OR)) { finalBool = finalBool || singleBool; } } } log.info("The business flow judgment process is as follows:" + finalBoolStr.toString()); return finalBool; }