| File: System\Data\SortExpressionBuilder.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Data { /// <summary> /// This class represents a combined sort expression build using multiple sort expressions. /// </summary> /// <typeparam name="T"></typeparam> internal sealed class SortExpressionBuilder<T> : IComparer<List<object>> { /** * This class ensures multiple orderby/thenbys are handled correctly. Its semantics is as follows: * * Query 1: * orderby a * thenby b * orderby c * orderby d * thenby e * * is equivalent to: * * Query 2: * orderby d * thenby e * thenby c * thenby a * thenby b * **/ // Selectors and comparers are mapped using the index in the list. // E.g: _comparers[i] is used with _selectors[i] private readonly LinkedList<Func<T, object>> _selectors = new LinkedList<Func<T, object>>(); private readonly LinkedList<Comparison<object>> _comparers = new LinkedList<Comparison<object>>(); private LinkedListNode<Func<T, object>>? _currentSelector; private LinkedListNode<Comparison<object>>? _currentComparer; /// <summary> /// Adds a sorting selector/comparer in the correct order /// </summary> internal void Add(Func<T, object> keySelector, Comparison<object> compare, bool isOrderBy) { Debug.Assert(keySelector != null); Debug.Assert(compare != null); // Inputs are assumed to be valid. The burden for ensuring it is on the caller. if (isOrderBy) { _currentSelector = _selectors.AddFirst(keySelector); _currentComparer = _comparers.AddFirst(compare); } else { // ThenBy can only be called after OrderBy Debug.Assert(_currentSelector != null); Debug.Assert(_currentComparer != null); _currentSelector = _selectors.AddAfter(_currentSelector, keySelector); _currentComparer = _comparers.AddAfter(_currentComparer, compare); } } /// <summary> /// Represents a Combined selector of all selectors added thus far. /// </summary> /// <returns>List of 'objects returned by each selector'. This list is the combined-selector</returns> public List<object> Select(T row) { List<object> result = new List<object>(); foreach (Func<T, object> selector in _selectors) { result.Add(selector(row)); } return result; } /// <summary> /// Represents a Comparer (of IComparer) that compares two combined-selectors using /// provided comparers for each individual selector. /// Note: Comparison is done in the order it was Added. /// </summary> /// <returns>Comparison result of the combined Sort comparer expression</returns> public int Compare(List<object>? a, List<object>? b) { Debug.Assert(a != null && b != null && a.Count == Count); int i = 0; foreach (Comparison<object> compare in _comparers) { int result = compare(a[i], b[i]); if (result != 0) { return result; } i++; } return 0; } internal int Count { get { //weak now that we have two dimensions Debug.Assert(_selectors.Count == _comparers.Count); return _selectors.Count; } } /// <summary> /// Clones the SortexpressionBuilder and returns a new object /// that points to same comparer and selectors (in the same order). /// </summary> internal SortExpressionBuilder<T> Clone() { SortExpressionBuilder<T> builder = new SortExpressionBuilder<T>(); foreach (Func<T, object> selector in _selectors) { if (selector == _currentSelector!.Value) { builder._currentSelector = builder._selectors.AddLast(selector); } else { builder._selectors.AddLast(selector); } } foreach (Comparison<object> comparer in _comparers) { if (comparer == _currentComparer!.Value) { builder._currentComparer = builder._comparers.AddLast(comparer); } else { builder._comparers.AddLast(comparer); } } return builder; } /// <summary> /// Clones the SortExpressinBuilder and casts to type TResult. /// </summary> internal SortExpressionBuilder<TResult> CloneCast<TResult>() { SortExpressionBuilder<TResult> builder = new SortExpressionBuilder<TResult>(); foreach (Func<T, object> selector in _selectors) { if (selector == _currentSelector!.Value) { builder._currentSelector = builder._selectors.AddLast(r => selector((T)(object)r!)); } else { builder._selectors.AddLast(r => selector((T)(object)r!)); } } foreach (Comparison<object> comparer in _comparers) { if (comparer == _currentComparer!.Value) { builder._currentComparer = builder._comparers.AddLast(comparer); } else { builder._comparers.AddLast(comparer); } } return builder; } } }