PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Atlassian.Jira/Linq/ExpressionEvaluator.cs

https://bitbucket.org/yyo/atlassian.net-sdk-v2.0
C# | 132 lines | 95 code | 12 blank | 25 comment | 10 complexity | 96e77b632f884522d48537fd844a6dcd MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. namespace Atlassian.Jira.Linq
  7. {
  8. /// <summary>
  9. /// Evaluates subtrees that contain local variables.
  10. /// </summary>
  11. /// <remarks>
  12. /// Thanks to http://blogs.msdn.com/b/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx
  13. /// for providing the source for this class
  14. /// </remarks>
  15. internal static class ExpressionEvaluator
  16. {
  17. /// <summary>
  18. /// Performs evaluation & replacement of independent sub-trees
  19. /// </summary>
  20. /// <param name="expression">The root of the expression tree.</param>
  21. /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
  22. public static Expression PartialEval(Expression expression)
  23. {
  24. return PartialEval(expression, ExpressionEvaluator.CanBeEvaluatedLocally);
  25. }
  26. /// <summary>
  27. /// Performs evaluation & replacement of independent sub-trees
  28. /// </summary>
  29. /// <param name="expression">The root of the expression tree.</param>
  30. /// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
  31. /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
  32. public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
  33. {
  34. return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
  35. }
  36. private static bool CanBeEvaluatedLocally(Expression expression)
  37. {
  38. return expression.NodeType != ExpressionType.Parameter;
  39. }
  40. /// <summary>
  41. /// Evaluates & replaces sub-trees when first candidate is reached (top-down)
  42. /// </summary>
  43. class SubtreeEvaluator : ExpressionVisitor
  44. {
  45. HashSet<Expression> candidates;
  46. internal SubtreeEvaluator(HashSet<Expression> candidates)
  47. {
  48. this.candidates = candidates;
  49. }
  50. internal Expression Eval(Expression exp)
  51. {
  52. return this.Visit(exp);
  53. }
  54. public override Expression Visit(Expression exp)
  55. {
  56. if (exp == null)
  57. {
  58. return null;
  59. }
  60. if (this.candidates.Contains(exp))
  61. {
  62. return this.Evaluate(exp);
  63. }
  64. return base.Visit(exp);
  65. }
  66. private Expression Evaluate(Expression e)
  67. {
  68. if (e.NodeType == ExpressionType.Constant)
  69. {
  70. return e;
  71. }
  72. LambdaExpression lambda = Expression.Lambda(e);
  73. Delegate fn = lambda.Compile();
  74. return Expression.Constant(fn.DynamicInvoke(null), e.Type);
  75. }
  76. }
  77. /// <summary>
  78. /// Performs bottom-up analysis to determine which nodes can possibly
  79. /// be part of an evaluated sub-tree.
  80. /// </summary>
  81. class Nominator : ExpressionVisitor
  82. {
  83. Func<Expression, bool> fnCanBeEvaluated;
  84. HashSet<Expression> candidates;
  85. bool cannotBeEvaluated;
  86. internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
  87. {
  88. this.fnCanBeEvaluated = fnCanBeEvaluated;
  89. }
  90. internal HashSet<Expression> Nominate(Expression expression)
  91. {
  92. this.candidates = new HashSet<Expression>();
  93. this.Visit(expression);
  94. return this.candidates;
  95. }
  96. public override Expression Visit(Expression expression)
  97. {
  98. if (expression != null)
  99. {
  100. bool saveCannotBeEvaluated = this.cannotBeEvaluated;
  101. this.cannotBeEvaluated = false;
  102. base.Visit(expression);
  103. if (!this.cannotBeEvaluated)
  104. {
  105. if (this.fnCanBeEvaluated(expression))
  106. {
  107. this.candidates.Add(expression);
  108. }
  109. else
  110. {
  111. this.cannotBeEvaluated = true;
  112. }
  113. }
  114. this.cannotBeEvaluated |= saveCannotBeEvaluated;
  115. }
  116. return expression;
  117. }
  118. }
  119. }
  120. }