/* * this method is to create virtual nodes where a certain node has 'AND' or 'MANDATORY_AND', and 'OR' children at the same time. * when a virtual node is created, all 'AND' children should be connected to the virtual node as 'AND' children * and the virtual node should be a 'OR' child of the original parent node */ public Dictionary <string, Node> HandlingVirtualNode(List <Dependency> dependencyList) { Dictionary <string, Node> virtualNodeMap = new Dictionary <string, Node>(); nodeSet.GetNodeMap().Values.ToList().ForEach((node) => { virtualNodeMap.Add(node.GetNodeName(), node); List <Dependency> dpList = dependencyList.Where(dp => node.GetNodeName().Equals(dp.GetParentNode().GetNodeName())).ToList(); /* * need to handle Mandatory, optionally, possibly NodeOptions */ int and = 0; int mandatoryAnd = 0; int or = 0; if (dpList.Count != 0) { foreach (Dependency dp in dpList) //can this for each loop be converted to dpList.stream().forEachOrdered() ? { if ((dp.GetDependencyType() & DependencyType.GetAnd()) == DependencyType.GetAnd()) { and++; if (dp.GetDependencyType() == (DependencyType.GetMandatory() | DependencyType.GetAnd())) { mandatoryAnd++; } } else if ((dp.GetDependencyType() & DependencyType.GetOr()) == DependencyType.GetOr()) { or++; } } bool hasAndOr = (and > 0 && or > 0) ? true : false; if (hasAndOr) { string parentNodeOfVirtualNodeName = node.GetNodeName(); Node virtualNode = new ValueConclusionLine("VirtualNode-" + parentNodeOfVirtualNodeName, Tokenizer.GetTokens("VirtualNode-" + parentNodeOfVirtualNodeName)); this.nodeSet.GetNodeIdMap().Add(virtualNode.GetNodeId(), "VirtualNode-" + parentNodeOfVirtualNodeName); virtualNodeMap.Add("VirtualNode-" + parentNodeOfVirtualNodeName, virtualNode); if (mandatoryAnd > 0) { dependencyList.Add(new Dependency(node, virtualNode, (DependencyType.GetMandatory() | DependencyType.GetOr()))); } else { dependencyList.Add(new Dependency(node, virtualNode, DependencyType.GetOr())); } dpList.Where(dp => dp.GetDependencyType() == DependencyType.GetAnd() || dp.GetDependencyType() == (DependencyType.GetMandatory() | DependencyType.GetAnd())) .ToList().ForEach(dp => dp.SetParentNode(virtualNode)); } } }); return(virtualNodeMap); }
public void HandleChild(string parentText, string childText, string firstKeywordsGroup, int lineNumber) { /* * the reason for using '*' at the last group of pattern within comparison is that * the last group contains No, Da, De, Ha, Url, Id. * In order to track more than one character within the square bracket of last group '*'(Matches 0 or more occurrences of the preceding expression) needs to be used. * */ int dependencyType = 0; // is 'ITEM' child line if (Regex.Match(childText, "(ITEM)(.*)").Success) { if (!Regex.Match(parentText, "(.*)(AS LIST)").Success) { HandleWarning(childText); return; } // is an indented item child childText = childText.Remove(childText.IndexOf("ITEM", StringComparison.CurrentCulture), "ITEM".Length).Trim(); MetaType?metaType = null; if (Regex.Match(parentText, "^(INPUT)(.*)").Success) { metaType = MetaType.INPUT; } else if (Regex.Match(parentText, "^(FIXED)(.*)").Success) { metaType = MetaType.FIXED; } HandleListItem(parentText, childText, metaType); } else // is 'A-statement', 'A IS B', 'A <= B', or 'A IS CALC (B * C)' child line { if (Regex.Match(firstKeywordsGroup, "^(AND\\s?)(.*)").Success) { dependencyType = HandleNotKnownManOptPos(firstKeywordsGroup, DependencyType.GetAnd()); // 8-AND | 1-KNOWN? 2-NOT? 64-MANDATORY? 32-OPTIONALLY? 16-POSSIBLY? } else if (Regex.Match(firstKeywordsGroup, "^(OR\\s?)(.*)").Success) { dependencyType = HandleNotKnownManOptPos(firstKeywordsGroup, DependencyType.GetOr()); // 4-OR | 1-KNOWN? 2-NOT? 64-MANDATORY? 32-OPTIONALLY? 16-POSSIBLY? } else if (Regex.Match(firstKeywordsGroup, "^(WANTS)").Success) { dependencyType = DependencyType.GetOr(); // 4-OR } else if (Regex.Match(firstKeywordsGroup, "^(NEEDS)").Success) { dependencyType = DependencyType.GetMandatory() | DependencyType.GetAnd(); // 8-AND | 64-MANDATORY } /* * the keyword of 'AND' or 'OR' should be removed individually. * it should NOT be removed by using firstToken string in Tokens.tokensList.get(0) * because firstToken string may have something else. * (e.g. string: 'AND NOT ALL Males' name should sound Male', then Token string will be 'UMLM', and 'U' contains 'AND NOT ALL'. * so if we used 'firstToken string' to remove 'AND' in this case as 'string.replace(firstTokenString)' * then it will remove 'AND NOT ALL' even we only need to remove 'AND' * */ Node data = null; nodeSet.GetNodeMap().TryGetValue(childText, out data); Tokens tokens = Tokenizer.GetTokens(childText); if (data == null) { Regex[] matchPatterns = { VALUE_MATCHER, COMPARISON_MATCHER, ITERATE_MATCHER, EXPRESSION_CONCLUSION_MATCHER, WARNING_MATCHER }; Node tempNode; List <string> possibleChildNodeKeyList; for (int i = 0; i < matchPatterns.Length; i++) { Regex regex = matchPatterns[i]; Match match = regex.Match(tokens.tokensString); if (match.Success) { switch (i) { case 4: // warningMatcher case HandleWarning(childText); break; case 0: // valueConclusionMatcher case data = new ValueConclusionLine(childText, tokens); tempNode = data; possibleChildNodeKeyList = nodeSet.GetNodeMap().Keys.Where(key => Regex.Match(key, "^(" + tempNode.GetVariableName() + ")(\\s+(IS(?!(\\s+IN\\s+LIST))).*)*$").Success).ToList(); if (possibleChildNodeKeyList.Count != 0) { possibleChildNodeKeyList.ForEach(item => { this.dependencyList.Add(new Dependency(tempNode, nodeSet.GetNodeMap()[item], DependencyType.GetOr())); //Dependency Type :OR }); } if (FactValue.GetValueInString(data.GetFactValue().GetFactValueType(), data.GetFactValue()).Equals("WARNING")) { HandleWarning(parentText); } break; case 1: // comparisonMatcher case data = new ComparisonLine(childText, tokens); FactValueType rhsType = ((ComparisonLine)data).GetRHS().GetFactValueType(); string rhsString = FactValue.GetValueInString(((ComparisonLine)data).GetRHS().GetFactValueType(), ((ComparisonLine)data).GetRHS()); string lhsString = ((ComparisonLine)data).GetLHS(); tempNode = data; possibleChildNodeKeyList = rhsType.Equals(FactValueType.STRING) ? nodeSet.GetNodeMap().Keys.Where(key => Regex.Match(key, "^(" + lhsString + ")(\\s+(IS(?!(\\s+IN\\s+LIST))).*)*$").Success || Regex.Match(key, "^(" + rhsString + ")(\\s+(IS(?!(\\s+IN\\s+LIST))).*)*$").Success).ToList() : nodeSet.GetNodeMap().Keys.Where(key => Regex.Match(key, "^(" + lhsString + ")(\\s+(IS(?!(\\s+IN\\s+LIST))).*)*$").Success).ToList(); if (possibleChildNodeKeyList.Count != 0) { possibleChildNodeKeyList.ForEach(item => { this.dependencyList.Add(new Dependency(tempNode, nodeSet.GetNodeMap()[item], DependencyType.GetOr())); //Dependency Type :OR }); } if (FactValue.GetValueInString(data.GetFactValue().GetFactValueType(), data.GetFactValue()).Equals("WARNING")) { HandleWarning(parentText); } break; case 2: // iteratenMatcher case data = new IterateLine(childText, tokens); if (FactValue.GetValueInString(data.GetFactValue().GetFactValueType(), data.GetFactValue()).Equals("WARNING")) { HandleWarning(parentText); } break; case 3: //exprConclusionMatcher case data = new ExprConclusionLine(childText, tokens); /* * In this case, there is no mechanism to find possible parent nodes. * I have brought 'local variable' concept for this case due to it may massed up with structuring node dependency tree with topological sort * If ExprConclusion node is used as a child, then it means that this node is a local node which has to be strictly bound to its parent node only. */ if (FactValue.GetValueInString(data.GetFactValue().GetFactValueType(), data.GetFactValue()).Equals("WARNING")) { HandleWarning(parentText); } break; } data.SetNodeLine(lineNumber); this.nodeSet.GetNodeMap().Add(data.GetNodeName(), data); this.nodeSet.GetNodeIdMap().Add(data.GetNodeId(), data.GetNodeName()); break; } } } this.dependencyList.Add(new Dependency(this.nodeSet.GetNode(parentText), data, dependencyType)); } }
/* * The idea of this method is to visit a rule that could get a result of parent rule of the rule as quick as it can be * for instance, if a 'OR' child rule is 'TRUE' then the parent rule is 'TRUE', * and if a 'AND' child rule is 'FALSE' then the parent rule is 'FALSE'. * AS result, visit more likely true 'OR' rule or more likely false 'AND' rule to determine a parent rule as fast as we can */ public static List <Node> Visit(Node node, List <Node> sortedList, Dictionary <string, Record> recordMapOfNodes, Dictionary <string, Node> nodeMap, Dictionary <int?, string> nodeIdMap, List <Node> visitedNodeList, int[,] dependencyMatrix) { if (node != null) { sortedList.Add(node); int nodeId = node.GetNodeId(); int orDependencyType = DependencyType.GetOr(); int andDependencyType = DependencyType.GetAnd(); List <int> dependencyMatrixAsList = new List <int>(); Enumerable.Range(0, dependencyMatrix.GetLength(1)).ToList().ForEach((index) => dependencyMatrixAsList.Add(dependencyMatrix[nodeId, index])); List <int> orOutDependency = (System.Collections.Generic.List <int>)Enumerable.Range(0, dependencyMatrixAsList.Count) .Where(index => (dependencyMatrixAsList[index] & orDependencyType) == orDependencyType); List <int> andOutDependency = (System.Collections.Generic.List <int>)Enumerable.Range(0, dependencyMatrixAsList.Count) .Where(index => (dependencyMatrixAsList[index] & andDependencyType) == andDependencyType); if (orOutDependency.Count != 0 || andOutDependency.Count != 0) { List <Node> childRuleList = new List <Node>(); Enumerable.Range(0, dependencyMatrixAsList.Count).ToList() .Where(childIndex => dependencyMatrixAsList[childIndex] != 0) .ToList() .ForEach(item => childRuleList.Add(nodeMap[nodeIdMap[item]])); if (orOutDependency.Count != 0 && andOutDependency.Count == 0) { while (childRuleList.Count != 0) { /* * the reason for selecting an option having more number of 'yes' is as follows * if it is 'OR' rule and it is 'TRUE' then it is the shortest path, and ignore other 'OR' rules * Therefore, looking for more likely 'TRUE' rule would be the shortest one rather than * looking for more likely 'FALSE' rule in terms of processing time */ Node theMostPositive = FindTheMostPositive(childRuleList, recordMapOfNodes, dependencyMatrixAsList); // Node theMostPositive = findTheMostPositive(childRuleList, recordMapOfNodes); if (!visitedNodeList.Contains(theMostPositive)) { visitedNodeList.Add(theMostPositive); sortedList = Visit(theMostPositive, sortedList, recordMapOfNodes, nodeMap, nodeIdMap, visitedNodeList, dependencyMatrix); } } } else { if (orOutDependency.Count == 0 && andOutDependency.Count != 0) { /* * the reason for selecting an option having more number of 'yes' is as follows * if it is 'AND' rule and it is 'FALSE' then it is the shortest path, and ignore other 'AND' rules * Therefore, looking for more likely 'FALSE' rule would be the shortest one rather than * looking for more likely 'TRUE' rule in terms of processing time */ while (childRuleList.Count != 0) { Node theMostNegative = FindTheMostNegative(childRuleList, recordMapOfNodes, dependencyMatrixAsList); // Node theMostNegative = findTheMostNegative(childRuleList, recordMapOfNodes); if (!visitedNodeList.Contains(theMostNegative)) { visitedNodeList.Add(theMostNegative); sortedList = Visit(theMostNegative, sortedList, recordMapOfNodes, nodeMap, nodeIdMap, visitedNodeList, dependencyMatrix); } } } } } } return(sortedList); }