| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="FilterBuilder.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Linq.Expressions; |
| | | 10 | | using MyNet.Primitives; |
| | | 11 | | |
| | | 12 | | namespace MyNet.Observable.Collections.Filters; |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Provides a fluent API for building complex filter expressions for collections of type T. |
| | | 16 | | /// </summary> |
| | | 17 | | /// <typeparam name="T">The type of items to be filtered.</typeparam> |
| | | 18 | | public sealed class FilterBuilder<T> |
| | | 19 | | { |
| | 30 | 20 | | private readonly List<(LogicalOperator Operator, IFilter<T> Node)> _nodes = []; |
| | | 21 | | |
| | | 22 | | /// <summary> |
| | | 23 | | /// Creates a new instance of the <see cref="FilterBuilder{T}"/> class, providing a fluent API for constructing comp |
| | | 24 | | /// </summary> |
| | | 25 | | /// <returns>A new instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | 27 | 26 | | public static FilterBuilder<T> Create() => new(); |
| | | 27 | | |
| | | 28 | | /// <summary> |
| | | 29 | | /// Adds a filter expression to the builder using the specified predicate. The predicate is a lambda expression that |
| | | 30 | | /// </summary> |
| | | 31 | | /// <param name="expr">A lambda expression that defines the filtering logic for items of type T.</param> |
| | | 32 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | 21 | 33 | | public FilterBuilder<T> Where(Expression<Func<T, bool>> expr) => And(expr); |
| | | 34 | | |
| | | 35 | | /// <summary> |
| | | 36 | | /// Adds a grouped filter expression to the builder using the specified group function. The group function is a lamb |
| | | 37 | | /// </summary> |
| | | 38 | | /// <param name="group">A lambda function that defines a group of filter expressions.</param> |
| | | 39 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | 0 | 40 | | public FilterBuilder<T> Where(Func<FilterBuilder<T>, FilterBuilder<T>> group) => And(group); |
| | | 41 | | |
| | | 42 | | /// <summary> |
| | | 43 | | /// Adds a filter expression to the builder using the logical AND operator. The provided expression is combined with |
| | | 44 | | /// </summary> |
| | | 45 | | /// <param name="expr">A lambda expression that defines the filtering logic for items of type T.</param> |
| | | 46 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | | 47 | | public FilterBuilder<T> And(Expression<Func<T, bool>> expr) |
| | | 48 | | { |
| | 21 | 49 | | AddNode(LogicalOperator.And, new ExpressionFilter<T>(expr)); |
| | 21 | 50 | | return this; |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// Adds a grouped filter expression to the builder using the logical AND operator. The provided group function is u |
| | | 55 | | /// </summary> |
| | | 56 | | /// <param name="group">A lambda function that defines a group of filter expressions.</param> |
| | | 57 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | 3 | 58 | | public FilterBuilder<T> And(Func<FilterBuilder<T>, FilterBuilder<T>> group) => AddGroup(LogicalOperator.And, group); |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Adds a grouped filter expression to the builder using the logical OR operator. The provided group function is us |
| | | 62 | | /// </summary> |
| | | 63 | | /// <param name="expression">A lambda function that defines a group of filter expressions.</param> |
| | | 64 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | | 65 | | public FilterBuilder<T> Or(Expression<Func<T, bool>> expression) |
| | | 66 | | { |
| | 3 | 67 | | AddNode(LogicalOperator.Or, new ExpressionFilter<T>(expression)); |
| | 3 | 68 | | return this; |
| | | 69 | | } |
| | | 70 | | |
| | | 71 | | /// <summary> |
| | | 72 | | /// Adds a grouped filter expression to the builder using the logical OR operator. The provided group function is us |
| | | 73 | | /// </summary> |
| | | 74 | | /// <param name="group">A lambda function that defines a group of filter expressions.</param> |
| | | 75 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | 0 | 76 | | public FilterBuilder<T> Or(Func<FilterBuilder<T>, FilterBuilder<T>> group) => AddGroup(LogicalOperator.Or, group); |
| | | 77 | | |
| | | 78 | | /// <summary> |
| | | 79 | | /// Adds a grouped filter expression to the builder using the specified logical operator. The provided group functio |
| | | 80 | | /// </summary> |
| | | 81 | | /// <param name="op">The logical operator to use when combining the group with existing expressions.</param> |
| | | 82 | | /// <param name="builder">A lambda function that defines a group of filter expressions.</param> |
| | | 83 | | /// <returns>The current instance of <see cref="FilterBuilder{T}"/>.</returns> |
| | | 84 | | private FilterBuilder<T> AddGroup(LogicalOperator op, Func<FilterBuilder<T>, FilterBuilder<T>> builder) |
| | | 85 | | { |
| | 3 | 86 | | var inner = builder(new()); |
| | 3 | 87 | | var node = inner.Build(); |
| | | 88 | | |
| | 3 | 89 | | if (node is not null) |
| | 3 | 90 | | AddNode(op, node); |
| | | 91 | | |
| | 3 | 92 | | return this; |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// Adds a filter node to the builder with the specified logical operator. The node represents a filter expression o |
| | | 97 | | /// </summary> |
| | | 98 | | /// <param name="op">The logical operator to use when combining the node with existing expressions.</param> |
| | | 99 | | /// <param name="node">The filter node to add to the builder.</param> |
| | 27 | 100 | | private void AddNode(LogicalOperator op, IFilter<T> node) => _nodes.Add((op, node)); |
| | | 101 | | |
| | | 102 | | /// <summary> |
| | | 103 | | /// Builds the filter expression tree based on the nodes added to the builder. If no nodes have been added, it retur |
| | | 104 | | /// </summary> |
| | | 105 | | /// <returns>The root filter node representing the entire filter expression tree.</returns> |
| | | 106 | | public IFilter<T>? Build() |
| | 30 | 107 | | => _nodes.Count switch |
| | 30 | 108 | | { |
| | 9 | 109 | | 0 => null, |
| | 15 | 110 | | 1 => _nodes[0].Node, |
| | 6 | 111 | | _ => BuildTree() |
| | 30 | 112 | | }; |
| | | 113 | | |
| | | 114 | | /// <summary> |
| | | 115 | | /// Builds the filter expression tree by combining all nodes in the builder according to their specified logical ope |
| | | 116 | | /// </summary> |
| | | 117 | | /// <returns>The root filter node representing the entire filter expression tree.</returns> |
| | | 118 | | private IFilter<T> BuildTree() |
| | | 119 | | { |
| | | 120 | | // Group sequentially respecting operators |
| | 6 | 121 | | var current = _nodes[0].Node; |
| | | 122 | | |
| | 24 | 123 | | for (var i = 1; i < _nodes.Count; i++) |
| | | 124 | | { |
| | 6 | 125 | | var (op, node) = _nodes[i]; |
| | | 126 | | |
| | 6 | 127 | | current = new FilterGroup<T>(op, [current, node]); |
| | | 128 | | } |
| | | 129 | | |
| | 6 | 130 | | return current; |
| | | 131 | | } |
| | | 132 | | } |
| | | 133 | | |