| | 1 | | using Plainquire.Filter.Abstractions; |
| | 2 | | using System; |
| | 3 | | using System.Collections.Generic; |
| | 4 | | using System.Globalization; |
| | 5 | | using System.Linq; |
| | 6 | | using System.Linq.Expressions; |
| | 7 | |
|
| | 8 | | namespace Plainquire.Filter.ValueFilterExpressions; |
| | 9 | |
|
| | 10 | | /// <inheritdoc cref="IEnumFilterExpression"/> |
| | 11 | | public class EnumFilterExpression : DefaultFilterExpression, IEnumFilterExpression |
| | 12 | | { |
| | 13 | | /// <inheritdoc /> |
| | 14 | | public override ICollection<FilterOperator> SupportedFilterOperators |
| | 15 | | => |
| 3576 | 16 | | [ |
| 3576 | 17 | | FilterOperator.Default, |
| 3576 | 18 | | FilterOperator.Contains, |
| 3576 | 19 | | FilterOperator.StartsWith, |
| 3576 | 20 | | FilterOperator.EndsWith, |
| 3576 | 21 | | FilterOperator.EqualCaseSensitive, |
| 3576 | 22 | | FilterOperator.EqualCaseInsensitive, |
| 3576 | 23 | | FilterOperator.NotEqual, |
| 3576 | 24 | | FilterOperator.LessThan, |
| 3576 | 25 | | FilterOperator.LessThanOrEqual, |
| 3576 | 26 | | FilterOperator.GreaterThan, |
| 3576 | 27 | | FilterOperator.GreaterThanOrEqual, |
| 3576 | 28 | | FilterOperator.IsNull, |
| 3576 | 29 | | FilterOperator.NotNull |
| 3576 | 30 | | ]; |
| | 31 | |
|
| | 32 | | /// <inheritdoc /> |
| | 33 | | public override bool CanCreateExpressionFor(Type type) |
| 3723 | 34 | | => type.GetUnderlyingType().IsEnum; |
| | 35 | |
|
| | 36 | | /// <inheritdoc /> |
| | 37 | | protected internal override Expression CreateExpressionForValue<TEntity, TProperty>(Expression<Func<TEntity, TProper |
| | 38 | | { |
| 3480 | 39 | | var operatorImpliesMatchByName = filterOperator is FilterOperator.Contains or FilterOperator.StartsWith or Filte |
| 3480 | 40 | | if (operatorImpliesMatchByName) |
| 984 | 41 | | return CreateEnumFromStringExpression(propertySelector, filterOperator, value, configuration, interceptor); |
| | 42 | |
|
| 2496 | 43 | | var valueIsNumeric = long.TryParse(value, NumberStyles.Any, new CultureInfo(configuration.CultureName), out var |
| 2496 | 44 | | if (!valueIsNumeric) |
| 1536 | 45 | | return CreateEnumFromStringExpression(propertySelector, filterOperator, value, configuration, interceptor); |
| | 46 | |
|
| 960 | 47 | | var enumValue = (TProperty)Enum.ToObject(typeof(TProperty).GetUnderlyingType(), numericValue); |
| 960 | 48 | | return CreateEnumExpressionByFilterOperator(propertySelector, filterOperator, enumValue); |
| | 49 | | } |
| | 50 | |
|
| | 51 | | private Expression CreateEnumFromStringExpression<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> propertyS |
| | 52 | | { |
| 2520 | 53 | | var enumValues = GetEnumValuesMatchByStringFilter<TProperty>(filterOperator, value, configuration, interceptor). |
| 2520 | 54 | | if (!enumValues.Any()) |
| 864 | 55 | | return Expression.Constant(false); |
| | 56 | |
|
| 1656 | 57 | | var result = enumValues |
| 1656 | 58 | | .Select(x => CreateEnumExpressionByFilterOperator(propertySelector, filterOperator, x)) |
| 1656 | 59 | | .Aggregate(Expression.OrElse); |
| | 60 | |
|
| 1656 | 61 | | return result; |
| | 62 | | } |
| | 63 | |
|
| | 64 | | private Expression CreateEnumExpressionByFilterOperator<TEntity, TProperty, TEnum>(Expression<Func<TEntity, TPropert |
| | 65 | | { |
| 3432 | 66 | | var underlyingEnumType = Enum.GetUnderlyingType(typeof(TProperty).GetUnderlyingType()); |
| 3432 | 67 | | var numericEnumValue = Convert.ChangeType(value, underlyingEnumType, CultureInfo.InvariantCulture); |
| | 68 | |
|
| | 69 | | switch (filterOperator) |
| | 70 | | { |
| | 71 | | case FilterOperator.Default: |
| | 72 | | case FilterOperator.Contains: |
| | 73 | | case FilterOperator.StartsWith: |
| | 74 | | case FilterOperator.EndsWith: |
| | 75 | | case FilterOperator.EqualCaseInsensitive: |
| | 76 | | case FilterOperator.EqualCaseSensitive: |
| 2112 | 77 | | return CreateEqualExpression(propertySelector, value); |
| | 78 | | case FilterOperator.NotEqual: |
| 264 | 79 | | return CreateNotEqualExpression(propertySelector, value); |
| | 80 | | case FilterOperator.LessThan: |
| 264 | 81 | | return CreateLessThanExpression(propertySelector, underlyingEnumType, numericEnumValue); |
| | 82 | | case FilterOperator.LessThanOrEqual: |
| 264 | 83 | | return CreateLessThanOrEqualExpression(propertySelector, underlyingEnumType, numericEnumValue); |
| | 84 | | case FilterOperator.GreaterThan: |
| 264 | 85 | | return CreateGreaterThanExpression(propertySelector, underlyingEnumType, numericEnumValue); |
| | 86 | | case FilterOperator.GreaterThanOrEqual: |
| 264 | 87 | | return CreateGreaterThanOrEqualExpression(propertySelector, underlyingEnumType, numericEnumValue); |
| | 88 | | default: |
| 0 | 89 | | throw CreateFilterExpressionCreationException($"Filter operator '{filterOperator}' not allowed for prope |
| | 90 | | } |
| | 91 | | } |
| | 92 | |
|
| | 93 | | private static IEnumerable<TProperty> GetEnumValuesMatchByStringFilter<TProperty>(FilterOperator filterOperator, str |
| | 94 | | { |
| 2520 | 95 | | var stringFilterExpressionCreator = new StringFilterExpression(); |
| 2520 | 96 | | Expression<Func<KeyValuePair<TProperty, string>, string>> dictionaryValueSelector = x => x.Value; |
| | 97 | |
|
| 2520 | 98 | | var stringFilterOperator = filterOperator switch |
| 2520 | 99 | | { |
| 312 | 100 | | FilterOperator.Contains => FilterOperator.Contains, |
| 336 | 101 | | FilterOperator.StartsWith => FilterOperator.StartsWith, |
| 336 | 102 | | FilterOperator.EndsWith => FilterOperator.EndsWith, |
| 192 | 103 | | FilterOperator.EqualCaseInsensitive => FilterOperator.EqualCaseInsensitive, |
| 192 | 104 | | FilterOperator.EqualCaseSensitive => FilterOperator.EqualCaseSensitive, |
| 1152 | 105 | | _ => FilterOperator.EqualCaseInsensitive |
| 2520 | 106 | | }; |
| | 107 | |
|
| 2520 | 108 | | var underlyingEnumType = typeof(TProperty).GetUnderlyingType(); |
| | 109 | |
|
| 2520 | 110 | | var stringFilterExpression = stringFilterExpressionCreator |
| 2520 | 111 | | .CreateExpressionForValue(dictionaryValueSelector, stringFilterOperator, value, configuration, interceptor); |
| | 112 | |
|
| 2520 | 113 | | var stringFilter = Expression |
| 2520 | 114 | | .Lambda<Func<KeyValuePair<TProperty, string>, bool>>(stringFilterExpression, dictionaryValueSelector.Paramet |
| 2520 | 115 | | .Compile(); |
| | 116 | |
|
| 2520 | 117 | | var filteredValues = Enum |
| 2520 | 118 | | .GetValues(underlyingEnumType) |
| 2520 | 119 | | .Cast<TProperty>() |
| 2520 | 120 | | .Select(enumValue => KeyValuePair.Create(enumValue, EnumGetName(underlyingEnumType, enumValue))) |
| 2520 | 121 | | .Where(stringFilter) |
| 2520 | 122 | | .Select(x => x.Key); |
| | 123 | |
|
| 2520 | 124 | | return filteredValues; |
| | 125 | | } |
| | 126 | |
|
| | 127 | | private static string EnumGetName<TEnum>(Type underlyingEnumType, TEnum value) |
| 12600 | 128 | | => Enum.GetName(underlyingEnumType, value!) |
| 12600 | 129 | | ?? throw new InvalidOperationException($"Unable to get value of {value} for enum {underlyingEnumType.Name}"); |
| | 130 | | } |