2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Utils / CollectionExtensions.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Microsoft Public License. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Microsoft Public License, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
21 using System.Runtime.CompilerServices;
22 #if !CODEPLEX_40
23 using Microsoft.Runtime.CompilerServices;
24 #endif
25
26
27 #if CODEPLEX_40
28 namespace System.Dynamic.Utils {
29 #else
30 namespace Microsoft.Scripting.Utils {
31 #endif
32     internal static class CollectionExtensions {
33         /// <summary>
34         /// Wraps the provided enumerable into a ReadOnlyCollection{T}
35         /// 
36         /// Copies all of the data into a new array, so the data can't be
37         /// changed after creation. The exception is if the enumerable is
38         /// already a ReadOnlyCollection{T}, in which case we just return it.
39         /// </summary>
40         internal static ReadOnlyCollection<T> ToReadOnly<T>(this IEnumerable<T> enumerable) {
41             if (enumerable == null) {
42                 return EmptyReadOnlyCollection<T>.Instance;
43             }
44
45             var troc = enumerable as TrueReadOnlyCollection<T>;
46             if (troc != null) {
47                 return troc;
48             }
49
50             var builder = enumerable as ReadOnlyCollectionBuilder<T>;
51             if (builder != null) {
52                 return builder.ToReadOnlyCollection();
53             }
54
55             var collection = enumerable as ICollection<T>;
56             if (collection != null) {
57                 int count = collection.Count;
58                 if (count == 0) {
59                     return EmptyReadOnlyCollection<T>.Instance;
60                 }
61
62                 T[] clone = new T[count];
63                 collection.CopyTo(clone, 0);
64                 return new TrueReadOnlyCollection<T>(clone);
65             }
66
67             // ToArray trims the excess space and speeds up access
68             return new TrueReadOnlyCollection<T>(new List<T>(enumerable).ToArray());
69         }
70
71         // We could probably improve the hashing here
72         internal static int ListHashCode<T>(this IEnumerable<T> list) {
73             var cmp = EqualityComparer<T>.Default;
74             int h = 6551;
75             foreach (T t in list) {
76                 h ^= (h << 5) ^ cmp.GetHashCode(t);
77             }
78             return h;
79         }
80
81         internal static bool ListEquals<T>(this ICollection<T> first, ICollection<T> second) {
82             if (first.Count != second.Count) {
83                 return false;
84             }
85             var cmp = EqualityComparer<T>.Default;
86             var f = first.GetEnumerator();
87             var s = second.GetEnumerator();
88             while (f.MoveNext()) {
89                 s.MoveNext();
90
91                 if (!cmp.Equals(f.Current, s.Current)) {
92                     return false;
93                 }
94             }
95             return true;
96         }
97
98         internal static IEnumerable<U> Select<T, U>(this IEnumerable<T> enumerable, Func<T, U> select) {
99             foreach (T t in enumerable) {
100                 yield return select(t);
101             }
102         }
103
104         // Name needs to be different so it doesn't conflict with Enumerable.Select
105         internal static U[] Map<T, U>(this ICollection<T> collection, Func<T, U> select) {
106             int count = collection.Count;
107             U[] result = new U[count];
108             count = 0;
109             foreach (T t in collection) {
110                 result[count++] = select(t);
111             }
112             return result;
113         }
114
115         internal static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> where) {
116             foreach (T t in enumerable) {
117                 if (where(t)) {
118                     yield return t;
119                 }
120             }
121         }
122
123         internal static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
124             foreach (T element in source) {
125                 if (predicate(element)) {
126                     return true;
127                 }
128             }
129             return false;
130         }
131
132         internal static bool All<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
133             foreach (T element in source) {
134                 if (!predicate(element)) {
135                     return false;
136                 }
137             }
138             return true;
139         }
140
141         internal static T[] RemoveFirst<T>(this T[] array) {
142             T[] result = new T[array.Length - 1];
143             Array.Copy(array, 1, result, 0, result.Length);
144             return result;
145         }
146
147         internal static T[] RemoveLast<T>(this T[] array) {
148             T[] result = new T[array.Length - 1];
149             Array.Copy(array, 0, result, 0, result.Length);
150             return result;
151         }
152
153         internal static T[] AddFirst<T>(this IList<T> list, T item) {
154             T[] res = new T[list.Count + 1];
155             res[0] = item;
156             list.CopyTo(res, 1);
157             return res;
158         }
159
160         internal static T[] AddLast<T>(this IList<T> list, T item) {
161             T[] res = new T[list.Count + 1];
162             list.CopyTo(res, 0);
163             res[list.Count] = item;
164             return res;
165         }
166
167         internal static T First<T>(this IEnumerable<T> source) {
168             var list = source as IList<T>;
169             if (list != null) {
170                 return list[0];
171             }
172             using (var e = source.GetEnumerator()) {
173                 if (e.MoveNext()) return e.Current;
174             }
175             throw new InvalidOperationException();
176         }
177
178         internal static T Last<T>(this IList<T> list) {
179             return list[list.Count - 1];
180         }
181
182         internal static T[] Copy<T>(this T[] array) {
183             T[] copy = new T[array.Length];
184             Array.Copy(array, copy, array.Length);
185             return copy;
186         }
187     }
188
189
190     internal static class EmptyReadOnlyCollection<T> {
191         internal static ReadOnlyCollection<T> Instance = new TrueReadOnlyCollection<T>(new T[0]);
192     }
193 }