2 // Copyright (c) 2007 James Newton-King
4 // Permission is hereby granted, free of charge, to any person
5 // obtaining a copy of this software and associated documentation
6 // files (the "Software"), to deal in the Software without
7 // restriction, including without limitation the rights to use,
8 // copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
27 using System.Collections.Generic;
28 using System.Collections.ObjectModel;
29 using System.Reflection;
31 using System.Collections;
33 using System.Globalization;
35 namespace Newtonsoft.Json.Utilities
37 internal static class CollectionUtils
39 public static IEnumerable<T> CastValid<T>(this IEnumerable enumerable)
41 ValidationUtils.ArgumentNotNull(enumerable, "enumerable");
43 return enumerable.Cast<object>().Where(o => o is T).Cast<T>();
46 public static List<T> CreateList<T>(params T[] values)
48 return new List<T>(values);
52 /// Determines whether the collection is null or empty.
54 /// <param name="collection">The collection.</param>
56 /// <c>true</c> if the collection is null or empty; otherwise, <c>false</c>.
58 public static bool IsNullOrEmpty(ICollection collection)
60 if (collection != null)
62 return (collection.Count == 0);
68 /// Determines whether the collection is null or empty.
70 /// <param name="collection">The collection.</param>
72 /// <c>true</c> if the collection is null or empty; otherwise, <c>false</c>.
74 public static bool IsNullOrEmpty<T>(ICollection<T> collection)
76 if (collection != null)
78 return (collection.Count == 0);
84 /// Determines whether the collection is null, empty or its contents are uninitialized values.
86 /// <param name="list">The list.</param>
88 /// <c>true</c> if the collection is null or empty or its contents are uninitialized values; otherwise, <c>false</c>.
90 public static bool IsNullOrEmptyOrDefault<T>(IList<T> list)
92 if (IsNullOrEmpty<T>(list))
95 return ReflectionUtils.ItemsUnitializedValue<T>(list);
99 /// Makes a slice of the specified list in between the start and end indexes.
101 /// <param name="list">The list.</param>
102 /// <param name="start">The start index.</param>
103 /// <param name="end">The end index.</param>
104 /// <returns>A slice of the list.</returns>
105 public static IList<T> Slice<T>(IList<T> list, int? start, int? end)
107 return Slice<T>(list, start, end, null);
111 /// Makes a slice of the specified list in between the start and end indexes,
112 /// getting every so many items based upon the step.
114 /// <param name="list">The list.</param>
115 /// <param name="start">The start index.</param>
116 /// <param name="end">The end index.</param>
117 /// <param name="step">The step.</param>
118 /// <returns>A slice of the list.</returns>
119 public static IList<T> Slice<T>(IList<T> list, int? start, int? end, int? step)
122 throw new ArgumentNullException("list");
125 throw new ArgumentException("Step cannot be zero.", "step");
127 List<T> slicedList = new List<T>();
133 // set defaults for null arguments
135 int startIndex = start ?? 0;
136 int endIndex = end ?? list.Count;
138 // start from the end of the list if start is negitive
139 startIndex = (startIndex < 0) ? list.Count + startIndex : startIndex;
141 // end from the start of the list if end is negitive
142 endIndex = (endIndex < 0) ? list.Count + endIndex : endIndex;
144 // ensure indexes keep within collection bounds
145 startIndex = Math.Max(startIndex, 0);
146 endIndex = Math.Min(endIndex, list.Count - 1);
148 // loop between start and end indexes, incrementing by the step
149 for (int i = startIndex; i < endIndex; i += s)
151 slicedList.Add(list[i]);
159 /// Group the collection using a function which returns the key.
161 /// <param name="source">The source collection to group.</param>
162 /// <param name="keySelector">The key selector.</param>
163 /// <returns>A Dictionary with each key relating to a list of objects in a list grouped under it.</returns>
164 public static Dictionary<K, List<V>> GroupBy<K, V>(ICollection<V> source, Func<V, K> keySelector)
166 if (keySelector == null)
167 throw new ArgumentNullException("keySelector");
169 Dictionary<K, List<V>> groupedValues = new Dictionary<K, List<V>>();
171 foreach (V value in source)
173 // using delegate to get the value's key
174 K key = keySelector(value);
175 List<V> groupedValueList;
177 // add a list for grouped values if the key is not already in Dictionary
178 if (!groupedValues.TryGetValue(key, out groupedValueList))
180 groupedValueList = new List<V>();
181 groupedValues.Add(key, groupedValueList);
184 groupedValueList.Add(value);
187 return groupedValues;
191 /// Adds the elements of the specified collection to the specified generic IList.
193 /// <param name="initial">The list to add to.</param>
194 /// <param name="collection">The collection of elements to add.</param>
195 public static void AddRange<T>(this IList<T> initial, IEnumerable<T> collection)
198 throw new ArgumentNullException("initial");
200 if (collection == null)
203 foreach (T value in collection)
209 public static void AddRange(this IList initial, IEnumerable collection)
211 ValidationUtils.ArgumentNotNull(initial, "initial");
213 ListWrapper<object> wrapper = new ListWrapper<object>(initial);
214 wrapper.AddRange(collection.Cast<object>());
217 public static List<T> Distinct<T>(List<T> collection)
219 List<T> distinctList = new List<T>();
221 foreach (T value in collection)
223 if (!distinctList.Contains(value))
224 distinctList.Add(value);
230 public static List<List<T>> Flatten<T>(params IList<T>[] lists)
232 List<List<T>> flattened = new List<List<T>>();
233 Dictionary<int, T> currentList = new Dictionary<int, T>();
235 Recurse<T>(new List<IList<T>>(lists), 0, currentList, flattened);
240 private static void Recurse<T>(IList<IList<T>> global, int current, Dictionary<int, T> currentSet, List<List<T>> flattenedResult)
242 IList<T> currentArray = global[current];
244 for (int i = 0; i < currentArray.Count; i++)
246 currentSet[current] = currentArray[i];
248 if (current == global.Count - 1)
250 List<T> items = new List<T>();
252 for (int k = 0; k < currentSet.Count; k++)
254 items.Add(currentSet[k]);
257 flattenedResult.Add(items);
261 Recurse(global, current + 1, currentSet, flattenedResult);
266 public static List<T> CreateList<T>(ICollection collection)
268 if (collection == null)
269 throw new ArgumentNullException("collection");
271 T[] array = new T[collection.Count];
272 collection.CopyTo(array, 0);
274 return new List<T>(array);
277 public static bool ListEquals<T>(IList<T> a, IList<T> b)
279 if (a == null || b == null)
280 return (a == null && b == null);
282 if (a.Count != b.Count)
285 EqualityComparer<T> comparer = EqualityComparer<T>.Default;
287 for (int i = 0; i < a.Count; i++)
289 if (!comparer.Equals(a[i], b[i]))
296 #region GetSingleItem
297 public static bool TryGetSingleItem<T>(IList<T> list, out T value)
299 return TryGetSingleItem<T>(list, false, out value);
302 public static bool TryGetSingleItem<T>(IList<T> list, bool returnDefaultIfEmpty, out T value)
304 return MiscellaneousUtils.TryAction<T>(delegate { return GetSingleItem(list, returnDefaultIfEmpty); }, out value);
307 public static T GetSingleItem<T>(IList<T> list)
309 return GetSingleItem<T>(list, false);
312 public static T GetSingleItem<T>(IList<T> list, bool returnDefaultIfEmpty)
316 else if (returnDefaultIfEmpty && list.Count == 0)
319 throw new Exception("Expected single {0} in list but got {1}.".FormatWith(CultureInfo.InvariantCulture, typeof(T), list.Count));
323 public static IList<T> Minus<T>(IList<T> list, IList<T> minus)
325 ValidationUtils.ArgumentNotNull(list, "list");
327 List<T> result = new List<T>(list.Count);
328 foreach (T t in list)
330 if (minus == null || !minus.Contains(t))
337 public static IList CreateGenericList(Type listType)
339 ValidationUtils.ArgumentNotNull(listType, "listType");
341 return (IList)ReflectionUtils.CreateGeneric(typeof(List<>), listType);
344 public static IDictionary CreateGenericDictionary(Type keyType, Type valueType)
346 ValidationUtils.ArgumentNotNull(keyType, "keyType");
347 ValidationUtils.ArgumentNotNull(valueType, "valueType");
349 return (IDictionary)ReflectionUtils.CreateGeneric(typeof(Dictionary<,>), keyType, valueType);
352 public static bool IsListType(Type type)
354 ValidationUtils.ArgumentNotNull(type, "type");
358 if (typeof(IList).IsAssignableFrom(type))
360 if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IList<>)))
366 public static bool IsCollectionType(Type type)
368 ValidationUtils.ArgumentNotNull(type, "type");
372 if (typeof(ICollection).IsAssignableFrom(type))
374 if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(ICollection<>)))
380 public static bool IsDictionaryType(Type type)
382 ValidationUtils.ArgumentNotNull(type, "type");
384 if (typeof(IDictionary).IsAssignableFrom(type))
386 if (ReflectionUtils.ImplementsGenericDefinition(type, typeof (IDictionary<,>)))
392 public static IWrappedCollection CreateCollectionWrapper(object list)
394 ValidationUtils.ArgumentNotNull(list, "list");
396 Type collectionDefinition;
397 if (ReflectionUtils.ImplementsGenericDefinition(list.GetType(), typeof(ICollection<>), out collectionDefinition))
399 Type collectionItemType = ReflectionUtils.GetCollectionItemType(collectionDefinition);
401 // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor
402 Func<Type, IList<object>, object> instanceCreator = (t, a) =>
404 ConstructorInfo c = t.GetConstructor(new[] { collectionDefinition });
405 return c.Invoke(new[] { list });
408 return (IWrappedCollection)ReflectionUtils.CreateGeneric(typeof(CollectionWrapper<>), new[] { collectionItemType }, instanceCreator, list);
410 else if (list is IList)
412 return new CollectionWrapper<object>((IList)list);
416 throw new Exception("Can not create ListWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, list.GetType()));
419 public static IWrappedList CreateListWrapper(object list)
421 ValidationUtils.ArgumentNotNull(list, "list");
424 if (ReflectionUtils.ImplementsGenericDefinition(list.GetType(), typeof(IList<>), out listDefinition))
426 Type collectionItemType = ReflectionUtils.GetCollectionItemType(listDefinition);
428 // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor
429 Func<Type, IList<object>, object> instanceCreator = (t, a) =>
431 ConstructorInfo c = t.GetConstructor(new[] {listDefinition});
432 return c.Invoke(new[] { list });
435 return (IWrappedList)ReflectionUtils.CreateGeneric(typeof(ListWrapper<>), new[] { collectionItemType }, instanceCreator, list);
437 else if (list is IList)
439 return new ListWrapper<object>((IList)list);
443 throw new Exception("Can not create ListWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, list.GetType()));
447 public static IWrappedDictionary CreateDictionaryWrapper(object dictionary)
449 ValidationUtils.ArgumentNotNull(dictionary, "dictionary");
451 Type dictionaryDefinition;
452 if (ReflectionUtils.ImplementsGenericDefinition(dictionary.GetType(), typeof(IDictionary<,>), out dictionaryDefinition))
454 Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryDefinition);
455 Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryDefinition);
457 // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor
458 Func<Type, IList<object>, object> instanceCreator = (t, a) =>
460 ConstructorInfo c = t.GetConstructor(new[] { dictionaryDefinition });
461 return c.Invoke(new[] { dictionary });
464 return (IWrappedDictionary)ReflectionUtils.CreateGeneric(typeof(DictionaryWrapper<,>), new[] { dictionaryKeyType, dictionaryValueType }, instanceCreator, dictionary);
466 else if (dictionary is IDictionary)
468 return new DictionaryWrapper<object, object>((IDictionary)dictionary);
472 throw new Exception("Can not create DictionaryWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, dictionary.GetType()));
476 public static object CreateAndPopulateList(Type listType, Action<IList, bool> populateList)
478 ValidationUtils.ArgumentNotNull(listType, "listType");
479 ValidationUtils.ArgumentNotNull(populateList, "populateList");
483 bool isReadOnlyOrFixedSize = false;
485 if (listType.IsArray)
487 // have to use an arraylist when creating array
488 // there is no way to know the size until it is finised
489 list = new List<object>();
490 isReadOnlyOrFixedSize = true;
492 else if (ReflectionUtils.InheritsGenericDefinition(listType, typeof(ReadOnlyCollection<>), out collectionType))
494 Type readOnlyCollectionContentsType = collectionType.GetGenericArguments()[0];
495 Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), readOnlyCollectionContentsType);
496 bool suitableConstructor = false;
498 foreach (ConstructorInfo constructor in listType.GetConstructors())
500 IList<ParameterInfo> parameters = constructor.GetParameters();
502 if (parameters.Count == 1)
504 if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType))
506 suitableConstructor = true;
512 if (!suitableConstructor)
513 throw new Exception("Read-only type {0} does not have a public constructor that takes a type that implements {1}.".FormatWith(CultureInfo.InvariantCulture, listType, genericEnumerable));
515 // can't add or modify a readonly list
516 // use List<T> and convert once populated
517 list = CreateGenericList(readOnlyCollectionContentsType);
518 isReadOnlyOrFixedSize = true;
520 else if (typeof(IList).IsAssignableFrom(listType))
522 if (ReflectionUtils.IsInstantiatableType(listType))
523 list = (IList)Activator.CreateInstance(listType);
524 else if (listType == typeof(IList))
525 list = new List<object>();
529 else if (ReflectionUtils.ImplementsGenericDefinition(listType, typeof(ICollection<>)))
531 if (ReflectionUtils.IsInstantiatableType(listType))
532 list = CreateCollectionWrapper(Activator.CreateInstance(listType));
542 throw new Exception("Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, listType));
544 populateList(list, isReadOnlyOrFixedSize);
546 // create readonly and fixed sized collections using the temporary list
547 if (isReadOnlyOrFixedSize)
549 if (listType.IsArray)
550 list = ToArray(((List<object>)list).ToArray(), ReflectionUtils.GetCollectionItemType(listType));
551 else if (ReflectionUtils.InheritsGenericDefinition(listType, typeof(ReadOnlyCollection<>)))
552 list = (IList)ReflectionUtils.CreateInstance(listType, list);
554 else if (list is IWrappedCollection)
556 return ((IWrappedCollection) list).UnderlyingCollection;
562 public static Array ToArray(Array initial, Type type)
565 throw new ArgumentNullException("type");
567 Array destinationArray = Array.CreateInstance(type, initial.Length);
568 Array.Copy(initial, 0, destinationArray, 0, initial.Length);
569 return destinationArray;
572 public static bool AddDistinct<T>(this IList<T> list, T value)
574 return list.AddDistinct(value, EqualityComparer<T>.Default);
577 public static bool AddDistinct<T>(this IList<T> list, T value, IEqualityComparer<T> comparer)
579 if (list.ContainsValue(value, comparer))
586 // this is here because LINQ Bridge doesn't support Contains with IEqualityComparer<T>
587 public static bool ContainsValue<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
589 if (comparer == null)
590 comparer = EqualityComparer<TSource>.Default;
593 throw new ArgumentNullException("source");
595 foreach (TSource local in source)
597 if (comparer.Equals(local, value))
604 public static bool AddRangeDistinct<T>(this IList<T> list, IEnumerable<T> values)
606 return list.AddRangeDistinct(values, EqualityComparer<T>.Default);
609 public static bool AddRangeDistinct<T>(this IList<T> list, IEnumerable<T> values, IEqualityComparer<T> comparer)
611 bool allAdded = true;
612 foreach (T value in values)
614 if (!list.AddDistinct(value, comparer))
621 public static int IndexOf<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
624 foreach (T value in collection)
626 if (predicate(value))