mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-02-16 13:15:07 +00:00
Created S# .sln and moved project to src folder
This commit is contained in:
47
ICD.Common/Utils/Extensions/CollectionExtensions.cs
Normal file
47
ICD.Common/Utils/Extensions/CollectionExtensions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for working with ICollections.
|
||||
/// </summary>
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes items matching the predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="predicate"></param>
|
||||
public static void RemoveAll<T>(this ICollection<T> extends, Func<T, bool> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
extends.RemoveAll(extends.Where(predicate).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all of the items from the other collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
public static void RemoveAll<T>(this ICollection<T> extends, IEnumerable<T> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
foreach (T item in other)
|
||||
extends.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ICD.Common/Utils/Extensions/DateTimeExtensions.cs
Normal file
24
ICD.Common/Utils/Extensions/DateTimeExtensions.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for DateTime.
|
||||
/// </summary>
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string representation of the DateTime with millisecond precision.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToLongTimeStringWithMilliseconds(this DateTime extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// Todo - Better handle different cultures
|
||||
return extends.ToString("HH:mm:ss:fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
340
ICD.Common/Utils/Extensions/DictionaryExtensions.cs
Normal file
340
ICD.Common/Utils/Extensions/DictionaryExtensions.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes the first key with a value matching the given value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns>False if value is not found in the dictionary.</returns>
|
||||
[PublicAPI]
|
||||
public static bool RemoveValue<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
try
|
||||
{
|
||||
TKey key = extends.GetKey(value);
|
||||
return extends.Remove(key);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all keys with the given value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
[PublicAPI]
|
||||
public static void RemoveAllValues<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
foreach (TKey key in extends.GetKeys(value).ToArray())
|
||||
extends.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the key is present in the dictionary return the value, otherwise returns default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.GetDefault(key, default(TValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the key is present in the dictionary return the value, otherwise return the default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key, TValue defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
return extends.ContainsKey(key) ? extends[key] : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the key is present in the dictionary return the value, otherwise add the default value to the dictionary and return it.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key,
|
||||
TValue defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
extends[key] = extends.GetDefault(key, defaultValue);
|
||||
return extends[key];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for the given value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The value does not exist in the dictionary.</exception>
|
||||
[PublicAPI]
|
||||
public static TKey GetKey<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
try
|
||||
{
|
||||
return extends.GetKeys(value).First();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
string message = string.Format("Unable to find Key with Value matching {0}", value);
|
||||
throw new KeyNotFoundException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keys that match the given value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<TKey> GetKeys<TKey, TValue>(this IDictionary<TKey, TValue> extends, TValue value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Where(kvp => EqualityComparer<TValue>.Default.Equals(kvp.Value, value))
|
||||
.Select(kvp => kvp.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the dictionary with items from the other dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
[PublicAPI]
|
||||
public static void Update<TKey, TValue>(this IDictionary<TKey, TValue> extends, IDictionary<TKey, TValue> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> pair in other)
|
||||
extends[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getKey"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TValue> items,
|
||||
Func<TValue, TKey> getKey)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
if (getKey == null)
|
||||
throw new ArgumentNullException("getKey");
|
||||
|
||||
foreach (TValue item in items)
|
||||
extends.Add(getKey(item), item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getValue"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<TKey> items,
|
||||
Func<TKey, TValue> getValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
if (getValue == null)
|
||||
throw new ArgumentNullException("getValue");
|
||||
|
||||
foreach (TKey item in items)
|
||||
extends.Add(item, getValue(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this Dictionary<TKey, TValue> extends,
|
||||
IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> item in items)
|
||||
extends.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the keys and values of the dictionary to determine equality.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IDictionary<TKey, TValue> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.DictionaryEqual(other, EqualityComparer<TValue>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the keys and values of the dictionary to determine equality.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="valueComparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool DictionaryEqual<TKey, TValue>(this IDictionary<TKey, TValue> extends,
|
||||
IDictionary<TKey, TValue> other,
|
||||
IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == other)
|
||||
return true;
|
||||
if (other == null)
|
||||
return false;
|
||||
if (extends.Count != other.Count)
|
||||
return false;
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in extends)
|
||||
{
|
||||
TValue secondValue;
|
||||
if (!other.TryGetValue(kvp.Key, out secondValue))
|
||||
return false;
|
||||
if (!valueComparer.Equals(kvp.Value, secondValue))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the KeyValuePairs in key order.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> OrderByKey<TKey, TValue>(
|
||||
this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.OrderBy(kvp => kvp.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a sequence of values ordered by the dictionary keys.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<TValue> OrderValuesByKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
ICD.Common/Utils/Extensions/EnumExtensions.cs
Normal file
71
ICD.Common/Utils/Extensions/EnumExtensions.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Check to see if a flags enumeration has a specific flag set.
|
||||
/// </summary>
|
||||
/// <param name="extends">Flags enumeration to check</param>
|
||||
/// <param name="value">Flag to check for</param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasFlag(this Enum extends, Enum value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (EnumUtils.HasMultipleFlags(value))
|
||||
throw new ArgumentException("Value has multiple flags", "value");
|
||||
|
||||
return extends.HasFlags(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if a flags enumeration has all of the given flags set.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool HasFlags(this Enum extends, Enum value)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
// Not as good as the .NET 4 version of this function, but should be good enough
|
||||
if (extends.GetType() != value.GetType())
|
||||
{
|
||||
string message = string.Format("Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
|
||||
value.GetType(), extends.GetType());
|
||||
throw new ArgumentException(message);
|
||||
}
|
||||
|
||||
ulong num = Convert.ToUInt64(value);
|
||||
return (Convert.ToUInt64(extends) & num) == num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Casts the enum to the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T Cast<T>(this Enum extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), extends);
|
||||
}
|
||||
}
|
||||
}
|
||||
717
ICD.Common/Utils/Extensions/EnumerableExtensions.cs
Normal file
717
ICD.Common/Utils/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,717 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Collections;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first item in the sequence. Returns the provided default item if there
|
||||
/// are no elements in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="defaultItem"></param>
|
||||
/// <returns></returns>
|
||||
public static T FirstOrDefault<T>(this IEnumerable<T> extends, T defaultItem)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.FirstOrDefault(i => true, defaultItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first element in the sequence matching the predicate. Returns the provided
|
||||
/// default item if there are no elements matching the predicate in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="defaultItem"></param>
|
||||
/// <returns></returns>
|
||||
public static T FirstOrDefault<T>(this IEnumerable<T> extends, Func<T, bool> predicate, T defaultItem)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
foreach (T item in extends.Where(predicate))
|
||||
return item;
|
||||
return defaultItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is at least 1 item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item">Outputs the first item in the sequence.</param>
|
||||
/// <returns></returns>
|
||||
public static bool TryFirstOrDefault<T>(this IEnumerable<T> extends, out T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.TryFirstOrDefault(i => true, out item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is at least 1 item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="item">Outputs the first item in the sequence.</param>
|
||||
/// <returns></returns>
|
||||
public static bool TryFirstOrDefault<T>(this IEnumerable<T> extends, Func<T, bool> predicate, out T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
item = default(T);
|
||||
|
||||
using (IEnumerator<T> iterator = extends.GetEnumerator())
|
||||
{
|
||||
while (iterator.MoveNext())
|
||||
{
|
||||
if (!predicate(iterator.Current))
|
||||
continue;
|
||||
|
||||
item = iterator.Current;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the true if an element with the given index is in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryElementAt<T>(this IEnumerable<T> extends, int index, out T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
item = default(T);
|
||||
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
T[] itemArray = extends as T[] ?? extends.ToArray();
|
||||
if (index >= itemArray.Length)
|
||||
return false;
|
||||
|
||||
item = itemArray[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two sequences for identical values, ignoring order.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ScrambledEquals<T>(this IEnumerable<T> extends, IEnumerable<T> other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
return extends.ScrambledEquals(other, EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two sequences for identical values, ignoring order.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ScrambledEquals<T>(this IEnumerable<T> extends, IEnumerable<T> other, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
Dictionary<T, int> count = new Dictionary<T, int>(comparer);
|
||||
|
||||
foreach (T item in extends)
|
||||
{
|
||||
if (count.ContainsKey(item))
|
||||
count[item]++;
|
||||
else
|
||||
count.Add(item, 1);
|
||||
}
|
||||
|
||||
foreach (T item in other)
|
||||
{
|
||||
if (count.ContainsKey(item))
|
||||
count[item]--;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return count.Values.All(c => c == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index that matches the predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="match"></param>
|
||||
/// <returns></returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> extends, Predicate<T> match)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
|
||||
int index = 0;
|
||||
|
||||
foreach (T item in extends)
|
||||
{
|
||||
if (match(item))
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows for selection of multiple results for each item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="selectors"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TResult> SelectMulti<TSource, TResult>(this IEnumerable<TSource> extends,
|
||||
params Func<TSource, TResult>[] selectors)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (selectors == null)
|
||||
throw new ArgumentNullException("selectors");
|
||||
|
||||
return extends.SelectMany(source => selectors, (source, selector) => selector(source));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates each item in the sequence.
|
||||
/// Useful for processing a LINQ query without creating a new collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
[PublicAPI]
|
||||
public static void Execute<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.ForEach(item => { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the action for each item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="action"></param>
|
||||
[PublicAPI]
|
||||
public static void ForEach<T>(this IEnumerable<T> extends, Action<T> action)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
|
||||
extends.ForEach((item, index) => action(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the action for each item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="action"></param>
|
||||
[PublicAPI]
|
||||
public static void ForEach<T>(this IEnumerable<T> extends, Action<T, int> action)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
|
||||
int index = 0;
|
||||
foreach (T item in extends)
|
||||
action(item, index++);
|
||||
}
|
||||
|
||||
#if SIMPLSHARP
|
||||
/// <summary>
|
||||
/// Prepends the item to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.PrependMany(new[] {item});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the items to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> PrependMany<T>(this IEnumerable<T> extends, params T[] items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
foreach (T each in extends)
|
||||
yield return each;
|
||||
}
|
||||
|
||||
#if SIMPLSHARP
|
||||
/// <summary>
|
||||
/// Appends the item to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Append<T>(this IEnumerable<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.AppendMany(new[] {item});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Appends the items to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> AppendMany<T>(this IEnumerable<T> extends, params T[] items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T each in extends)
|
||||
yield return each;
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default ordering for the items in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Order<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.OrderBy(i => i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns every item in the sequence except the given item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Except<T>(this IEnumerable<T> extends, T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Except(new[] {item});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sequence as a IcdHashSet.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static IcdHashSet<T> ToHashSet<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return new IcdHashSet<T>(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sequence as an index:value dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
Dictionary<int, T> output = new Dictionary<int, T>();
|
||||
extends.ForEach((item, index) => output.Add(index, item));
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sequence as an index:value dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static Dictionary<uint, T> ToDictionaryUInt<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
Dictionary<uint, T> output = new Dictionary<uint, T>();
|
||||
extends.ForEach((item, index) => output.Add((uint)index, item));
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns an enumerable of KeyValuePairs back into a dictionary
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns other if the list is empty.
|
||||
/// Returns other if the list is non-empty and there are two different elements.
|
||||
/// Returns the element of the list if it is non-empty and all elements are the same.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T Unanimous<T>(this IEnumerable<T> extends, T other)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
T[] array = extends as T[] ?? extends.ToArray();
|
||||
return array.Unanimous() ? array.First() : other;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if the list is empty.
|
||||
/// Returns false if the list is non-empty and there are two different elements.
|
||||
/// Returns true if the list is non-empty and all elements are the same.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool Unanimous<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
T[] array = extends as T[] ?? extends.ToArray();
|
||||
|
||||
if (array.Length == 0)
|
||||
return false;
|
||||
|
||||
T val = array.First();
|
||||
return array.All(x => EqualityComparer<T>.Default.Equals(x, val));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a sequence into sequences of the given length.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="partitionSize"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> extends, int partitionSize)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
using (IEnumerator<T> enumerator = extends.GetEnumerator())
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
yield return YieldBatchElements(enumerator, partitionSize - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<T> YieldBatchElements<T>(IEnumerator<T> source, int partitionSize)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
// Important to enumerate through partitionSize items before returning
|
||||
// Otherwise enumerable.Partition(3).Skip(1) will do unwanted things.
|
||||
List<T> output = new List<T> {source.Current};
|
||||
|
||||
for (int i = 0; i < partitionSize && source.MoveNext(); i++)
|
||||
output.Add(source.Current);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimal element of the given sequence, based on
|
||||
/// the given projection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If more than one element has the minimal projected value, the first
|
||||
/// one encountered will be returned. This overload uses the default comparer
|
||||
/// for the projected type. This operator uses immediate execution, but
|
||||
/// only buffers a single result (the current minimal element).
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSource">Type of the source sequence</typeparam>
|
||||
/// <typeparam name="TKey">Type of the projected element</typeparam>
|
||||
/// <param name="source">Source sequence</param>
|
||||
/// <param name="selector">Selector to use to pick the results to compare</param>
|
||||
/// <returns>The minimal element, according to the projection.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source"/> is empty</exception>
|
||||
[PublicAPI]
|
||||
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
|
||||
Func<TSource, TKey> selector)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
if (selector == null)
|
||||
throw new ArgumentNullException("selector");
|
||||
|
||||
return source.MinBy(selector, Comparer<TKey>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimal element of the given sequence, based on
|
||||
/// the given projection and the specified comparer for projected values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If more than one element has the minimal projected value, the first
|
||||
/// one encountered will be returned. This operator uses immediate execution, but
|
||||
/// only buffers a single result (the current minimal element).
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSource">Type of the source sequence</typeparam>
|
||||
/// <typeparam name="TKey">Type of the projected element</typeparam>
|
||||
/// <param name="source">Source sequence</param>
|
||||
/// <param name="selector">Selector to use to pick the results to compare</param>
|
||||
/// <param name="comparer">Comparer to use to compare projected values</param>
|
||||
/// <returns>The minimal element, according to the projection.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source"/>, <paramref name="selector"/>
|
||||
/// or <paramref name="comparer"/> is null</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source"/> is empty</exception>
|
||||
[PublicAPI]
|
||||
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector,
|
||||
IComparer<TKey> comparer)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
if (selector == null)
|
||||
throw new ArgumentNullException("selector");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
|
||||
{
|
||||
if (!sourceIterator.MoveNext())
|
||||
throw new InvalidOperationException("Sequence contains no elements");
|
||||
TSource min = sourceIterator.Current;
|
||||
TKey minKey = selector(min);
|
||||
while (sourceIterator.MoveNext())
|
||||
{
|
||||
TSource candidate = sourceIterator.Current;
|
||||
TKey candidateProjected = selector(candidate);
|
||||
if (comparer.Compare(candidateProjected, minKey) >= 0)
|
||||
continue;
|
||||
|
||||
min = candidate;
|
||||
minKey = candidateProjected;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any null elements from an enumerable of nullable value types
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> ExceptNulls<T>(this IEnumerable<T?> extends)
|
||||
where T : struct
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.Where(e => e.HasValue).Select(e => e.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the sum of a sequence of byte values.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static byte Sum(this IEnumerable<byte> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return (byte)extends.Select(i => (int)i).Sum();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips duplicate, consecutive items.
|
||||
/// E.g.
|
||||
/// [1, 2, 2, 3, 1, 1]
|
||||
/// Becomes
|
||||
/// [1, 2, 3, 1]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> Consolidate<T>(IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return Consolidate(extends, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips duplicate, consecutive items.
|
||||
/// E.g.
|
||||
/// [1, 2, 2, 3, 1, 1]
|
||||
/// Becomes
|
||||
/// [1, 2, 3, 1]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> Consolidate<T>(IEnumerable<T> extends, IComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
bool first = true;
|
||||
T last = default(T);
|
||||
|
||||
foreach (T item in extends)
|
||||
{
|
||||
if (!first && comparer.Compare(last, item) != 0)
|
||||
continue;
|
||||
|
||||
first = false;
|
||||
last = item;
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all items in the sequence match the predicate.
|
||||
/// Returns false if the sequence is empty.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool AnyAndAll<T>(this IEnumerable<T> extends, Func<T, bool> predicate)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
bool any = false;
|
||||
foreach (T item in extends)
|
||||
{
|
||||
any = true;
|
||||
if (!predicate(item))
|
||||
return false;
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
ICD.Common/Utils/Extensions/EventHandlerExtensions.cs
Normal file
35
ICD.Common/Utils/Extensions/EventHandlerExtensions.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for EventHandlers.
|
||||
/// </summary>
|
||||
public static class EventHandlerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
public static void Raise(this EventHandler extends, object sender)
|
||||
{
|
||||
if (extends != null)
|
||||
extends(sender, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the event safely. Simply skips if the handler is null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void Raise<T>(this EventHandler<T> extends, object sender, T args)
|
||||
where T : EventArgs
|
||||
{
|
||||
if (extends != null)
|
||||
extends(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
ICD.Common/Utils/Extensions/HashSetExtensions.cs
Normal file
87
ICD.Common/Utils/Extensions/HashSetExtensions.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
#if STANDARD
|
||||
using ICD.Common.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ICD.NetStandard.Common.Utils.Extensions
|
||||
{
|
||||
public static class HashSetExtensions
|
||||
{
|
||||
[PublicAPI]
|
||||
public static HashSet<T> Subtract<T>(this HashSet<T> extends, IEnumerable<T> set)
|
||||
{
|
||||
HashSet<T> subtractSet = new HashSet<T>(extends);
|
||||
|
||||
if (set == null)
|
||||
return subtractSet;
|
||||
|
||||
foreach (T item in set)
|
||||
subtractSet.Remove(item);
|
||||
|
||||
return subtractSet;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static bool IsSubsetOf<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
HashSet<T> setToCompare = set ?? new HashSet<T>();
|
||||
return extends.All(setToCompare.Contains);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static HashSet<T> Intersection<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
HashSet<T> intersectionSet = new HashSet<T>();
|
||||
|
||||
if (set == null)
|
||||
return intersectionSet;
|
||||
|
||||
foreach (T item in extends.Where(set.Contains))
|
||||
intersectionSet.Add(item);
|
||||
|
||||
foreach (T item in set.Where(extends.Contains))
|
||||
intersectionSet.Add(item);
|
||||
|
||||
return intersectionSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns items that are not common between both sets.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static HashSet<T> NonIntersection<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
return new HashSet<T>(extends.Subtract(set).Union(set.Subtract(extends)));
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static bool IsProperSubsetOf<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
HashSet<T> setToCompare = set ?? new HashSet<T>();
|
||||
|
||||
// Is a proper subset if A is a subset of B and A != B
|
||||
return (extends.IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(extends));
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static bool IsSupersetOf<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
HashSet<T> setToCompare = set ?? new HashSet<T>();
|
||||
return setToCompare.IsSubsetOf(extends);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static bool IsProperSupersetOf<T>(this HashSet<T> extends, HashSet<T> set)
|
||||
{
|
||||
HashSet<T> setToCompare = set ?? new HashSet<T>();
|
||||
|
||||
// B is a proper superset of A if B is a superset of A and A != B
|
||||
return (extends.IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(extends));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
107
ICD.Common/Utils/Extensions/JsonExtensions.cs
Normal file
107
ICD.Common/Utils/Extensions/JsonExtensions.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for working with JSON.
|
||||
/// </summary>
|
||||
public static class JsonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the object value.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="converter"></param>
|
||||
[PublicAPI]
|
||||
public static void WriteObject(this JsonWriter extends, object value, JsonSerializer serializer,
|
||||
JsonConverter converter)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (serializer == null)
|
||||
throw new ArgumentNullException("serializer");
|
||||
|
||||
if (converter == null)
|
||||
throw new ArgumentNullException("converter");
|
||||
|
||||
JObject jObject = JObject.FromObject(value, serializer);
|
||||
jObject.WriteTo(extends, converter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an integer.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int GetValueAsInt(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Integer)
|
||||
return (int)(long)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Integer);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string GetValueAsString(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.String || extends.TokenType == JsonToken.Null)
|
||||
return extends.Value as string;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.String);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as a bool.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool GetValueAsBool(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.Boolean)
|
||||
return (bool)extends.Value;
|
||||
|
||||
string message = string.Format("Token {0} {1} is not {2}", extends.TokenType, extends.Value, JsonToken.Boolean);
|
||||
throw new InvalidCastException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value as an enum.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static T GetValueAsEnum<T>(this JsonReader extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends.TokenType == JsonToken.String)
|
||||
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
|
||||
return (T)(object)extends.GetValueAsInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
47
ICD.Common/Utils/Extensions/QueueExtensions.cs
Normal file
47
ICD.Common/Utils/Extensions/QueueExtensions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class QueueExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enqueues each item in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
public static void EnqueueRange<T>(this Queue<T> extends, IEnumerable<T> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T item in items)
|
||||
extends.Enqueue(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues the next item in the queue. Returns false if the queue is empty.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Dequeue<T>(this Queue<T> extends, out T item)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
item = default(T);
|
||||
|
||||
if (extends.Count == 0)
|
||||
return false;
|
||||
|
||||
item = extends.Dequeue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
ICD.Common/Utils/Extensions/ReflectionExtensions.cs
Normal file
32
ICD.Common/Utils/Extensions/ReflectionExtensions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
#if SIMPLSHARP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for use with reflection objects.
|
||||
/// </summary>
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the custom attributes attached to the member.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="inherits"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo extends, bool inherits)
|
||||
where T : Attribute
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.GetCustomAttributes(typeof(T), inherits).Cast<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
36
ICD.Common/Utils/Extensions/StringBuilderExtensions.cs
Normal file
36
ICD.Common/Utils/Extensions/StringBuilderExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class StringBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Empties the StringBuilder.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
public static void Clear(this StringBuilder extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
extends.Remove(0, extends.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current string value of the StringBuilder and clears the
|
||||
/// StringBuilder for further use.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string Pop(this StringBuilder extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
string output = extends.ToString();
|
||||
extends.Clear();
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
256
ICD.Common/Utils/Extensions/StringExtensions.cs
Normal file
256
ICD.Common/Utils/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first appearance of an item from the given sequence.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static int IndexOf(this string extends, IEnumerable<string> items, out string first)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
int index = -1;
|
||||
first = null;
|
||||
|
||||
foreach (string item in items)
|
||||
{
|
||||
int thisIndex = extends.IndexOf(item, StringComparison.Ordinal);
|
||||
if (thisIndex == -1)
|
||||
continue;
|
||||
|
||||
if (index != -1 && thisIndex >= index)
|
||||
continue;
|
||||
|
||||
index = thisIndex;
|
||||
first = item;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the string starts with the given character.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="character"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool StartsWith(this string extends, char character)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.StartsWith(character.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the string ends with the given character.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="character"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool EndsWith(this string extends, char character)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.EndsWith(character.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the string by the given delimiter, returning up to the given number of substrings.
|
||||
/// E.g. "a:b:c".Split(':', 2) returns ["a", "b:c"]
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> Split(this string extends, char delimeter, int count)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (count < 2)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
}
|
||||
|
||||
int index = extends.IndexOf(delimeter);
|
||||
if (index < 0)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
}
|
||||
|
||||
string first = extends.Substring(0, index);
|
||||
string second = extends.Substring(index + 1);
|
||||
count--;
|
||||
|
||||
yield return first;
|
||||
foreach (string item in second.Split(delimeter, count))
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string into chunks of the given length.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="chunkSize"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<string> Split(this string extends, int chunkSize)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (chunkSize <= 0)
|
||||
throw new InvalidOperationException("chunkSize must be greater than 0");
|
||||
|
||||
return Enumerable.Range(0, extends.Length / chunkSize)
|
||||
.Select(i => extends.Substring(i * chunkSize, chunkSize));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by a given substring.
|
||||
/// Taken from
|
||||
/// https://social.msdn.microsoft.com/Forums/en-US/914a350f-e0e9-45e0-91a4-6b4b2168e780/string-split-function
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<string> Split(this string extends, string delimeter)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (delimeter == null)
|
||||
throw new ArgumentNullException("delimeter");
|
||||
|
||||
int dSum = 0;
|
||||
int sSum = 0;
|
||||
int length = extends.Length;
|
||||
int delimiterLength = delimeter.Length;
|
||||
|
||||
if (delimiterLength == 0 || length == 0 || length < delimiterLength)
|
||||
{
|
||||
yield return extends;
|
||||
yield break;
|
||||
}
|
||||
|
||||
char[] cd = delimeter.ToCharArray();
|
||||
char[] cs = extends.ToCharArray();
|
||||
|
||||
for (int i = 0; i < delimiterLength; i++)
|
||||
{
|
||||
dSum += cd[i];
|
||||
sSum += cs[i];
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
for (int i = start; i < length - delimiterLength; i++)
|
||||
{
|
||||
if (i >= start && dSum == sSum && extends.Substring(i, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, i - start);
|
||||
start = i + delimiterLength;
|
||||
}
|
||||
|
||||
sSum += cs[i + delimiterLength] - cs[i];
|
||||
}
|
||||
|
||||
if (dSum == sSum && extends.Substring(length - delimiterLength, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, length - delimiterLength - start);
|
||||
yield return string.Empty;
|
||||
}
|
||||
else
|
||||
yield return extends.Substring(start, length - start);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by the given substrings.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeters"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static IEnumerable<string> Split(this string extends, IEnumerable<string> delimeters)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (delimeters == null)
|
||||
throw new ArgumentNullException("delimeters");
|
||||
|
||||
string[] delimitersArray = delimeters as string[] ?? delimeters.ToArray();
|
||||
return delimitersArray.Length == 0
|
||||
? new[] {extends}
|
||||
: extends.Split(delimitersArray.First())
|
||||
.SelectMany(s => s.Split(delimitersArray.Skip(1)))
|
||||
.Where(s => !string.IsNullOrEmpty(s));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes whitespace from the string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string RemoveWhitespace(this string extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return new string(extends.Where(c => !Char.IsWhiteSpace(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given characters from the string.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="characters"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string Remove(this string extends, IEnumerable<char> characters)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (characters == null)
|
||||
throw new ArgumentNullException("characters");
|
||||
|
||||
return new string(extends.Where(c => !characters.Contains(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the string only contains numbers.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static bool IsNumeric(this string extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.All(char.IsDigit);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
ICD.Common/Utils/Extensions/TimeSpanExtensions.cs
Normal file
32
ICD.Common/Utils/Extensions/TimeSpanExtensions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class TimeSpanExtensions
|
||||
{
|
||||
public static string ToReadableString(this TimeSpan extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (extends.Days > 0)
|
||||
builder.AppendFormat("{0} days, ", extends.Days);
|
||||
if (extends.Hours > 0)
|
||||
builder.AppendFormat("{0} hours, ", extends.Hours);
|
||||
if (extends.Minutes > 0)
|
||||
builder.AppendFormat("{0} minutes, ", extends.Minutes);
|
||||
if (extends.Seconds > 0)
|
||||
builder.AppendFormat("{0} seconds, ", extends.Seconds);
|
||||
if (extends.Milliseconds > 0)
|
||||
{
|
||||
builder.AppendFormat("{0}.{1} ms", extends.Milliseconds,
|
||||
((double)extends.Ticks / TimeSpan.TicksPerMillisecond) - extends.TotalMilliseconds);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
17
ICD.Common/Utils/Extensions/TypeExtensions.cs
Normal file
17
ICD.Common/Utils/Extensions/TypeExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class TypeExtensions
|
||||
{
|
||||
public static bool IsAssignableTo(this Type from, Type to)
|
||||
{
|
||||
return to.IsAssignableFrom(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user