/// <summary> /// Removes redundancies in a filter to give simplest expression /// </summary> /// <typeparam name="TLeafNode"></typeparam> /// <param name="filter"></param> /// <returns></returns> public static IFilterNode <TLeafNode> Collapse <TLeafNode>(this IFilterNode <TLeafNode> filter) where TLeafNode : class, ILeafFilterNode { return(filter.Match( combinationFilter => { return combinationFilter.Operator.Match( () => { var collapsedInnerFilters = combinationFilter.Filters.Select(f => f.Collapse()) .ToList(); if (collapsedInnerFilters.Any(f => f.Equals(FilterNode <TLeafNode> .False))) { return FilterNode <TLeafNode> .False; } var nonTrivialFilters = collapsedInnerFilters.Where(f => !f.Equals(FilterNode <TLeafNode> .True)); var collapsedCombinationFilter = new CombinationFilter <TLeafNode>(nonTrivialFilters, combinationFilter.Operator); return collapsedCombinationFilter.Filters.Count == 1 ? collapsedCombinationFilter.Filters.Single() : collapsedCombinationFilter; }, () => { var collapsedInnerFilters = combinationFilter.Filters.Select(f => f.Collapse()) .ToList(); if (collapsedInnerFilters.Any(f => f.Equals(FilterNode <TLeafNode> .True))) { return FilterNode <TLeafNode> .True; } var nonTrivialFilters = collapsedInnerFilters.Where(f => !f.Equals(FilterNode <TLeafNode> .False)); var collapsedCombinationFilter = new CombinationFilter <TLeafNode>(nonTrivialFilters, combinationFilter.Operator); return collapsedCombinationFilter.Filters.Count == 1 ? collapsedCombinationFilter.Filters.Single() : collapsedCombinationFilter; }); }, invertedFilter => { var collapsedInnerFilter = invertedFilter.FilterToInvert.Collapse(); // If we have NOT(TRUE) then return FALSE or if we have NOT(FALSE) return TRUE. if (collapsedInnerFilter is ICombinationFilterNode <TLeafNode> combinationInner && combinationInner.Filters.Count == 0) { return combinationInner.Operator.Match(() => FilterNode <TLeafNode> .False, () => FilterNode <TLeafNode> .True); } // If we have NOT(NOT(f)) just return f if (collapsedInnerFilter is IInvertedFilter <TLeafNode> invertedInner) { return invertedInner.FilterToInvert; } return new InvertedFilter <TLeafNode>(collapsedInnerFilter); }, leafFilter => (IFilterNode <TLeafNode>)leafFilter)); // TODO: Figure out a way to remove this evil cast? }
/// <summary> /// 'Relaxes' a filter by relaxing each leaf of the filter according to <see cref="relaxFilterFunc"/>, or restricting /// inversions according to <see cref="restrictFilterFunc"/>. /// /// This is the inverse operation of <see cref="Restrict{TFilter}"/>. /// </summary> /// <param name="filter"></param> /// <param name="relaxFilterFunc"> /// A function which takes a leaf node and relaxes it /// </param> /// <param name="restrictFilterFunc"> /// A function which takes a leaf node and restricts it (i.e. the inverse operation of relax). /// </param> /// <returns></returns> public static IFilterNode <TFilter> Relax <TFilter>( this IFilterNode <TFilter> filter, Func <TFilter, IFilterNode <TFilter> > relaxFilterFunc, Func <TFilter, IFilterNode <TFilter> > restrictFilterFunc) where TFilter : IFilter => filter.Match( combinationFilterNode => { var innerNodes = combinationFilterNode.Nodes.Select(f => Relax(f, relaxFilterFunc, restrictFilterFunc)); return(innerNodes.Combine(combinationFilterNode.Operator)); }, invertedFilterNode => Restrict(invertedFilterNode.NodeToInvert, restrictFilterFunc, relaxFilterFunc).Invert(), leafFilterNode => relaxFilterFunc(leafFilterNode.Filter)) .Collapse();
/// <summary> /// 'Relaxes' a filter by relaxing each leaf of the filter according to <see cref="relaxFilterFunc"/>, or restricting /// inversions according to <see cref="restrictFilterFunc"/>. /// /// This is the inverse operation of <see cref="RestrictAsync{T}"/>. /// </summary> /// <param name="filter"></param> /// <param name="relaxFilterFunc"> /// A function which takes a leaf node and relaxes it /// </param> /// <param name="restrictFilterFunc"> /// A function which takes a leaf node and restricts it (i.e. the inverse operation of relax). /// </param> /// <returns></returns> public static async Task <IFilterNode <TFilter> > RelaxAsync <TFilter>( this IFilterNode <TFilter> filter, Func <TFilter, Task <IFilterNode <TFilter> > > relaxFilterFunc, Func <TFilter, Task <IFilterNode <TFilter> > > restrictFilterFunc) where TFilter : IFilter { var result = await filter.Match <Task <IFilterNode <TFilter> > >( async combinationFilterNode => { var innerFilterNodeTasks = combinationFilterNode.Nodes.Select(f => RelaxAsync(f, relaxFilterFunc, restrictFilterFunc)); var innerFilterNodes = await Task.WhenAll(innerFilterNodeTasks).ConfigureAwait(false); return(innerFilterNodes.Combine(combinationFilterNode.Operator)); }, async invertedFilterNode => (await RestrictAsync(invertedFilterNode.NodeToInvert, restrictFilterFunc, relaxFilterFunc).ConfigureAwait(false)).Invert(), leafFilterNode => relaxFilterFunc(leafFilterNode.Filter)) .ConfigureAwait(false); return(result.Collapse()); }