Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Internal / Materialization / Coordinator.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="Coordinator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Linq;
12 using System.Text;
13 using System.Globalization;
14 using System.Data.Objects.Internal;
15
16 namespace System.Data.Common.Internal.Materialization
17 {
18
19     /// <summary>
20     /// A coordinator is responsible for tracking state and processing result in a root or nested query
21     /// result collection. The coordinator exists within a graph, and knows its Parent, (First)Child,
22     /// and Next sibling. This allows the Shaper to use the coordinator as a simple state machine when
23     /// consuming store reader results.
24     /// </summary>
25     internal abstract class Coordinator
26     {
27         #region state
28
29         /// <summary>
30         /// The factory used to generate this coordinator instance. Contains delegates used
31         /// by the Shaper during result enumeration.
32         /// </summary>
33         internal readonly CoordinatorFactory CoordinatorFactory;
34
35         /// <summary>
36         /// Parent coordinator (the coordinator producing rows containing this collection).
37         /// If this is the root, null.
38         /// </summary>
39         internal readonly Coordinator Parent;
40
41         /// <summary>
42         /// First coordinator for nested results below this collection. When reading a new row
43         /// for this coordinator, we walk down to the Child.
44         /// 
45         /// NOTE:: this cannot be readonly because we can't know both the parent and the child
46         /// at initialization time; we set the Child in the parent's constructor.
47         /// </summary>
48         public Coordinator Child
49         {
50             get { return _child; }
51             protected set { _child = value; }
52         }
53         private Coordinator _child;
54
55         /// <summary>
56         /// Next coordinator at this depth. Once we're done consuming results for this reader,
57         /// we move on to this.Next.
58         /// </summary>
59         internal readonly Coordinator Next;
60
61         /// <summary>
62         /// Indicates whether data has been read for the collection being aggregated or yielded
63         /// by this coordinator.
64         /// </summary> 
65         public bool IsEntered
66         {
67             get { return _isEntered; }
68             protected set { _isEntered = value; }
69         }
70         private bool _isEntered;
71
72         /// <summary>
73         /// Indicates whether this is the top level coordinator for a query.
74         /// </summary>
75         internal bool IsRoot
76         {
77             get { return null == Parent; }
78         }
79
80         #endregion
81
82         #region constructor
83
84         protected Coordinator(CoordinatorFactory coordinatorFactory, Coordinator parent, Coordinator next)
85         {
86             this.CoordinatorFactory = coordinatorFactory;
87             this.Parent = parent;
88             this.Next = next;
89         }
90
91         #endregion
92
93         #region "public" surface area
94
95         /// <summary>
96         /// Registers this hierarchy of coordinators in the given shaper.
97         /// </summary>
98         internal void Initialize(Shaper shaper)
99         {
100             ResetCollection(shaper);
101
102             // Add this coordinator to the appropriate state slot in the 
103             // shaper so that it is available to materialization delegates.
104             shaper.State[this.CoordinatorFactory.StateSlot] = this;
105             
106             if (null != this.Child)
107             {
108                 this.Child.Initialize(shaper);
109             }
110             if (null != this.Next)
111             {
112                 this.Next.Initialize(shaper);
113             }
114         }
115
116         /// <summary>
117         /// Determines the maximum depth of this subtree.
118         /// </summary>
119         internal int MaxDistanceToLeaf()
120         {
121             int maxDistance = 0;
122             Coordinator child = this.Child;
123             while (null != child)
124             {
125                 maxDistance = Math.Max(maxDistance, child.MaxDistanceToLeaf() + 1);
126                 child = child.Next;
127             }
128             return maxDistance;
129         }
130
131         /// <summary>
132         /// This method is called when the current collection is finished and it's time to move to the next collection.
133         /// Recursively initializes children and siblings as well.
134         /// </summary>
135         internal abstract void ResetCollection(Shaper shaper);
136
137         /// <summary>
138         /// Precondition: the current row has data for the coordinator.
139         /// Side-effects: updates keys currently stored in state and updates IsEntered if a new value is encountered.
140         /// Determines whether the row contains the next element in this collection.
141         /// </summary>
142         internal bool HasNextElement(Shaper shaper)
143         {
144             // check if this row contains a new element for this coordinator
145             bool result = false;
146             
147             if (!this.IsEntered || !this.CoordinatorFactory.CheckKeys(shaper))
148             {
149                 // remember initial keys values
150                 this.CoordinatorFactory.SetKeys(shaper);
151                 this.IsEntered = true;
152                 result = true;
153             }
154
155             return result;
156         }
157
158         /// <summary>
159         /// Precondition: the current row has data and contains a new element for the coordinator.
160         /// Reads the next element in this collection.
161         /// </summary>
162         internal abstract void ReadNextElement(Shaper shaper);
163
164         #endregion
165     }
166
167     /// <summary>
168     /// Typed <see cref="Coordinator"/>
169     /// </summary>
170     internal class Coordinator<T> : Coordinator
171     {
172         #region state
173
174         internal readonly CoordinatorFactory<T> TypedCoordinatorFactory;
175
176         /// <summary>
177         /// Exposes the Current element that has been materialized (and is being populated) by this coordinator.
178         /// </summary>
179         internal T Current
180         {
181             get { return _current; }
182         }
183         private T _current;
184
185         /// <summary>
186         /// For ObjectResult, aggregates all elements for in the nested collection handled by this coordinator.
187         /// </summary>
188         private ICollection<T> _elements;
189
190         /// <summary>
191         /// For ObjectResult, aggregates all elements as wrapped entities for in the nested collection handled by this coordinator.
192         /// </summary>
193         private List<IEntityWrapper> _wrappedElements;
194
195         /// <summary>
196         /// Delegate called when the current nested collection has been consumed. This is necessary in Span
197         /// scenarios where an EntityCollection RelatedEnd is populated only when all related entities have
198         /// been materialized.  This version of the close handler works with wrapped entities.
199         /// </summary>
200         private Action<Shaper, List<IEntityWrapper>> _handleClose;
201
202         /// <summary>
203         /// For nested, object-layer coordinators we want to collect all the elements we find and handle them
204         /// when the root coordinator advances.  Otherwise we just want to return them as we find them.
205         /// </summary>
206         private readonly bool IsUsingElementCollection;
207
208         #endregion
209
210         #region constructors
211
212         internal Coordinator(CoordinatorFactory<T> coordinator, Coordinator parent, Coordinator next)
213             : base(coordinator, parent, next)
214         {
215             this.TypedCoordinatorFactory = coordinator;
216
217             // generate all children
218             Coordinator nextChild = null;
219             foreach (var nestedCoordinator in coordinator.NestedCoordinators.Reverse())
220             {
221                 // last child processed is first child...
222                 this.Child = nestedCoordinator.CreateCoordinator(this, nextChild);
223                 nextChild = this.Child;
224             }
225
226             this.IsUsingElementCollection = (!this.IsRoot && typeof(T) != typeof(RecordState));
227         }
228
229         #endregion
230
231         #region "public" surface area
232
233         internal override void ResetCollection(Shaper shaper)
234         {
235             // Check to see if anyone has registered for notification when the current coordinator
236             // is reset.
237             if (null != _handleClose)
238             {
239                 _handleClose(shaper, _wrappedElements);
240                 _handleClose = null;
241             }
242
243             // Reset is entered for this collection.
244             this.IsEntered = false;
245
246             if (IsUsingElementCollection)
247             {
248                 _elements = this.TypedCoordinatorFactory.InitializeCollection(shaper);
249                 _wrappedElements = new List<IEntityWrapper>();
250             }
251
252             if (null != this.Child)
253             {
254                 this.Child.ResetCollection(shaper);
255             }
256             if (null != this.Next)
257             {
258                 this.Next.ResetCollection(shaper);
259             }
260         }
261
262         internal override void ReadNextElement(Shaper shaper)
263         {
264             T element;
265             IEntityWrapper wrappedElement = null;
266             try
267             {
268                 if (this.TypedCoordinatorFactory.WrappedElement == null)
269                 {
270                     element = this.TypedCoordinatorFactory.Element(shaper);
271                 }
272                 else
273                 {
274                     wrappedElement = this.TypedCoordinatorFactory.WrappedElement(shaper);
275                     // This cast may throw, in which case it will be immediately caught
276                     // and the error handling expression will be used to get the appropriate error message.
277                     element = (T)wrappedElement.Entity;
278                 }
279             }
280             catch (Exception e)
281             {
282                 if (EntityUtil.IsCatchableExceptionType(e))
283                 {
284                     // Some errors can occur while a close handler is registered.  This clears
285                     // out the handler so that ElementWithErrorHandling will report the correct
286                     // error rather than asserting on the missing close handler.
287                     ResetCollection(shaper);
288                     // call a variation of the "Element" delegate with more detailed
289                     // error handling (to produce a better exception message)
290                     element = this.TypedCoordinatorFactory.ElementWithErrorHandling(shaper);
291                 }
292
293                 // rethrow
294                 throw;
295             }
296             if (IsUsingElementCollection)
297             {
298                 _elements.Add(element);
299                 if (wrappedElement != null)
300                 {
301                     _wrappedElements.Add(wrappedElement);
302                 }
303             }
304             else
305             {
306                 _current = element;
307             }
308         }
309
310         /// <summary>
311         /// Sets the delegate called when this collection is closed.  This close handler works on
312         /// a collection of wrapped entities, rather than on the raw entity objects.
313         /// </summary>
314         internal void RegisterCloseHandler(Action<Shaper, List<IEntityWrapper>> closeHandler)
315         {
316             Debug.Assert(null == _handleClose, "more than one handler for a collection close 'event'");
317             _handleClose = closeHandler;
318         }
319
320         /// <summary>
321         /// Called when we're disposing the enumerator;         
322         /// </summary>
323         internal void SetCurrentToDefault()
324         {
325             _current = default(T);
326         }
327
328         #endregion
329
330         #region runtime callable code
331         
332         // Code in this section is called from the delegates produced by the Translator.  It may  
333         // not show up if you search using Find All References
334
335         /// <summary>
336         /// Returns a handle to the element aggregator for this nested collection.
337         /// </summary>
338         private IEnumerable<T> GetElements()
339         {
340             return _elements;
341         }
342
343         #endregion
344     }
345 }