/Simple.Data/QueryPolyfills/WhereClauseHandler.cs

https://github.com/marcusoftnet/Simple.Data · C# · 171 lines · 141 code · 30 blank · 0 comment · 24 complexity · d7df6455653d991d54b7ca6f525fe372 MD5 · raw file

  1. namespace Simple.Data.QueryPolyfills
  2. {
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text.RegularExpressions;
  8. internal class WhereClauseHandler
  9. {
  10. private readonly Dictionary<SimpleExpressionType, Func<SimpleExpression, Func<IDictionary<string, object>, bool>>> _expressionFormatters;
  11. private readonly WhereClause _whereClause;
  12. public WhereClauseHandler(WhereClause whereClause)
  13. {
  14. _whereClause = whereClause;
  15. _expressionFormatters = new Dictionary<SimpleExpressionType, Func<SimpleExpression, Func<IDictionary<string,object>, bool>>>
  16. {
  17. {SimpleExpressionType.And, LogicalExpressionToWhereClause},
  18. {SimpleExpressionType.Or, LogicalExpressionToWhereClause},
  19. {SimpleExpressionType.Equal, EqualExpressionToWhereClause},
  20. {SimpleExpressionType.NotEqual, NotEqualExpressionToWhereClause},
  21. {SimpleExpressionType.Function, FunctionExpressionToWhereClause},
  22. {SimpleExpressionType.GreaterThan, GreaterThanToWhereClause},
  23. {SimpleExpressionType.LessThan, LessThanToWhereClause},
  24. {SimpleExpressionType.GreaterThanOrEqual, GreaterThanOrEqualToWhereClause},
  25. {SimpleExpressionType.LessThanOrEqual, LessThanOrEqualToWhereClause},
  26. {SimpleExpressionType.Empty, expr => _ => true },
  27. };
  28. }
  29. private Func<IDictionary<string, object>, bool> FunctionExpressionToWhereClause(SimpleExpression arg)
  30. {
  31. var function = arg.RightOperand as SimpleFunction;
  32. if (ReferenceEquals(function, null)) throw new InvalidOperationException("Expression type of function but no function supplied.");
  33. if (function.Name.Equals("like", StringComparison.OrdinalIgnoreCase))
  34. {
  35. var pattern = function.Args[0].ToString();
  36. if (pattern.Contains("%") || pattern.Contains("_")) // SQL Server LIKE
  37. {
  38. pattern = pattern.Replace("%", ".*").Replace('_', '.');
  39. }
  40. var regex = new Regex("^" + pattern + "$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  41. return d => Resolve(d, arg.LeftOperand).Count > 0 && Resolve(d, arg.LeftOperand).OfType<string>().Any(regex.IsMatch);
  42. }
  43. throw new NotSupportedException("Expression Function not supported.");
  44. }
  45. private Func<IDictionary<string, object>, bool> GreaterThanToWhereClause(SimpleExpression arg)
  46. {
  47. return d => Resolve(d, arg.LeftOperand).OfType<IComparable>().Any(o => o.CompareTo(arg.RightOperand) > 0);
  48. }
  49. private Func<IDictionary<string, object>, bool> LessThanToWhereClause(SimpleExpression arg)
  50. {
  51. return d => Resolve(d, arg.LeftOperand).OfType<IComparable>().Any(o => o.CompareTo(arg.RightOperand) < 0);
  52. }
  53. private Func<IDictionary<string, object>, bool> GreaterThanOrEqualToWhereClause(SimpleExpression arg)
  54. {
  55. return d => Resolve(d, arg.LeftOperand).OfType<IComparable>().Any(o => o.CompareTo(arg.RightOperand) >= 0);
  56. }
  57. private Func<IDictionary<string, object>, bool> LessThanOrEqualToWhereClause(SimpleExpression arg)
  58. {
  59. return d => Resolve(d, arg.LeftOperand).OfType<IComparable>().Any(o => o.CompareTo(arg.RightOperand) <= 0);
  60. }
  61. private Func<IDictionary<string, object>, bool> NotEqualExpressionToWhereClause(SimpleExpression arg)
  62. {
  63. var equal = EqualExpressionToWhereClause(arg);
  64. return d => !equal(d);
  65. }
  66. private Func<IDictionary<string, object>, bool> EqualExpressionToWhereClause(SimpleExpression arg)
  67. {
  68. if (ReferenceEquals(arg.RightOperand, null))
  69. {
  70. return d => Resolve(d, arg.LeftOperand).Count == 0 || Resolve(d, arg.LeftOperand).Any(o => ReferenceEquals(o, null));
  71. }
  72. if (arg.RightOperand.GetType().IsArray)
  73. {
  74. return
  75. d =>
  76. Resolve(d, arg.LeftOperand).OfType<IEnumerable>().Any(
  77. o => o.Cast<object>().SequenceEqual(((IEnumerable) arg.RightOperand).Cast<object>()));
  78. }
  79. return d => Resolve(d, arg.LeftOperand).Contains(arg.RightOperand);
  80. }
  81. private Func<IDictionary<string,object>, bool> Format(SimpleExpression expression)
  82. {
  83. Func<SimpleExpression, Func<IDictionary<string,object>,bool>> formatter;
  84. if (_expressionFormatters.TryGetValue(expression.Type, out formatter))
  85. {
  86. return formatter(expression);
  87. }
  88. return _ => true;
  89. }
  90. private Func<IDictionary<string, object>, bool> LogicalExpressionToWhereClause(SimpleExpression arg)
  91. {
  92. var left = Format((SimpleExpression) arg.LeftOperand);
  93. var right = Format((SimpleExpression) arg.RightOperand);
  94. if (arg.Type == SimpleExpressionType.Or)
  95. {
  96. return d => (left(d) || right(d));
  97. }
  98. return d => (left(d) && right(d));
  99. }
  100. private IList<object> Resolve(IDictionary<string, object> dict, object operand, string key = null)
  101. {
  102. var objectReference = operand as ObjectReference;
  103. if (objectReference.IsNull()) return new object[0];
  104. key = key ?? objectReference.GetAliasOrName();
  105. var keys = objectReference.GetAllObjectNames();
  106. if (keys.Length > 2)
  107. {
  108. return ResolveSubs(dict, objectReference.GetOwner(), key).ToList();
  109. }
  110. if (dict.ContainsKey(key))
  111. return new[] {dict[key]};
  112. return new object[0];
  113. }
  114. private IEnumerable<object> ResolveSubs(IDictionary<string, object> dict, ObjectReference objectReference, string key)
  115. {
  116. if (objectReference.IsNull()) return Enumerable.Empty<object>();
  117. if (dict.ContainsKey(objectReference.GetName()))
  118. {
  119. var master = dict[objectReference.GetName()] as IDictionary<string,object>;
  120. if (master != null)
  121. {
  122. if (master.ContainsKey(key))
  123. {
  124. return new[] {master[key]};
  125. }
  126. }
  127. var detail = dict[objectReference.GetName()] as IEnumerable<IDictionary<string, object>>;
  128. if (detail != null)
  129. {
  130. return detail.SelectMany(d => Resolve(d, objectReference, key));
  131. }
  132. }
  133. return ResolveSubs(dict, objectReference.GetOwner(), key);
  134. }
  135. public IEnumerable<IDictionary<string, object>> Run(IEnumerable<IDictionary<string, object>> source)
  136. {
  137. var predicate = Format(_whereClause.Criteria);
  138. return source.Where(predicate);
  139. }
  140. }
  141. }