Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / CommandTrees / Internal / DbExpressionRules.cs
1 //---------------------------------------------------------------------
2 // <copyright file="DbExpressionRules.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System.Data.Common.CommandTrees;
11 using System.Collections.Generic;
12 using System.Data.Metadata.Edm;
13 using System.Diagnostics;
14 using System.Data.Common.Utils;
15 using System.Linq;
16 using System.Globalization;
17 using System.Data.Common.CommandTrees.ExpressionBuilder;
18
19 namespace System.Data.Common.CommandTrees.Internal
20 {
21     /// <summary>
22     /// Enacapsulates the logic that defines an expression 'rule' which is capable of transforming a candidate <see cref="DbExpression"/>
23     /// into a result DbExpression, and indicating what action should be taken on that result expression by the rule application logic.
24     /// </summary>
25     internal abstract class DbExpressionRule
26     {
27         /// <summary>
28         /// Indicates what action the rule processor should take if the rule successfully processes an expression.
29         /// </summary>
30         internal enum ProcessedAction
31         {
32             /// <summary>
33             /// Continue to apply rules, from the rule immediately following this rule, to the result expression
34             /// </summary>
35             Continue = 0,
36
37             /// <summary>
38             /// Going back to the first rule, apply all rules to the result expression
39             /// </summary>
40             Reset,
41
42             /// <summary>
43             /// Stop all rule processing and return the result expression as the final result expression
44             /// </summary>
45             Stop
46         }
47
48         /// <summary>
49         /// Indicates whether <see cref="TryProcess"/> should be called on the specified argument expression.
50         /// </summary>
51         /// <param name="expression">The <see cref="DbExpression"/> that the rule should inspect and determine if processing is possible</param>
52         /// <returns><c>true</c> if the rule can attempt processing of the expression via the <see cref="TryProcess"/> method; otherwise <c>false</c></returns>
53         internal abstract bool ShouldProcess(DbExpression expression);
54         
55         /// <summary>
56         /// Attempts to process the input <paramref name="expression"/> to produce a <paramref name="result"/> <see cref="DbExpression"/>.
57         /// </summary>
58         /// <param name="expression">The input expression that the rule should process</param>
59         /// <param name="result">The result expression produced by the rule if processing was successful</param>
60         /// <returns><c>true</c> if the rule was able to successfully process the input expression and produce a result expression; otherwise <c>false</c></returns>
61         internal abstract bool TryProcess(DbExpression expression, out DbExpression result);
62         
63         /// <summary>
64         /// Indicates what action - as a <see cref="ProcessedAction"/> value - the rule processor should take if <see cref="TryProcess"/> returns true.
65         /// </summary>
66         internal abstract ProcessedAction OnExpressionProcessed { get; }
67     }
68
69     /// <summary>
70     /// Abstract base class for a DbExpression visitor that can apply a collection of <see cref="DbExpressionRule"/>s during the visitor pass, returning the final result expression.
71     /// This class encapsulates the rule application logic that applies regardless of how the ruleset - modelled as the abstract <see cref="GetRules"/> method - is provided.
72     /// </summary>
73     internal abstract class DbExpressionRuleProcessingVisitor : DefaultExpressionVisitor
74     {
75         protected DbExpressionRuleProcessingVisitor() { }
76
77         protected abstract IEnumerable<DbExpressionRule> GetRules();
78
79         private static Tuple<DbExpression, DbExpressionRule.ProcessedAction> ProcessRules(DbExpression expression, List<DbExpressionRule> rules)
80         {
81             // Considering each rule in the rule set in turn, if the rule indicates that it can process the
82             // input expression, call TryProcess to attempt processing. If successful, take the action specified
83             // by the rule's OnExpressionProcessed action, which may involve returning the action and the result
84             // expression so that processing can be reset or halted.
85
86             for (int idx = 0; idx < rules.Count; idx++)
87             {
88                 DbExpressionRule currentRule = rules[idx];
89                 if (currentRule.ShouldProcess(expression))
90                 {
91                     DbExpression result;
92                     if (currentRule.TryProcess(expression, out result))
93                     {
94                         if (currentRule.OnExpressionProcessed != DbExpressionRule.ProcessedAction.Continue)
95                         {
96                             return Tuple.Create(result, currentRule.OnExpressionProcessed);
97                         }
98                         else
99                         {
100                             expression = result;
101                         }
102                     }
103                 }
104             }
105             return Tuple.Create(expression, DbExpressionRule.ProcessedAction.Continue);
106         }
107
108         private bool _stopped;
109
110         private DbExpression ApplyRules(DbExpression expression)
111         {
112             // Driver loop to apply rules while the status of processing is 'Reset',
113             // or correctly set the _stopped flag if status is 'Stopped'.
114
115             List<DbExpressionRule> currentRules = this.GetRules().ToList();
116             var ruleResult = ProcessRules(expression, currentRules);
117             while (ruleResult.Item2 == DbExpressionRule.ProcessedAction.Reset)
118             {
119                 currentRules = this.GetRules().ToList();
120                 ruleResult = ProcessRules(ruleResult.Item1, currentRules);
121             }
122             if (ruleResult.Item2 == DbExpressionRule.ProcessedAction.Stop)
123             {
124                 _stopped = true;
125             }
126             return ruleResult.Item1;
127         }
128
129         protected override DbExpression VisitExpression(DbExpression expression)
130         {
131             // Pre-process this visitor's rules
132             DbExpression result = ApplyRules(expression);
133             if (_stopped)
134             {
135                 // If rule processing was stopped, the result expression must be returned immediately
136                 return result;
137             }
138
139             // Visit the expression to recursively apply rules to subexpressions
140             result = base.VisitExpression(result);
141             if (_stopped)
142             {
143                 // If rule processing was stopped, the result expression must be returned immediately
144                 return result;
145             }
146
147             // Post-process the rules over the resulting expression and return the result.
148             // This is done so that rules that did not match the original structure of the
149             // expression have an opportunity to examine the structure of the result expression.
150             result = ApplyRules(result);
151             return result;
152         }
153     }
154 }