public string VoicePhrases(string sep) { List <Tuple <string, ConditionEntry.MatchType> > ret = actionfiles.ReturnValuesOfSpecificConditions("VoiceInput", new List <ConditionEntry.MatchType>() { ConditionEntry.MatchType.MatchSemicolonList, ConditionEntry.MatchType.MatchSemicolon }); // need these to decide string s = ""; foreach (var vp in ret) { BaseUtils.StringCombinations sb = new BaseUtils.StringCombinations(); sb.ParseString(vp.Item1); s += String.Join(",", sb.Permutations.ToArray()) + sep; } return(s); }
public string VoicePhrases(string sep) { var ret = actionfiles.ReturnSpecificConditions(ActionEventEDList.onVoiceInput.TriggerName, "VoiceInput", new List <ConditionEntry.MatchType>() { ConditionEntry.MatchType.MatchSemicolonList, ConditionEntry.MatchType.MatchSemicolon }); // need these to decide string s = ""; foreach (var vp in ret) { BaseUtils.StringCombinations sb = new BaseUtils.StringCombinations(); sb.ParseString(vp.Item2.MatchString); s += String.Join(",", sb.Permutations.ToArray()) + sep; } return(s); }
static public bool?CheckConditions(List <Condition> fel, Variables values, out string errlist, out ErrorClass errclass, List <Condition> passed = null, Functions cf = null) { errlist = null; errclass = ErrorClass.None; bool?outerres = null; foreach (Condition fe in fel) // find all values needed { bool?innerres = null; foreach (ConditionEntry f in fe.fields) { bool matched = false; if (f.matchtype == ConditionEntry.MatchType.AlwaysTrue || f.matchtype == ConditionEntry.MatchType.AlwaysFalse) { if (f.itemname.Length == 0 || f.itemname.Equals("Condition", StringComparison.InvariantCultureIgnoreCase)) // empty (legacy) or { if (f.matchtype == ConditionEntry.MatchType.AlwaysTrue) { matched = true; // matched, else if false, leave as false. } } else { errlist += "AlwaysFalse/True does not have on the left side the word 'Condition'"; errclass = ErrorClass.ExprFormatError; innerres = false; break; } } else { string leftside = null; Functions.ExpandResult er = Functions.ExpandResult.NoExpansion; if (cf != null) // if we have a string expander, try the left side { er = cf.ExpandString(f.itemname, out leftside); if (er == Functions.ExpandResult.Failed) // stop on error { errlist += leftside; // add on errors.. innerres = false; // stop loop, false break; } } if (f.matchtype == ConditionEntry.MatchType.IsPresent) // these use f.itemname without any expansion { if (leftside == null || er == Functions.ExpandResult.NoExpansion) // no expansion, must be a variable name { leftside = values.Qualify(f.itemname); // its a straight variable name, allow any special formatting } if (values.Exists(leftside) && values[leftside] != null) { matched = true; } } else if (f.matchtype == ConditionEntry.MatchType.IsNotPresent) { if (leftside == null || er == Functions.ExpandResult.NoExpansion) // no expansion, must be a variable name { leftside = values.Qualify(f.itemname); // its a straight variable name, allow any special formatting } if (!values.Exists(leftside) || values[leftside] == null) { matched = true; } } else { if (er == Functions.ExpandResult.NoExpansion) // no expansion, must be a variable name { string qualname = values.Qualify(f.itemname); leftside = values.Exists(qualname) ? values[qualname] : null; // then lookup.. lookup may also be null if its a pre-def if (leftside == null) { errlist += "Variable '" + qualname + "' does not exist" + Environment.NewLine; errclass = ErrorClass.LeftSideVarUndefined; innerres = false; break; // stop the loop, its a false } } string rightside; if (cf != null) // if we have a string expander, pass it thru { er = cf.ExpandString(f.matchstring, out rightside); if (er == Functions.ExpandResult.Failed) // if error, abort { errlist += rightside; // add on errors.. innerres = false; // stop loop, false break; } } else { rightside = f.matchstring; } if (f.matchtype == ConditionEntry.MatchType.DateBefore || f.matchtype == ConditionEntry.MatchType.DateAfter) { DateTime tmevalue, tmecontent; if (!DateTime.TryParse(leftside, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmevalue)) { errlist += "Date time not in correct format on left side" + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } else if (!DateTime.TryParse(rightside, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmecontent)) { errlist += "Date time not in correct format on right side" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; } else { if (f.matchtype == ConditionEntry.MatchType.DateBefore) { matched = tmevalue.CompareTo(tmecontent) < 0; } else { matched = tmevalue.CompareTo(tmecontent) >= 0; } } } else if (f.matchtype == ConditionEntry.MatchType.Equals) { matched = leftside.Equals(rightside, StringComparison.InvariantCultureIgnoreCase); } else if (f.matchtype == ConditionEntry.MatchType.EqualsCaseSensitive) { matched = leftside.Equals(rightside); } else if (f.matchtype == ConditionEntry.MatchType.NotEqual) { matched = !leftside.Equals(rightside, StringComparison.InvariantCultureIgnoreCase); } else if (f.matchtype == ConditionEntry.MatchType.NotEqualCaseSensitive) { matched = !leftside.Equals(rightside); } else if (f.matchtype == ConditionEntry.MatchType.Contains) { matched = leftside.IndexOf(rightside, StringComparison.InvariantCultureIgnoreCase) >= 0; } else if (f.matchtype == ConditionEntry.MatchType.ContainsCaseSensitive) { matched = leftside.Contains(rightside); } else if (f.matchtype == ConditionEntry.MatchType.DoesNotContain) { matched = leftside.IndexOf(rightside, StringComparison.InvariantCultureIgnoreCase) < 0; } else if (f.matchtype == ConditionEntry.MatchType.DoesNotContainCaseSensitive) { matched = !leftside.Contains(rightside); } else if (f.matchtype == ConditionEntry.MatchType.IsOneOf) { StringParser p = new StringParser(rightside); List <string> ret = p.NextQuotedWordList(); if (ret == null) { errlist += "IsOneOf value list is not in a optionally quoted comma separated form" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; // stop the loop, its a false } else { matched = ret.Contains(leftside, StringComparer.InvariantCultureIgnoreCase); } } else if (f.matchtype == ConditionEntry.MatchType.MatchSemicolon) { string[] list = rightside.Split(';').Select(x => x.Trim()).ToArray(); // split and trim matched = list.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (f.matchtype == ConditionEntry.MatchType.MatchCommaList) { StringCombinations sc = new StringCombinations(','); sc.ParseString(rightside); // parse, give all combinations matched = sc.Permutations.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (f.matchtype == ConditionEntry.MatchType.MatchSemicolonList) { StringCombinations sc = new StringCombinations(';'); sc.ParseString(rightside); // parse, give all combinations matched = sc.Permutations.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (f.matchtype == ConditionEntry.MatchType.AnyOfAny) { StringParser l = new StringParser(leftside); List <string> ll = l.NextQuotedWordList(); StringParser r = new StringParser(rightside); List <string> rl = r.NextQuotedWordList(); if (ll == null || rl == null) { errlist += "AnyOfAny value list is not in a optionally quoted comma separated form on both sides" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; // stop the loop, its a false } else { foreach (string s in ll) // for all left strings { if (rl.Contains(s, StringComparer.InvariantCultureIgnoreCase)) // if right has it.. { matched = true; // matched and break break; } } } } else if (f.matchtype == ConditionEntry.MatchType.IsEmpty) { matched = leftside.Length == 0; } else if (f.matchtype == ConditionEntry.MatchType.IsNotEmpty) { matched = leftside.Length > 0; } else if (f.matchtype == ConditionEntry.MatchType.IsTrue || f.matchtype == ConditionEntry.MatchType.IsFalse) { int inum = 0; if (leftside.InvariantParse(out inum)) { matched = (f.matchtype == ConditionEntry.MatchType.IsTrue) ? (inum != 0) : (inum == 0); } else { errlist += "True/False value is not an integer on left side" + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } } else { double fnum = 0, num = 0; if (!leftside.InvariantParse(out num)) { errlist += "Number not in correct format on left side" + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } else if (!rightside.InvariantParse(out fnum)) { errlist += "Number not in correct format on right side" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; } else { if (f.matchtype == ConditionEntry.MatchType.NumericEquals) { matched = Math.Abs(num - fnum) < 0.0000000001; // allow for rounding } else if (f.matchtype == ConditionEntry.MatchType.NumericNotEquals) { matched = Math.Abs(num - fnum) >= 0.0000000001; } else if (f.matchtype == ConditionEntry.MatchType.NumericGreater) { matched = num > fnum; } else if (f.matchtype == ConditionEntry.MatchType.NumericGreaterEqual) { matched = num >= fnum; } else if (f.matchtype == ConditionEntry.MatchType.NumericLessThan) { matched = num < fnum; } else if (f.matchtype == ConditionEntry.MatchType.NumericLessThanEqual) { matched = num <= fnum; } else { System.Diagnostics.Debug.Assert(false); } } } } } // System.Diagnostics.Debug.WriteLine(fe.eventname + ":Compare " + f.matchtype + " '" + f.contentmatch + "' with '" + vr.value + "' res " + matched + " IC " + fe.innercondition); if (fe.innercondition == ConditionEntry.LogicalCondition.And) // Short cut, if AND, all must pass, and it did not { if (!matched) { innerres = false; break; } } else if (fe.innercondition == ConditionEntry.LogicalCondition.Nand) // Short cut, if NAND, and not matched { if (!matched) { innerres = true; // positive non match - NAND produces a true break; } } else if (fe.innercondition == ConditionEntry.LogicalCondition.Or) // Short cut, if OR, and matched { if (matched) { innerres = true; break; } } else { // short cut, if NOR, and matched, its false if (matched) { innerres = false; break; } } } if (!innerres.HasValue) // All tests executed, without a short cut, we set it to a definitive state { if (fe.innercondition == ConditionEntry.LogicalCondition.And) // none did not match, producing a false, so therefore AND is true { innerres = true; } else if (fe.innercondition == ConditionEntry.LogicalCondition.Or) // none did match, producing a true, so therefore OR must be false { innerres = false; } else if (fe.innercondition == ConditionEntry.LogicalCondition.Nor) // none did match, producing a false, so therefore NOR must be true { innerres = true; } else // NAND none did match, producing a true, so therefore NAND must be false { innerres = false; } } if (innerres.Value && passed != null) // if want a list of passes, do it { passed.Add(fe); } if (!outerres.HasValue) // if first time, its just the value { outerres = innerres.Value; } else if (fe.outercondition == ConditionEntry.LogicalCondition.Or) { outerres |= innerres.Value; } else if (fe.outercondition == ConditionEntry.LogicalCondition.And) { outerres &= innerres.Value; } else if (fe.outercondition == ConditionEntry.LogicalCondition.Nor) { outerres = !(outerres | innerres.Value); } else if (fe.outercondition == ConditionEntry.LogicalCondition.Nand) { outerres = !(outerres & innerres.Value); } } return(outerres); }
// static // Check condition list fel, using the outercondition on each to combine the results // values are the set of values to use for variable lookups // pass back errlist, errclass // optionally pass back conditions which passed // optional use the function/macro expander on both sides // optionally shortcircuit on outer AND condition // obeys disabled static public bool?CheckConditions(List <Condition> fel, Variables values, out string errlist, out ErrorClass errclass, List <Condition> passed = null, Functions functionmacroexpander = null, bool shortcircuitouter = false, bool debugit = false) { errlist = null; errclass = ErrorClass.None; bool?outerres = null; for (int oc = 0; oc < fel.Count; oc++) { Condition cond = fel[oc]; if (cond.Disabled) // disabled means that its ignored { continue; } bool?innerres = null; foreach (ConditionEntry ce in cond.Fields) { bool matched = false; if (debugit) { System.Diagnostics.Debug.WriteLine($"CE `{ce.ItemName}` {ce.MatchCondition} `{ce.MatchString}`"); } // these require no left or right if (ce.MatchCondition == ConditionEntry.MatchType.AlwaysTrue || ce.MatchCondition == ConditionEntry.MatchType.AlwaysFalse) { if (ce.ItemName.Length == 0 || ce.ItemName.Equals("Condition", StringComparison.InvariantCultureIgnoreCase)) // empty (legacy) or Condition { if (ce.MatchCondition == ConditionEntry.MatchType.AlwaysTrue) { matched = true; // matched, else if false, leave as false. } } else { errlist += "AlwaysFalse/True does not have on the left side the word 'Condition'" + Environment.NewLine; errclass = ErrorClass.ExprFormatError; innerres = false; break; } } else { // at least a left side string leftside = null; Functions.ExpandResult leftexpansionresult = Functions.ExpandResult.NoExpansion; if (functionmacroexpander != null) // if we have a string expander, try the left side { leftexpansionresult = functionmacroexpander.ExpandString(ce.ItemName, out leftside); if (leftexpansionresult == Functions.ExpandResult.Failed) // stop on error { errlist += leftside; // add on errors.. innerres = false; // stop loop, false break; } } // variable names if (ce.MatchCondition == ConditionEntry.MatchType.IsPresent) { if (leftside == null || leftexpansionresult == Functions.ExpandResult.NoExpansion) // no expansion, must be a variable name { leftside = values.Qualify(ce.ItemName); // its a straight variable name, allow any special formatting } if (values.Exists(leftside) && values[leftside] != null) { matched = true; } } else if (ce.MatchCondition == ConditionEntry.MatchType.IsNotPresent) { if (leftside == null || leftexpansionresult == Functions.ExpandResult.NoExpansion) // no expansion, must be a variable name { leftside = values.Qualify(ce.ItemName); // its a straight variable name, allow any special formatting } if (!values.Exists(leftside) || values[leftside] == null) { matched = true; } } else { // if no expansion, then pass thru the eval engine now or its a variable name if (leftexpansionresult == Functions.ExpandResult.NoExpansion) { string qualname = values.Qualify(ce.ItemName); leftside = values.Exists(qualname) ? values[qualname] : null; // then lookup.. lookup may also be null if its a pre-def if (leftside == null) { errlist += "Variable '" + qualname + "' does not exist" + Environment.NewLine; errclass = ErrorClass.LeftSideVarUndefined; innerres = false; break; // stop the loop, its a false } } // System.Diagnostics.Debug.WriteLine($".. left side {leftside}"); // left side only if (ce.MatchCondition == ConditionEntry.MatchType.IsEmpty) { matched = leftside.Length == 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.IsNotEmpty) { matched = leftside.Length > 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.IsTrue || ce.MatchCondition == ConditionEntry.MatchType.IsFalse) { int inum = 0; if (leftside.InvariantParse(out inum)) { matched = (ce.MatchCondition == ConditionEntry.MatchType.IsTrue) ? (inum != 0) : (inum == 0); } else { errlist += "True/False value is not an integer on left side" + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } } else { // require a right side string rightside = null; Functions.ExpandResult rightexpansionresult = Functions.ExpandResult.NoExpansion; if (functionmacroexpander != null) // if we have a string expander, pass it thru { rightexpansionresult = functionmacroexpander.ExpandString(ce.MatchString, out rightside); if (rightexpansionresult == Functions.ExpandResult.Failed) // if error, abort { errlist += rightside; // add on errors.. innerres = false; // stop loop, false break; } } else { rightside = ce.MatchString; // no eval, we just use the string } if (debugit) { System.Diagnostics.Debug.WriteLine($"Condition `{leftside}` {ce.MatchCondition} `{rightside}`"); } if (ce.MatchCondition == ConditionEntry.MatchType.DateBefore || ce.MatchCondition == ConditionEntry.MatchType.DateAfter) { DateTime tmevalue, tmecontent; if (!DateTime.TryParse(leftside, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmevalue)) { errlist += "Date time not in correct format on left side: " + leftside + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } else if (!DateTime.TryParse(rightside, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmecontent)) { errlist += "Date time not in correct format on right side: " + rightside + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; } else { if (ce.MatchCondition == ConditionEntry.MatchType.DateBefore) { matched = tmevalue.CompareTo(tmecontent) < 0; } else { matched = tmevalue.CompareTo(tmecontent) >= 0; } } } else if (ce.MatchCondition == ConditionEntry.MatchType.Equals) { matched = leftside.Equals(rightside, StringComparison.InvariantCultureIgnoreCase); } else if (ce.MatchCondition == ConditionEntry.MatchType.EqualsCaseSensitive) { matched = leftside.Equals(rightside); } else if (ce.MatchCondition == ConditionEntry.MatchType.NotEqual) { matched = !leftside.Equals(rightside, StringComparison.InvariantCultureIgnoreCase); } else if (ce.MatchCondition == ConditionEntry.MatchType.NotEqualCaseSensitive) { matched = !leftside.Equals(rightside); } else if (ce.MatchCondition == ConditionEntry.MatchType.Contains) { matched = leftside.IndexOf(rightside, StringComparison.InvariantCultureIgnoreCase) >= 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.ContainsCaseSensitive) { matched = leftside.Contains(rightside); } else if (ce.MatchCondition == ConditionEntry.MatchType.DoesNotContain) { matched = leftside.IndexOf(rightside, StringComparison.InvariantCultureIgnoreCase) < 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.DoesNotContainCaseSensitive) { matched = !leftside.Contains(rightside); } else if (ce.MatchCondition == ConditionEntry.MatchType.IsOneOf) { StringParser p = new StringParser(rightside); List <string> ret = p.NextQuotedWordList(); if (ret == null) { errlist += "IsOneOf value list is not in a optionally quoted comma separated form" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; // stop the loop, its a false } else { matched = ret.Contains(leftside, StringComparer.InvariantCultureIgnoreCase); } } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchSemicolon) { string[] list = rightside.Split(';').Select(x => x.Trim()).ToArray(); // split and trim matched = list.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchCommaList) { StringCombinations sc = new StringCombinations(','); sc.ParseString(rightside); // parse, give all combinations matched = sc.Permutations.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchSemicolonList) { StringCombinations sc = new StringCombinations(';'); sc.ParseString(rightside); // parse, give all combinations matched = sc.Permutations.Contains(leftside.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.AnyOfAny) { StringParser l = new StringParser(leftside); List <string> ll = l.NextQuotedWordList(); StringParser r = new StringParser(rightside); List <string> rl = r.NextQuotedWordList(); if (ll == null || rl == null) { errlist += "AnyOfAny value list is not in a optionally quoted comma separated form on both sides" + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; // stop the loop, its a false } else { foreach (string s in ll) // for all left strings { if (rl.Contains(s, StringComparer.InvariantCultureIgnoreCase)) // if right has it.. { matched = true; // matched and break break; } } } } else { double fnum = 0, num = 0; if (!leftside.InvariantParse(out num)) { errlist += "Number not in correct format on left side: " + leftside + Environment.NewLine; errclass = ErrorClass.LeftSideBadFormat; innerres = false; break; } else if (!rightside.InvariantParse(out fnum)) { errlist += "Number not in correct format on right side: " + rightside + Environment.NewLine; errclass = ErrorClass.RightSideBadFormat; innerres = false; break; } else { if (ce.MatchCondition == ConditionEntry.MatchType.NumericEquals) { matched = num.ApproxEquals(fnum); } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericNotEquals) { matched = !num.ApproxEquals(fnum); } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreater) { matched = num > fnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreaterEqual) { matched = num >= fnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThan) { matched = num < fnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThanEqual) { matched = num <= fnum; } else { System.Diagnostics.Debug.Assert(false); } } } } } } // System.Diagnostics.Debug.WriteLine(fe.eventname + ":Compare " + f.matchtype + " '" + f.contentmatch + "' with '" + vr.value + "' res " + matched + " IC " + fe.innercondition); if (cond.InnerCondition == Condition.LogicalCondition.And) // Short cut, if AND, all must pass, and it did not { if (!matched) { innerres = false; break; } } else if (cond.InnerCondition == Condition.LogicalCondition.Nand) // Short cut, if NAND, and not matched { if (!matched) { innerres = true; // positive non match - NAND produces a true break; } } else if (cond.InnerCondition == Condition.LogicalCondition.Or) // Short cut, if OR, and matched { if (matched) { innerres = true; break; } } else { // short cut, if NOR, and matched, its false if (matched) { innerres = false; break; } } } // end of inner condition list look // System.Diagnostics.Debug.WriteLine($"Condition list {innerres} {errlist}"); if (!innerres.HasValue) // if does not have a value { if (cond.InnerCondition == Condition.LogicalCondition.And) // none did not matched producing a false, so therefore AND is true { innerres = true; } else if (cond.InnerCondition == Condition.LogicalCondition.Or) // none did matched producing a true, so therefore OR must be false { innerres = false; } else if (cond.InnerCondition == Condition.LogicalCondition.Nor) // none did matched producing a false, so therefore NOR must be true { innerres = true; } else // NAND none did matched producing a true, so therefore NAND must be false { innerres = false; } } else if (innerres == true) { passed?.Add(cond); } if (!outerres.HasValue) // if first time, its just the value { outerres = innerres.Value; if (shortcircuitouter && oc < fel.Count - 1) // check short circuits on NEXT ONE! if we have a next one.. { // if NEXT outer condition is an OR, and we are true // if NEXT outer condition is an AND, and we are false if ((fel[oc + 1].OuterCondition == Condition.LogicalCondition.Or && outerres == true) || (fel[oc + 1].OuterCondition == Condition.LogicalCondition.And && outerres == false)) { // System.Diagnostics.Debug.WriteLine("Short circuit on {0} cur {1}", fel[oc + 1].OuterCondition, outerres); break; } } } else if (cond.OuterCondition == Condition.LogicalCondition.Or) { outerres |= innerres.Value; if (shortcircuitouter && outerres.Value == true) // no point continuing, first one true wins { //System.Diagnostics.Debug.WriteLine("Short circuit second on {0} cur {1}", fe.OuterCondition, outerres); break; } } else if (cond.OuterCondition == Condition.LogicalCondition.And) { outerres &= innerres.Value; if (shortcircuitouter && outerres.Value == false) // no point continuing, first one false wins { //System.Diagnostics.Debug.WriteLine("Short circuit second on {0} cur {1}", fe.OuterCondition, outerres); break; } } else if (cond.OuterCondition == Condition.LogicalCondition.Nor) { outerres = !(outerres | innerres.Value); } else if (cond.OuterCondition == Condition.LogicalCondition.Nand) { outerres = !(outerres & innerres.Value); } else { System.Diagnostics.Debug.Assert(false, "Bad outer condition"); } } return(outerres); }
// Check condition list fel, using the outercondition on each to combine the results. // Use the eval engine to assemble arguments. Keep arguments as original types (long,double,strings) // values are the set of values to use for variable lookups // pass back errlist, errclass // optionally pass back test executed in order // shortcircuit on outer AND condition // obeys disabled static public bool?CheckConditionsEval(List <Condition> fel, Variables values, List <ConditionEntry> tests = null, List <string> testerrors = null, bool debugit = false) { Eval evl = new Eval(true, true, true, true, true); // check end, allow fp, allow strings, allow members, allow arrays evl.ReturnFunctionValue = BaseFunctionsForEval.BaseFunctions; // allow functions evl.ReturnSymbolValue += (str) => // on symbol lookup { string qualname = values.Qualify(str); if (values.Exists(qualname)) // if we have a variable { string text = values[qualname]; if (double.TryParse(text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double d)) { if (long.TryParse(text, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out long v)) // if its a number, return number { return(v); } else { return(d); } } else { return(text); // else its a string } } else { return(new StringParser.ConvertError("Unknown symbol " + qualname)); } }; bool?outerres = null; for (int oc = 0; oc < fel.Count; oc++) { Condition cond = fel[oc]; if (cond.Disabled) // disabled means that its ignored { continue; } bool?innerres = null; foreach (ConditionEntry ce in cond.Fields) { bool matched = false; if (debugit) { System.Diagnostics.Debug.WriteLine($"CE `{ce.ItemName}` {ce.MatchCondition} `{ce.MatchString}`"); } tests?.Add(ce); testerrors?.Add(null); // these require no left or right if (ce.MatchCondition == ConditionEntry.MatchType.AlwaysTrue || ce.MatchCondition == ConditionEntry.MatchType.AlwaysFalse) { if (ce.ItemName.Length == 0 || ce.ItemName.Equals("Condition", StringComparison.InvariantCultureIgnoreCase)) // empty (legacy) or Condition { if (ce.MatchCondition == ConditionEntry.MatchType.AlwaysTrue) { matched = true; // matched, else if false, leave as false. } } else { testerrors[testerrors.Count - 1] = "AlwaysFalse/True does not have on the left side the word 'Condition'"; innerres = false; break; } } else { // variable names if (ce.MatchCondition == ConditionEntry.MatchType.IsPresent) { string name = values.Qualify(ce.ItemName); // its a straight variable name, allow any special formatting if (values.Exists(name) && values[name] != null) { matched = true; } } else if (ce.MatchCondition == ConditionEntry.MatchType.IsNotPresent) { string name = values.Qualify(ce.ItemName); // its a straight variable name, allow any special formatting if (!values.Exists(name) || values[name] == null) { matched = true; } } else { Object leftside; if (values.Contains(ce.ItemName)) { string text = values[ce.ItemName]; if (double.TryParse(text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double d)) // if a double.. { if (long.TryParse(text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out long v)) // if its a number, return number { leftside = v; } else { leftside = d; } } else { leftside = text; // else its a string } } else { leftside = evl.EvaluateQuickCheck(ce.ItemName); // evaluate left side if (evl.InError) { if (debugit) { System.Diagnostics.Debug.WriteLine($" .. Left side in error ${((StringParser.ConvertError)leftside).ErrorValue}"); } testerrors[testerrors.Count - 1] = "Left side did not evaluate: " + ce.ItemName; leftside = null; // indicate condition has failed } } string lstring = leftside as string; // used below var clf = ConditionEntry.Classify(ce.MatchCondition); bool stringordate = clf == ConditionEntry.Classification.String || clf == ConditionEntry.Classification.Date; if (leftside != null) // if we have a leftside, check it for stringness { if (stringordate) { if (lstring == null) { if (debugit) { System.Diagnostics.Debug.WriteLine(" .. Left side not string"); } testerrors[testerrors.Count - 1] = "Left side is not a string: " + ce.ItemName; leftside = null; // indicate condition has failed } } else if (!(leftside is double || leftside is long)) // must be long or double { if (debugit) { System.Diagnostics.Debug.WriteLine(" .. Left side not number"); } testerrors[testerrors.Count - 1] = "Left side is not a number: " + ce.ItemName; leftside = null; // indicate condition has failed } } if (leftside != null) // we have a good left side { // System.Diagnostics.Debug.WriteLine($".. left side {leftside}"); if (ce.MatchCondition == ConditionEntry.MatchType.IsEmpty) { matched = lstring.Length == 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.IsNotEmpty) { matched = lstring.Length > 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.IsTrue || ce.MatchCondition == ConditionEntry.MatchType.IsFalse) { if (leftside is long) // we already checked about that leftside is double or long { matched = (ce.MatchCondition == ConditionEntry.MatchType.IsTrue) ? ((long)leftside != 0) : ((long)leftside == 0); } else { matched = (ce.MatchCondition == ConditionEntry.MatchType.IsTrue) ? ((double)leftside != 0) : ((double)leftside == 0); } } else { // require a right side Object rightside = evl.EvaluateQuickCheck(ce.MatchString); if (evl.InError) { if (stringordate) // if in error, and we are doing string date comparisions, allow bare on right { rightside = ce.MatchString; } else { testerrors[testerrors.Count - 1] = "Right side did not evaluate: " + ce.MatchString; rightside = null; // indicate bad right side } } string rstring = rightside as string; if (rightside != null) // if good right side { if (stringordate) { if (rstring == null) // must have a string { testerrors[testerrors.Count - 1] = "Right side is not a string: " + ce.ItemName; innerres = false; rightside = null; } } else if (!(rightside is double || rightside is long)) { testerrors[testerrors.Count - 1] = "Right side is not a number: " + ce.ItemName; innerres = false; rightside = null; } } if (debugit) { System.Diagnostics.Debug.WriteLine($" .. `{leftside}` {ce.MatchCondition} `{rightside}`"); } if (rightside != null) { if (ce.MatchCondition == ConditionEntry.MatchType.DateBefore || ce.MatchCondition == ConditionEntry.MatchType.DateAfter) { DateTime tmevalue, tmecontent; if (!DateTime.TryParse(lstring, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmevalue)) { testerrors[testerrors.Count - 1] = "Date time not in correct format on left side: " + leftside; innerres = false; break; } else if (!DateTime.TryParse(rstring, System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), System.Globalization.DateTimeStyles.None, out tmecontent)) { testerrors[testerrors.Count - 1] = "Date time not in correct format on right side: " + rightside; innerres = false; break; } else { if (ce.MatchCondition == ConditionEntry.MatchType.DateBefore) { matched = tmevalue.CompareTo(tmecontent) < 0; } else { matched = tmevalue.CompareTo(tmecontent) >= 0; } } } else if (ce.MatchCondition == ConditionEntry.MatchType.Equals) { matched = lstring.Equals(rstring, StringComparison.InvariantCultureIgnoreCase); } else if (ce.MatchCondition == ConditionEntry.MatchType.EqualsCaseSensitive) { matched = lstring.Equals(rstring); } else if (ce.MatchCondition == ConditionEntry.MatchType.NotEqual) { matched = !lstring.Equals(rstring, StringComparison.InvariantCultureIgnoreCase); } else if (ce.MatchCondition == ConditionEntry.MatchType.NotEqualCaseSensitive) { matched = !lstring.Equals(rstring); } else if (ce.MatchCondition == ConditionEntry.MatchType.Contains) { matched = lstring.IndexOf(rstring, StringComparison.InvariantCultureIgnoreCase) >= 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.ContainsCaseSensitive) { matched = lstring.Contains(rstring); } else if (ce.MatchCondition == ConditionEntry.MatchType.DoesNotContain) { matched = lstring.IndexOf(rstring, StringComparison.InvariantCultureIgnoreCase) < 0; } else if (ce.MatchCondition == ConditionEntry.MatchType.DoesNotContainCaseSensitive) { matched = !lstring.Contains(rstring); } else if (ce.MatchCondition == ConditionEntry.MatchType.IsOneOf) { StringParser p = new StringParser(rstring); List <string> ret = p.NextQuotedWordList(); if (ret == null) { testerrors[testerrors.Count - 1] = "IsOneOf value list is not in a optionally quoted comma separated form"; innerres = false; break; // stop the loop, its a false } else { matched = ret.Contains(lstring, StringComparer.InvariantCultureIgnoreCase); } } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchSemicolon) { string[] list = rstring.Split(';').Select(x => x.Trim()).ToArray(); // split and trim matched = list.Contains(lstring.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchCommaList) { StringCombinations sc = new StringCombinations(','); sc.ParseString(rstring); // parse, give all combinations matched = sc.Permutations.Contains(lstring.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.MatchSemicolonList) { StringCombinations sc = new StringCombinations(';'); sc.ParseString(rstring); // parse, give all combinations matched = sc.Permutations.Contains(lstring.Trim(), StringComparer.InvariantCultureIgnoreCase); // compare, trimmed, case insensitive } else if (ce.MatchCondition == ConditionEntry.MatchType.AnyOfAny) { StringParser l = new StringParser(lstring); List <string> ll = l.NextQuotedWordList(); StringParser r = new StringParser(rstring); List <string> rl = r.NextQuotedWordList(); if (ll == null || rl == null) { testerrors[testerrors.Count - 1] = "AnyOfAny value list is not in a optionally quoted comma separated form on both sides"; innerres = false; break; // stop the loop, its a false } else { foreach (string s in ll) // for all left strings { if (rl.Contains(s, StringComparer.InvariantCultureIgnoreCase)) // if right has it.. { matched = true; // matched and break break; } } } } else { if (leftside is double || rightside is double) { double lnum = leftside is long?(double)(long)leftside : (double)leftside; double rnum = rightside is long?(double)(long)rightside : (double)rightside; if (ce.MatchCondition == ConditionEntry.MatchType.NumericEquals) { matched = lnum.ApproxEquals(rnum); } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericNotEquals) { matched = !lnum.ApproxEquals(rnum); } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreater) { matched = lnum > rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreaterEqual) { matched = lnum >= rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThan) { matched = lnum < rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThanEqual) { matched = lnum <= rnum; } } else { long lnum = (long)leftside; long rnum = (long)rightside; if (ce.MatchCondition == ConditionEntry.MatchType.NumericEquals) { matched = lnum == rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericNotEquals) { matched = lnum != rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreater) { matched = lnum > rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericGreaterEqual) { matched = lnum >= rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThan) { matched = lnum < rnum; } else if (ce.MatchCondition == ConditionEntry.MatchType.NumericLessThanEqual) { matched = lnum <= rnum; } } } } } } } } if (debugit) { System.Diagnostics.Debug.WriteLine($" .. match result {matched}"); } // System.Diagnostics.Debug.WriteLine(fe.eventname + ":Compare " + f.matchtype + " '" + f.contentmatch + "' with '" + vr.value + "' res " + matched + " IC " + fe.innercondition); if (cond.InnerCondition == Condition.LogicalCondition.And) // Short cut, if AND, all must pass, and it did not { if (!matched) { innerres = false; break; } } else if (cond.InnerCondition == Condition.LogicalCondition.Nand) // Short cut, if NAND, and not matched { if (!matched) { innerres = true; // positive non match - NAND produces a true break; } } else if (cond.InnerCondition == Condition.LogicalCondition.Or) // Short cut, if OR, and matched { if (matched) { innerres = true; break; } } else { // short cut, if NOR, and matched, its false if (matched) { innerres = false; break; } } } // end of inner condition list look // System.Diagnostics.Debug.WriteLine($"Condition list {innerres} {errlist}"); if (!innerres.HasValue) // if does not have a value { if (cond.InnerCondition == Condition.LogicalCondition.And) // none did not matched producing a false, so therefore AND is true { innerres = true; } else if (cond.InnerCondition == Condition.LogicalCondition.Or) // none did matched producing a true, so therefore OR must be false { innerres = false; } else if (cond.InnerCondition == Condition.LogicalCondition.Nor) // none did matched producing a false, so therefore NOR must be true { innerres = true; } else // NAND none did matched producing a true, so therefore NAND must be false { innerres = false; } } if (!outerres.HasValue) // if first time, its just the value { outerres = innerres.Value; if (oc < fel.Count - 1) // check short circuits on NEXT ONE! if we have a next one.. { // if NEXT outer condition is an OR, and we are true // if NEXT outer condition is an AND, and we are false if ((fel[oc + 1].OuterCondition == Condition.LogicalCondition.Or && outerres == true) || (fel[oc + 1].OuterCondition == Condition.LogicalCondition.And && outerres == false)) { // System.Diagnostics.Debug.WriteLine("Short circuit on {0} cur {1}", fel[oc + 1].OuterCondition, outerres); break; } } } else if (cond.OuterCondition == Condition.LogicalCondition.Or) { outerres |= innerres.Value; if (outerres.Value == true) // no point continuing, first one true wins { //System.Diagnostics.Debug.WriteLine("Short circuit second on {0} cur {1}", fe.OuterCondition, outerres); break; } } else if (cond.OuterCondition == Condition.LogicalCondition.And) { outerres &= innerres.Value; if (outerres.Value == false) // no point continuing, first one false wins { //System.Diagnostics.Debug.WriteLine("Short circuit second on {0} cur {1}", fe.OuterCondition, outerres); break; } } else if (cond.OuterCondition == Condition.LogicalCondition.Nor) { outerres = !(outerres | innerres.Value); } else if (cond.OuterCondition == Condition.LogicalCondition.Nand) { outerres = !(outerres & innerres.Value); } else { System.Diagnostics.Debug.Assert(false, "Bad outer condition"); } } return(outerres); }