Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / InternalTrees / RuleProcessor.cs
blob2d43a1cb77b8012dc33fccf42ef0468e46bb463e
1 //---------------------------------------------------------------------
2 // <copyright file="RuleProcessor.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System;
11 using System.Collections.Generic;
12 using System.Collections.ObjectModel;
13 using System.Globalization;
14 using System.Diagnostics;
16 namespace System.Data.Query.InternalTrees
18 #region RuleProcessor
19 /// <summary>
20 /// The RuleProcessor helps apply a set of rules to a query tree
21 /// </summary>
22 internal class RuleProcessor
24 #region private state
25 /// <summary>
26 /// A lookup table for rules.
27 /// The lookup table is an array indexed by OpType and each entry has a list of rules.
28 /// </summary>
29 private Dictionary<SubTreeId, SubTreeId> m_processedNodeMap;
30 #endregion
32 #region constructors
33 /// <summary>
34 /// Initializes a new RuleProcessor
35 /// </summary>
36 internal RuleProcessor()
38 // Build up the accelerator tables
39 m_processedNodeMap = new Dictionary<SubTreeId, SubTreeId>();
41 #endregion
43 #region private methods
45 private static bool ApplyRulesToNode(RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules, Node currentNode, out Node newNode)
47 newNode = currentNode;
49 // Apply any pre-rule delegates
50 context.PreProcess(currentNode);
52 foreach (Rule r in rules[(int)currentNode.Op.OpType])
54 if (!r.Match(currentNode))
56 continue;
59 // Did the rule modify the subtree?
60 if (r.Apply(context, currentNode, out newNode))
62 // The node has changed; don't try to apply any more rules
63 context.PostProcess(newNode, r);
64 return true;
66 else
68 Debug.Assert(newNode == currentNode, "Liar! This rule should have returned 'true'");
72 context.PostProcess(currentNode, null);
73 return false;
76 /// <summary>
77 /// Apply rules to the current subtree in a bottom-up fashion.
78 /// </summary>
79 /// <param name="context">Current rule processing context</param>
80 /// <param name="rules">The look-up table with the rules to be applied</param>
81 /// <param name="subTreeRoot">Current subtree</param>
82 /// <param name="parent">Parent node</param>
83 /// <param name="childIndexInParent">Index of this child within the parent</param>
84 /// <returns>the result of the transformation</returns>
85 private Node ApplyRulesToSubtree(RuleProcessingContext context,
86 ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules,
87 Node subTreeRoot, Node parent, int childIndexInParent)
89 int loopCount = 0;
90 Dictionary<SubTreeId, SubTreeId> localProcessedMap = new Dictionary<SubTreeId, SubTreeId>();
91 SubTreeId subTreeId;
93 while (true)
95 // Am I looping forever
96 Debug.Assert(loopCount < 12, "endless loops?");
97 loopCount++;
100 // We may need to update state regardless of whether this subTree has
101 // changed after it has been processed last. For example, it may be
102 // affected by transformation in its siblings due to external references.
104 context.PreProcessSubTree(subTreeRoot);
105 subTreeId = new SubTreeId(context, subTreeRoot, parent, childIndexInParent);
107 // Have I seen this subtree already? Just return, if so
108 if (m_processedNodeMap.ContainsKey(subTreeId))
110 break;
113 // Avoid endless loops here - avoid cycles of 2 or more
114 if (localProcessedMap.ContainsKey(subTreeId))
116 // mark this subtree as processed
117 m_processedNodeMap[subTreeId] = subTreeId;
118 break;
120 // Keep track of this one
121 localProcessedMap[subTreeId] = subTreeId;
123 // Walk my children
124 for (int i = 0; i < subTreeRoot.Children.Count; i++)
126 subTreeRoot.Children[i] = ApplyRulesToSubtree(context, rules, subTreeRoot.Children[i], subTreeRoot, i);
129 // Apply rules to myself. If no transformations were performed,
130 // then mark this subtree as processed, and break out
131 Node newSubTreeRoot;
132 if (!ApplyRulesToNode(context, rules, subTreeRoot, out newSubTreeRoot))
134 Debug.Assert(subTreeRoot == newSubTreeRoot);
135 // mark this subtree as processed
136 m_processedNodeMap[subTreeId] = subTreeId;
137 break;
139 context.PostProcessSubTree(subTreeRoot);
140 subTreeRoot = newSubTreeRoot;
143 context.PostProcessSubTree(subTreeRoot);
144 return subTreeRoot;
146 #endregion
148 #region public methods
149 /// <summary>
150 /// Apply a set of rules to the subtree
151 /// </summary>
152 /// <param name="context">Rule processing context</param>
153 /// <param name="subTreeRoot">current subtree</param>
154 /// <returns>transformed subtree</returns>
155 internal Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules, Node subTreeRoot)
157 return ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0);
160 #endregion
162 #endregion
164 #region SubTreeId
165 internal class SubTreeId
167 #region private state
168 public Node m_subTreeRoot;
169 private int m_hashCode;
170 private Node m_parent;
171 private int m_parentHashCode;
172 private int m_childIndex;
173 #endregion
175 #region constructors
176 internal SubTreeId(RuleProcessingContext context, Node node, Node parent, int childIndex)
178 m_subTreeRoot = node;
179 m_parent = parent;
180 m_childIndex = childIndex;
181 m_hashCode = context.GetHashCode(node);
182 m_parentHashCode = parent == null ? 0 : context.GetHashCode(parent);
184 #endregion
186 #region public surface
187 public override int GetHashCode()
189 return m_hashCode;
191 public override bool Equals(object obj)
193 SubTreeId other = obj as SubTreeId;
194 return ((other != null) && (m_hashCode == other.m_hashCode) &&
195 ((other.m_subTreeRoot == this.m_subTreeRoot) ||
196 ((other.m_parent == this.m_parent) && (other.m_childIndex == this.m_childIndex))));
198 #endregion
200 #endregion
202 #region RuleProcessingContext
204 /// <summary>
205 /// Delegate that describes the processing
206 /// </summary>
207 /// <param name="context">RuleProcessing context</param>
208 /// <param name="node">Node to process</param>
209 internal delegate void OpDelegate(RuleProcessingContext context, Node node);
211 /// <summary>
212 /// A RuleProcessingContext encapsulates information needed by various rules to process
213 /// the query tree.
214 /// </summary>
215 internal abstract class RuleProcessingContext
217 #region public surface
218 internal Command Command
220 get { return m_command; }
223 /// <summary>
224 /// Callback function to be applied to a node before any rules are applied
225 /// </summary>
226 /// <param name="node">the node</param>
227 internal virtual void PreProcess(Node node)
232 /// <summary>
233 /// Callback function to be applied to the subtree rooted at the given
234 /// node before any rules are applied
235 /// </summary>
236 /// <param name="node">the node that is the root of the subtree</param>
237 internal virtual void PreProcessSubTree(Node node)
241 /// <summary>
242 /// Callback function to be applied on a node after a rule has been applied
243 /// that has modified the node
244 /// </summary>
245 /// <param name="node">current node</param>
246 /// <param name="rule">the rule that modified the node</param>
247 internal virtual void PostProcess(Node node, Rule rule)
251 /// <summary>
252 /// Callback function to be applied to the subtree rooted at the given
253 /// node after any rules are applied
254 /// </summary>
255 /// <param name="node">the node that is the root of the subtree</param>
256 internal virtual void PostProcessSubTree(Node node)
260 /// <summary>
261 /// Get the hashcode for this node - to ensure that we don't loop forever
262 /// </summary>
263 /// <param name="node">current node</param>
264 /// <returns>int hashcode</returns>
265 internal virtual int GetHashCode(Node node)
267 return node.GetHashCode();
269 #endregion
271 #region constructors
272 internal RuleProcessingContext(Command command)
274 m_command = command;
276 #endregion
278 #region private state
279 private Command m_command;
280 #endregion
282 #endregion