Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.DataSetExtensions / System / Data / EnumerableRowCollection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="GenericEnumRowCollection.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;
10 using System.Collections.Generic;
11 using System.Collections;
12 using System.Text;
13 using System.Data;
14 using System.Linq;
15 using System.Diagnostics;
16 using System.Linq.Expressions;
17 using System.Collections.ObjectModel;
18 using System.Data.DataSetExtensions;
19
20 namespace System.Data
21 {
22     /// <summary>
23     /// Provides an entry point so that Cast operator call can be intercepted within an extension method.
24     /// </summary>
25     public abstract class EnumerableRowCollection : IEnumerable
26     {
27         internal abstract Type ElementType { get; }
28         internal abstract DataTable Table { get; }
29
30         internal EnumerableRowCollection()
31         {
32         }
33
34         IEnumerator IEnumerable.GetEnumerator()
35         {
36             return null;
37         }
38     }
39
40     /// <summary>
41     /// This class provides a wrapper for DataTables to allow for querying via LINQ.
42     /// </summary>
43     public class EnumerableRowCollection<TRow> : EnumerableRowCollection, IEnumerable<TRow>
44     {
45         private readonly DataTable _table;
46         private readonly IEnumerable<TRow> _enumerableRows;
47         private readonly List<Func<TRow, bool>> _listOfPredicates;
48
49         // Stores list of sort expression in the order provided by user. E.g. order by, thenby, thenby descending..
50         private readonly SortExpressionBuilder<TRow> _sortExpression;
51
52         private readonly Func<TRow, TRow> _selector;
53
54         #region Properties
55
56         internal override Type ElementType
57         {
58             get
59             {
60                 return typeof(TRow);
61             }
62
63         }
64
65         internal IEnumerable<TRow> EnumerableRows
66         {
67             get
68             {
69                 return _enumerableRows;
70             }
71         }
72
73         internal override DataTable Table
74         {
75             get
76             {
77                 return _table;
78             }
79         }
80
81
82         #endregion Properties
83
84         #region Constructors
85
86         /// <summary>
87         /// This constructor is used when Select operator is called with output Type other than input row Type.
88         /// Basically fail on GetLDV(), but other LINQ operators must work.
89         /// </summary>
90         internal EnumerableRowCollection(IEnumerable<TRow> enumerableRows, bool isDataViewable, DataTable table)
91         {
92             Debug.Assert(!isDataViewable || table != null, "isDataViewable bug table is null");
93
94             _enumerableRows = enumerableRows;
95             if (isDataViewable)
96             {
97                 _table = table;
98             }
99             _listOfPredicates = new List<Func<TRow, bool>>();
100             _sortExpression = new SortExpressionBuilder<TRow>();
101         }
102
103         /// <summary>
104         /// Basic Constructor
105         /// </summary>
106         internal EnumerableRowCollection(DataTable table)
107         {
108             _table = table;
109             _enumerableRows = table.Rows.Cast<TRow>();
110             _listOfPredicates = new List<Func<TRow, bool>>();
111             _sortExpression = new SortExpressionBuilder<TRow>();
112         }
113
114         /// <summary>
115         /// Copy Constructor that sets the input IEnumerable as enumerableRows
116         /// Used to maintain IEnumerable that has linq operators executed in the same order as the user
117         /// </summary>
118         internal EnumerableRowCollection(EnumerableRowCollection<TRow> source, IEnumerable<TRow> enumerableRows, Func<TRow, TRow> selector)
119         {
120             Debug.Assert(null != enumerableRows, "null enumerableRows");
121
122             _enumerableRows = enumerableRows;
123             _selector = selector;
124             if (null != source)
125             {
126                 if (null == source._selector)
127                 {
128                     _table = source._table;
129                 }
130                 _listOfPredicates = new List<Func<TRow, bool>>(source._listOfPredicates);
131                 _sortExpression = source._sortExpression.Clone(); //deep copy the List
132             }
133             else
134             {
135                 _listOfPredicates = new List<Func<TRow, bool>>();
136                 _sortExpression = new SortExpressionBuilder<TRow>();
137             }
138         }
139
140         #endregion Constructors
141
142         #region PublicInterface
143         IEnumerator IEnumerable.GetEnumerator()
144         {
145             return GetEnumerator();
146         }
147
148         /// <summary>
149         ///  This method returns an strongly typed iterator
150         ///  for the underlying DataRow collection.
151         /// </summary>
152         /// <returns>
153         ///   A strongly typed iterator.
154         /// </returns>
155         public IEnumerator<TRow> GetEnumerator()
156         {
157             return _enumerableRows.GetEnumerator();
158         }
159         #endregion PublicInterface
160
161         /// <summary>
162         /// Evaluates filter and sort if necessary and returns
163         /// a LinqDataView representing the LINQ query this class has collected.
164         /// </summary>
165         /// <returns>LinqDataView repesenting the LINQ query</returns>
166         internal LinqDataView GetLinqDataView() //Called by AsLinqDataView
167         {
168             if ((null == _table) || !typeof(DataRow).IsAssignableFrom(typeof(TRow)))
169             {
170                 throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
171             }
172
173             LinqDataView view = null;
174
175             #region BuildSinglePredicate
176
177             Func<DataRow, bool> finalPredicate = null; //Conjunction of all .Where(..) predicates
178             if ((null != _selector) && (0 < _listOfPredicates.Count))
179             {
180                 // Hook up all individual predicates into one predicate
181                 // This delegate is a conjunction of multiple predicates set by the user
182                 // Note: This is a Short-Circuit Conjunction
183                 finalPredicate =
184                     delegate(DataRow row)
185                     {
186                         if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
187                         {
188                             throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
189                         }
190                         foreach (Func<TRow, bool> pred in _listOfPredicates)
191                         {
192                             if (!pred((TRow)(object)row))
193                             {
194                                 return false;
195                             }
196                         }
197                         return true;
198                     };
199             }
200             else if (null != _selector)
201             {
202                 finalPredicate =
203                     delegate(DataRow row)
204                     {
205                         if (!Object.ReferenceEquals(row, _selector((TRow)(object)row)))
206                         {
207                             throw DataSetUtil.NotSupported(Strings.ToLDVUnsupported);
208                         }
209                         return true;
210                     };
211             }
212             else if (0 < _listOfPredicates.Count)
213             {
214                 finalPredicate =
215                     delegate(DataRow row)
216                     {
217                         foreach (Func<TRow, bool> pred in _listOfPredicates)
218                         {
219                             if (!pred((TRow)(object)row))
220                             {
221                                 return false;
222                             }
223                         }
224                         return true;
225                     };
226             }
227             #endregion BuildSinglePredicate
228
229             #region Evaluate Filter/Sort
230             //  All of this mess below is because we want to create index only once.
231             //
232             //  If we only have filter, we set _view.Predicate       - 1 index creation
233             //  If we only have sort, we set _view.SortExpression()  - 1 index creation
234             //  If we have BOTH, we set them through the constructor - 1 index creation
235             //
236
237             // Filter AND Sort
238             if ((null != finalPredicate) && (0 < _sortExpression.Count))
239             {
240                 // A lot more work here because constructor does not know type K,
241                 // so the responsibility to create appropriate delegate comparers
242                 // is outside of the constructor.
243
244                 view = new LinqDataView(
245                                _table,
246                                finalPredicate,                      //Func() Predicate
247                                delegate(DataRow row)                //System.Predicate
248                                {
249                                    return finalPredicate(row);
250                                },
251                                delegate(DataRow a, DataRow b)       //Comparison for DV for Index creation
252                                {
253                                    return _sortExpression.Compare(
254                                             _sortExpression.Select((TRow)(object)a),
255                                             _sortExpression.Select((TRow)(object)b)
256                                        );
257                                },
258                                delegate(object key, DataRow row)    //Comparison_K_T for DV's Find()
259                                {
260                                    return _sortExpression.Compare(
261                                         (List<object>)key,
262                                         _sortExpression.Select((TRow)(object)row)
263                                       );
264                                },
265                                 _sortExpression.CloneCast<DataRow>());
266             }
267             else if (null != finalPredicate)
268             {
269                 //Only Filtering
270                 view = new LinqDataView(
271                                     _table,
272                                     finalPredicate,
273                                     delegate(DataRow row)                //System.Predicate
274                                     {
275                                         return finalPredicate(row);
276                                     },
277                                     null,
278                                     null,
279                                     _sortExpression.CloneCast<DataRow>());
280             }
281             else if (0 < _sortExpression.Count)
282             {
283                 //Only Sorting
284                 view = new LinqDataView(
285                             _table,
286                             null,
287                             null,
288                             delegate(DataRow a, DataRow b)
289                             {
290                                 return _sortExpression.Compare(_sortExpression.Select((TRow)(object)a), _sortExpression.Select((TRow)(object)b));
291                             },
292                             delegate(object key, DataRow row)
293                             {
294                                 return _sortExpression.Compare((List<object>)key, _sortExpression.Select((TRow)(object)row));
295                             },
296                             _sortExpression.CloneCast<DataRow>());
297             }
298             else
299             {
300                 view = new LinqDataView(_table, _sortExpression.CloneCast<DataRow>());
301             }
302             #endregion Evaluate Filter and Sort
303
304             return view;
305         }
306
307
308         #region Add Single Filter/Sort Expression
309
310         /// <summary>
311         /// Used to add a filter predicate.
312         /// A conjunction of all predicates are evaluated in LinqDataView
313         /// </summary>
314         internal void AddPredicate(Func<TRow, bool> pred)
315         {
316             Debug.Assert(pred != null);
317             _listOfPredicates.Add(pred);
318         }
319
320         /// <summary>
321         /// Adds a sort expression when Keyselector is provided but not Comparer
322         /// </summary>
323         internal void AddSortExpression<TKey>(Func<TRow, TKey> keySelector, bool isDescending, bool isOrderBy)
324         {
325             AddSortExpression<TKey>(keySelector, Comparer<TKey>.Default, isDescending, isOrderBy);
326         }
327
328         /// <summary>
329         /// Adds a sort expression when Keyselector and Comparer are provided.
330         /// </summary>
331         internal void AddSortExpression<TKey>(
332                             Func<TRow, TKey> keySelector,
333                             IComparer<TKey> comparer,
334                             bool isDescending,
335                             bool isOrderBy)
336         {
337             DataSetUtil.CheckArgumentNull(keySelector, "keySelector");
338             DataSetUtil.CheckArgumentNull(comparer, "comparer");
339
340             _sortExpression.Add(
341                     delegate(TRow input)
342                     {
343                         return (object)keySelector(input);
344                     },
345                     delegate(object val1, object val2)
346                     {
347                         return (isDescending ? -1 : 1) * comparer.Compare((TKey)val1, (TKey)val2);
348                     },
349                       isOrderBy);
350         }
351
352         #endregion Add Single Filter/Sort Expression
353
354     }
355
356 }