Пример #1
0
        internal OperationResult StartsWithOperationDelegate(Clause clause, object?state1, object?state2, IEnumerable <ClauseCapture>?captures)
        {
            (var stateOneList, var stateOneDict) = Analyzer?.ObjectToValues(state1) ?? (new List <string>(), new List <KeyValuePair <string, string> >());
            (var stateTwoList, var stateTwoDict) = Analyzer?.ObjectToValues(state2) ?? (new List <string>(), new List <KeyValuePair <string, string> >());
            if (clause.Data is List <string> StartsWithData)
            {
                var results = new List <string>();
                foreach (var entry in stateOneList)
                {
                    var res = StartsWithData.Any(x => entry.StartsWith(x));
                    if ((res && !clause.Invert) || (clause.Invert && !res))
                    {
                        results.Add(entry);
                    }
                }

                if (results.Any())
                {
                    var typeHolder = state1 ?? state2;

                    return(typeHolder switch
                    {
                        string _ => new OperationResult(true, !clause.Capture ? null : new TypedClauseCapture <string>(clause, results.First(), state1, null)),
                        _ => new OperationResult(true, !clause.Capture ? null : new TypedClauseCapture <List <string> >(clause, results, state1, null)),
                    });
        protected bool AnalyzeClause(Clause clause, object?before = null, object?after = null)
        {
            if (clause == null || (before == null && after == null))
            {
                return(false);
            }
            try
            {
                // Support bare objects
                if (clause.Field is string)
                {
                    after  = GetValueByPropertyString(after, clause.Field);
                    before = GetValueByPropertyString(before, clause.Field);
                }


                var typeHolder = before is null ? after : before;

                (var beforeList, var beforeDict) = ObjectToValues(before);
                (var afterList, var afterDict)   = ObjectToValues(after);

                var valsToCheck = beforeList.Union(afterList);
                var dictToCheck = beforeDict.Union(afterDict);

                switch (clause.Operation)
                {
                case OPERATION.EQ:
                    if (clause.Data is List <string> EqualsData)
                    {
                        if (EqualsData.Intersect(valsToCheck).Any())
                        {
                            return(true);
                        }
                    }
                    return(false);

                case OPERATION.NEQ:
                    if (clause.Data is List <string> NotEqualsData)
                    {
                        if (!NotEqualsData.Intersect(valsToCheck).Any())
                        {
                            return(true);
                        }
                    }
                    return(false);

                // If *every* entry of the clause data is matched
                case OPERATION.CONTAINS:
                    if (dictToCheck.Any())
                    {
                        if (clause.DictData is List <KeyValuePair <string, string> > ContainsData)
                        {
                            if (ContainsData.All(y => dictToCheck.Any((x) => x.Key == y.Key && x.Value == y.Value)))
                            {
                                return(true);
                            }
                        }
                    }
                    else if (valsToCheck.Any())
                    {
                        if (clause.Data is List <string> ContainsDataList)
                        {
                            // If we are dealing with an array on the object side
                            if (typeHolder is List <string> )
                            {
                                if (ContainsDataList.All(x => valsToCheck.Contains(x)))
                                {
                                    return(true);
                                }
                            }
                            // If we are dealing with a single string we do a .Contains instead
                            else if (typeHolder is string)
                            {
                                if (clause.Data.All(x => valsToCheck.First()?.Contains(x) ?? false))
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                    return(false);

                // If *any* entry of the clause data is matched
                case OPERATION.CONTAINS_ANY:
                    if (dictToCheck.Any())
                    {
                        if (clause.DictData is List <KeyValuePair <string, string> > ContainsData)
                        {
                            foreach (KeyValuePair <string, string> value in ContainsData)
                            {
                                if (dictToCheck.Any(x => x.Key == value.Key && x.Value == value.Value))
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                    else if (valsToCheck.Any())
                    {
                        if (clause.Data is List <string> ContainsDataList)
                        {
                            if (typeHolder is List <string> )
                            {
                                if (ContainsDataList.Any(x => valsToCheck.Contains(x)))
                                {
                                    return(true);
                                }
                            }
                            // If we are dealing with a single string we do a .Contains instead
                            else if (typeHolder is string)
                            {
                                if (clause.Data.Any(x => valsToCheck.First()?.Contains(x) ?? false))
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                    return(false);

                // If any of the data values are greater than the first provided clause value We
                // ignore all other clause values
                case OPERATION.GT:
                    foreach (var val in valsToCheck)
                    {
                        if (int.TryParse(val, out int valToCheck))
                        {
                            if (int.TryParse(clause.Data?[0], out int dataValue))
                            {
                                if (valToCheck > dataValue)
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                    return(false);

                // If any of the data values are less than the first provided clause value We
                // ignore all other clause values
                case OPERATION.LT:
                    foreach (var val in valsToCheck)
                    {
                        if (int.TryParse(val, out int valToCheck))
                        {
                            if (int.TryParse(clause.Data?[0], out int dataValue))
                            {
                                if (valToCheck < dataValue)
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                    return(false);

                // If any of the regexes match any of the values
                case OPERATION.REGEX:
                    if (clause.Data is List <string> RegexList)
                    {
                        if (RegexList.Count > 0)
                        {
                            var built = string.Join('|', RegexList);

                            if (!RegexCache.ContainsKey(built))
                            {
                                try
                                {
                                    RegexCache.TryAdd(built, new Regex(built, RegexOptions.Compiled));
                                }
                                catch (ArgumentException)
                                {
                                    Log.Warning("InvalidArgumentException when analyzing clause {0}. Regex {1} is invalid and will be skipped.", clause.Label, built);
                                    RegexCache.TryAdd(built, new Regex("", RegexOptions.Compiled));
                                }
                            }

                            if (valsToCheck.Any(x => RegexCache[built].IsMatch(x)))
                            {
                                return(true);
                            }
                        }
                    }
                    return(false);

                // Ignores provided data. Checks if the named property has changed.
                case OPERATION.WAS_MODIFIED:
                    CompareLogic compareLogic = new CompareLogic();

                    ComparisonResult comparisonResult = compareLogic.Compare(before, after);

                    return(!comparisonResult.AreEqual);

                // Ends with any of the provided data
                case OPERATION.ENDS_WITH:
                    if (clause.Data is List <string> EndsWithData)
                    {
                        if (valsToCheck.Any(x => EndsWithData.Any(y => x is string && x.EndsWith(y, StringComparison.CurrentCulture))))
                        {
                            return(true);
                        }
                    }
                    return(false);

                // Starts with any of the provided data
                case OPERATION.STARTS_WITH:
                    if (clause.Data is List <string> StartsWithData)
                    {
                        if (valsToCheck.Any(x => StartsWithData.Any(y => x is string && x.StartsWith(y, StringComparison.CurrentCulture))))
                        {
                            return(true);
                        }
                    }
                    return(false);

                case OPERATION.IS_NULL:
                    if (valsToCheck.Count(x => x is null) == valsToCheck.Count())
                    {
                        return(true);
                    }
                    return(false);

                case OPERATION.IS_TRUE:
                    foreach (var valToCheck in valsToCheck)
                    {
                        if (bool.TryParse(valToCheck, out bool result))
                        {
                            if (result)
                            {
                                return(true);
                            }
                        }
                    }
                    return(false);

                case OPERATION.IS_BEFORE:
                    var valDateTimes = new List <DateTime>();
                    foreach (var valToCheck in valsToCheck)
                    {
                        if (DateTime.TryParse(valToCheck, out DateTime result))
                        {
                            valDateTimes.Add(result);
                        }
                    }
                    foreach (var data in clause.Data ?? new List <string>())
                    {
                        if (DateTime.TryParse(data, out DateTime result))
                        {
                            if (valDateTimes.Any(x => x.CompareTo(result) < 0))
                            {
                                return(true);
                            }
                        }
                    }
                    return(false);

                case OPERATION.IS_AFTER:
                    valDateTimes = new List <DateTime>();
                    foreach (var valToCheck in valsToCheck)
                    {
                        if (DateTime.TryParse(valToCheck, out DateTime result))
                        {
                            valDateTimes.Add(result);
                        }
                    }
                    foreach (var data in clause.Data ?? new List <string>())
                    {
                        if (DateTime.TryParse(data, out DateTime result))
                        {
                            if (valDateTimes.Any(x => x.CompareTo(result) > 0))
                            {
                                return(true);
                            }
                        }
                    }
                    return(false);

                case OPERATION.IS_EXPIRED:
                    foreach (var valToCheck in valsToCheck)
                    {
                        if (DateTime.TryParse(valToCheck, out DateTime result))
                        {
                            if (result.CompareTo(DateTime.Now) < 0)
                            {
                                return(true);
                            }
                        }
                    }
                    return(false);

                case OPERATION.CONTAINS_KEY:
                    return(dictToCheck.Any(x => clause.Data.Any(y => x.Key == y)));

                case OPERATION.CUSTOM:
                    if (CustomOperationDelegate is null)
                    {
                        Log.Debug("Custom operation hit but {0} isn't set.", nameof(CustomOperationDelegate));
                        return(false);
                    }
                    else
                    {
                        return(CustomOperationDelegate.Invoke(clause, valsToCheck, dictToCheck));
                    }

                default:
                    Log.Debug("Unimplemented operation {0}", clause.Operation);
                    return(false);
                }
            }
            catch (Exception e)
            {
                Log.Debug(e, $"Hit while parsing {JsonConvert.SerializeObject(clause)} onto ({JsonConvert.SerializeObject(before)},{JsonConvert.SerializeObject(after)})");
            }

            return(false);
        }