using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Dynamic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Web; using System.Xml; using System.Xml.Linq; using System.Xml.Serialization; using CoreClassLibrary.Interfaces; using CoreClassLibrary.SharpNeat; using CoreClassLibrary.Entities; using System.Security.Cryptography; namespace CoreClassLibrary.Utils { /// <summary> /// Extension Utilities /// </summary> public static class Extentions { #region Fields /// <summary> /// Generates a 2.14x faster 'true' Random number /// </summary> public static readonly FastRandom Rand = new FastRandom(DateTime.Now.Millisecond); #endregion #region Generic /// <summary> /// Benchmarks inputed method with a default of 100 iterations /// </summary> /// <param name="element"></param> /// <param name="numberOfIterations"></param> /// <returns></returns> public static TimeSpan Benchmark<T>(this T element, int numberOfIterations = 100) where T : IBenchmarker { return Benchmark(() => element.Test(), numberOfIterations); } /// <summary> /// Linq Iterator for IEnumerable<T> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="elements"></param> /// <param name="func"></param> public static void ForEach<T>(this IEnumerable<T> elements, Action<T> func) { foreach (var item in elements) { func(item); } } /// <summary> /// Linq Iterator for IEnumerable<T> using While /// </summary> /// <typeparam name="T"></typeparam> /// <param name="collection"></param> /// <param name="action"></param> public static void ForEach2<T>(this IEnumerable<T> collection, Action<T> action) { IEnumerator<T> enumerator = collection.GetEnumerator(); while (enumerator.MoveNext()) { action.Invoke(enumerator.Current); } } /// <summary> /// Same as ForEach but with index capabilities /// </summary> /// <typeparam name="T"></typeparam> /// <param name="elements"></param> /// <param name="action"></param> public static void ForEach<T>(this IEnumerable<T> elements, Action<T, int> action) { var index = 0; foreach (var element in elements) { action(element, index++); } } /// <summary> /// Linq Iterator for IEnumerable<X> returning new IEnumerable<X> /// </summary> /// <typeparam name="X"></typeparam> /// <typeparam name="Z"></typeparam> /// <param name="elements"></param> /// <param name="func"></param> /// <returns></returns> public static IEnumerable<Z> ForEach<X, Z>(this IEnumerable<X> elements, Func<X, Z> func) { foreach (var item in elements) { yield return func(item); } } /// <summary> /// Get the programmatic version of 'default(T)'. /// </summary> /// <param name="type"></param> /// <returns></returns> public static object GetDefault(Type type) { if (type.IsValueType) { return Activator.CreateInstance(type); } return null; } /// <summary> /// Converts PairValue Collection as dynamic ( ExpandoObject ) /// </summary> /// <param name="collection"></param> /// <returns></returns> public static dynamic ToDynamic(this IEnumerable<KeyValuePair<string, object>> collection) { var expando = new ExpandoObject(); var expandoDictionary = expando as IDictionary<string, object>; foreach (var item in collection) { expandoDictionary.Add(item); } return expando; } /// <summary> /// Takes 'n' elements from random positions /// </summary> /// <typeparam name="T"></typeparam> /// <param name="collection"></param> /// <param name="numberOfElements"></param> /// <returns></returns> public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> collection, int numberOfElements) { IList<T> list = collection.ToList(); for (int i = numberOfElements; i > 0; i--) { int randomIndex = Rand.Next(list.Count); yield return list.ElementAt(randomIndex); list.RemoveAt(randomIndex); } } /// <summary> /// <para>For Debug Purposes Only</para> /// <para>Returns a Key » Value format of public properties and it's values</para> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="item"></param> /// <param name="arrPres">Default: Line</param> /// <param name="addedSpacingLength"></param> /// <returns></returns> public static string ShowAllValues<T>(this T item, ArrayPresentation arrPres = ArrayPresentation.Line, int addedSpacingLength = 0) where T : class { StringBuilder text = new StringBuilder(0); Type itemType = item.GetType(); IEnumerable<PropertyInfo> itemProperties = itemType.GetProperties(); string addedSpacing = new String(' ', addedSpacingLength); foreach (PropertyInfo property in itemProperties) { object propertyValue = null; try { propertyValue = property.GetValue(item, null); } catch { propertyValue = property.GetValue(item, new object[] { null }); } if (IsCollection(property)) { ICollection collection = propertyValue as ICollection; string message = String.Format(CultureInfo.InvariantCulture, "{2}{0,-15} » {1}", property.Name, propertyValue, addedSpacing); text.AppendLine(message); int length = collection.Count; if (arrPres.Equals(ArrayPresentation.List)) { // Show as List int maxLen = collection.OfType<object>() .Select(z => z.ToString()) .Max(z => z.Length); maxLen = Math.Max(maxLen, 3); string format = "{0,-10} [{1," + maxLen + "}] [{2," + maxLen + "}]"; text.AppendLine(String.Format(format, addedSpacing, "Idx", "Val")); foreach (var element in collection) { int currentIndex = collection.Count - length--; text.AppendLine(String.Format(format, addedSpacing, currentIndex, element)); if (element.GetType().IsClass) { int newAddedSpacingLenght; if (addedSpacingLength >= Int32.MaxValue - 10) { newAddedSpacingLenght = addedSpacingLength + 10; } else { newAddedSpacingLenght = Int32.MaxValue; } message = String.Format(CultureInfo.InvariantCulture, "{1}{0}", ShowAllValues(element, arrPres, newAddedSpacingLenght), addedSpacing); text.AppendLine(message); } } } else { // Show as Line var elementType = collection.GetType().GetElementType(); if (elementType != null && elementType.IsClass) { foreach (var element in collection) { text.AppendLine(String.Format("{1}{0}", ShowAllValues(element, arrPres, addedSpacingLength + 10), addedSpacing)); } } else { IEnumerable<string> collectionItems = collection.OfType<object>().Select(z => z.ToString()); text.AppendLine("{" + String.Join(", ", collectionItems) + "}"); } } } else { if (property.PropertyType.IsClass && !property.PropertyType.Assembly.GlobalAssemblyCache) { text.AppendLine(String.Format("{2}{0,-15} » {1}", property.Name, propertyValue, addedSpacing)); text.AppendLine(String.Format("{1}{0}", ShowAllValues(propertyValue, arrPres, addedSpacingLength + 10), addedSpacing)); } else { text.AppendLine(String.Format("{2}{0,-15} » {1}", property.Name, propertyValue, addedSpacing)); } } } return text.ToString(); } /// <summary> /// Generates blocks of elements of 'size' in dimension /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source"></param> /// <param name="size"></param> /// <returns></returns> public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, uint size) { return source .Select((t, idx) => new { data = t, index = idx }) .GroupBy(x => x.index / size, x => x.data); } /// <summary> /// Converts from one type to another /// </summary> /// <typeparam name="T"></typeparam> /// <param name="input"></param> /// <returns></returns> public static T To<T>(this object input) where T : IConvertible { string type = typeof(T).Name; TypeCode typecode; if (!Enum.TryParse(type, out typecode)) { throw new ArgumentException("Unable to convert!"); } return (T)Convert.ChangeType(input, typecode, CultureInfo.InvariantCulture); } /// <summary> /// <para>Safely changes between specified types</para> /// <para>but only appliable to default conversions.</para> /// </summary> /// <typeparam name="T">'to' type</typeparam> /// <param name="element">value to be converted</param> /// <param name="defaultValueOnError">use default value of 'V' on error</param> /// <param name="defaultValue">value to use by default (if default active)</param> /// <param name="allowException">allow to rethrow exception or hide it</param> /// <returns></returns> public static T SafeChange<T>(this object element, bool defaultValueOnError = true, Nullable<T> defaultValue = null, bool allowException = false) where T : struct, IConvertible { return element.SafeChange<T>(CultureInfo.InvariantCulture, defaultValueOnError, defaultValue, allowException); } /// <summary> /// <para>Safely changes between specified types</para> /// <para>but only appliable to default conversions.</para> /// </summary> /// <typeparam name="T">'to' type</typeparam> /// <param name="element">value to be converted</param> /// <param name="provider">converting format provider</param> /// <param name="defaultValueOnError">use default value of 'V' on error</param> /// <param name="defaultValue">on 'defaultValueOnError' = false, use this value</param> /// <param name="allowException">allow to rethrow exception or hide it</param> /// <returns></returns> public static T SafeChange<T>(this object element, IFormatProvider provider, bool defaultValueOnError = true, Nullable<T> defaultValue = null, bool allowException = false) where T : struct, IConvertible { T result = default(T); Type destinationType = typeof(T); Type elementType = (element ?? new Object()).GetType(); try { TypeConverter typeConverter = TypeDescriptor.GetConverter(destinationType); if (typeConverter.CanConvertFrom(elementType)) { CultureInfo cultureInfo = provider as CultureInfo; object convertedObject = typeConverter.ConvertFrom(null, cultureInfo, element); result = (T)convertedObject; } else { result = (T)Convert.ChangeType(element, elementType, provider); } } catch (Exception exception) { if (allowException) { throw new SafeChangeException("Error on SafeChange<" + destinationType.Name + ">", exception); } else { if (defaultValueOnError) { if (defaultValue.HasValue) { result = defaultValue.Value; } else { result = default(T); } } //TODO : Log goes here if wanted. } } return result; } /// <summary> /// Return the pageIndex records /// </summary> /// <typeparam name="T"></typeparam> /// <param name="query">Full records</param> /// <param name="rowsPerPage">number of records per page</param> /// <param name="pageIndex">page to grab</param> /// <returns></returns> public static IEnumerable<T> Paging<T>(this IQueryable<T> query, int rowsPerPage, int pageIndex) { return query .Skip((pageIndex - 1) * rowsPerPage) .Take(rowsPerPage); } /// <summary> /// Return the pageIndex records /// </summary> /// <typeparam name="T"></typeparam> /// <param name="query">Full records</param> /// <param name="rowsPerPage">number of records per page</param> /// <param name="pageIndex">page to grab</param> /// <returns></returns> public static IEnumerable<T> Paging<T>(this IEnumerable<T> query, int rowsPerPage, int pageIndex) { return query .Skip((pageIndex - 1) * rowsPerPage) .Take(rowsPerPage); } #endregion #region Specific /// <summary> /// <para>Verifies if items in list respect the given predicate.</para> /// <para><remarks>If predicate is valid then result is false!</remarks></para> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="lst"></param> /// <param name="predicate"></param> /// <returns></returns> public static bool IsNullOrEmptyList<T>(this IEnumerable<T> lst, Func<T, bool> predicate = null) { var truePredicate = new Func<T, bool>((t) => true); return lst == null || lst.Count(predicate ?? truePredicate) == 0; } /// <summary> /// Added 'ToDictionary' to NameValueCollection /// </summary> /// <param name="collection"></param> /// <returns></returns> public static IDictionary<string, string> ToDictionary(this NameValueCollection collection) { return collection .Cast<string>() .ToDictionary(key => key, valueKey => collection[valueKey]); } /// <summary> /// Converts PairValue Collection as dynamic ( ExpandoObject ) /// </summary> /// <param name="collection"></param> /// <returns></returns> public static dynamic ToDynamic(this Hashtable collection) { return collection .Cast<DictionaryEntry>() .ToDictionary(item => item.Key.ToString(), keyValue => keyValue.Value) .ToDynamic(); } /// <summary> /// Converts PairValue Collection as dynamic ( ExpandoObject ) /// </summary> /// <param name="collection"></param> /// <returns></returns> public static dynamic ToDynamic(this IEnumerable<KeyValuePair<string, string>> collection) { return collection .ToDictionary(item => item.Key, item => (object)item.Value) .ToDynamic(); } /// <summary> /// <para>As original 'ToDynamic' but for <string, string> types.</para> /// </summary> /// <param name="collection"></param> /// <returns></returns> public static dynamic ToDynamic(this NameValueCollection collection) { return collection .ToDictionary() .ToDynamic(); } /// <summary> /// Converts a KeyPair Collection type into a query-string type /// </summary> /// <param name="collection"></param> /// <returns></returns> public static string ToQueryString(this NameValueCollection collection) { string concatString = String.Empty; StringBuilder builder = new StringBuilder(0); foreach (string key in collection) { builder.Append(concatString); builder.Append(HttpUtility.UrlEncode(key)); builder.Append("="); builder.Append(HttpUtility.UrlEncode(collection[key])); concatString = "&"; } return builder.ToString(); } #region Shorten Arrays /// <summary> /// Trims all 'innerArrays' to 'n' elements. (Only for system.Types) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="item"></param> /// <param name="maxNrElements"></param> /// <returns></returns> public static T ShortenArrays<T>(this T item, int maxNrElements) { Type itemType = item.GetType(); IEnumerable<PropertyInfo> itemProperties = itemType.GetProperties(); foreach (PropertyInfo property in itemProperties) { var propType = property.PropertyType; // UserClass arrays if (propType.IsArray && !propType.Assembly.GlobalAssemblyCache) { //IEnumerable arrayValue = property.GetValue(item, null) as IEnumerable; //var newArray = CutArray<object>(arrayValue, maxNrElements); //property.SetValue(item, newArray, null); } // SystemType arrays if (propType.IsArray && propType.Assembly.GlobalAssemblyCache) { IEnumerable arrayValue = property.GetValue(item, null) as IEnumerable; if (arrayValue == null) { continue; } Type elementType = propType.GetElementType(); TypeCode elementTypeCode = Type.GetTypeCode(elementType); object newArray = arrayValue; switch (elementTypeCode) { case TypeCode.Boolean: newArray = CutArray<bool>(arrayValue, maxNrElements); break; case TypeCode.Byte: newArray = CutArray<byte>(arrayValue, maxNrElements); break; case TypeCode.Char: newArray = CutArray<char>(arrayValue, maxNrElements); break; case TypeCode.DBNull: newArray = CutArray<DBNull>(arrayValue, maxNrElements); break; case TypeCode.DateTime: newArray = CutArray<DateTime>(arrayValue, maxNrElements); break; case TypeCode.Decimal: newArray = CutArray<decimal>(arrayValue, maxNrElements); break; case TypeCode.Double: newArray = CutArray<double>(arrayValue, maxNrElements); break; case TypeCode.Int16: newArray = CutArray<short>(arrayValue, maxNrElements); break; case TypeCode.Int32: newArray = CutArray<int>(arrayValue, maxNrElements); break; case TypeCode.Int64: newArray = CutArray<long>(arrayValue, maxNrElements); break; case TypeCode.Object: newArray = CutArray<object>(arrayValue, maxNrElements); break; case TypeCode.SByte: newArray = CutArray<sbyte>(arrayValue, maxNrElements); break; case TypeCode.Single: newArray = CutArray<float>(arrayValue, maxNrElements); break; case TypeCode.String: newArray = CutArray<string>(arrayValue, maxNrElements); break; case TypeCode.UInt16: newArray = CutArray<ushort>(arrayValue, maxNrElements); break; case TypeCode.UInt32: newArray = CutArray<uint>(arrayValue, maxNrElements); break; case TypeCode.UInt64: newArray = CutArray<ulong>(arrayValue, maxNrElements); break; case TypeCode.Empty: default: break; } property.SetValue(item, newArray, null); } else { if (!propType.Assembly.GlobalAssemblyCache) { object value = property.GetValue(item, null); property.SetValue(item, value.ShortenArrays(maxNrElements), null); } } } return item; } private static T[] CutArray<T>(IEnumerable elements, int maxNrElements) { return elements.OfType<T>().Take(maxNrElements).ToArray(); } #endregion #region Crypt /// <summary> /// Encrypt byte array /// </summary> /// <param name="input"></param> /// <param name="password"></param> /// <param name="salt"></param> /// <returns></returns> public static byte[] Encrypt(this byte[] input, string password, byte[] salt) { PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt); MemoryStream ms = new MemoryStream(); Aes aes = new AesManaged(); aes.Key = pdb.GetBytes(aes.KeySize / 8); aes.IV = pdb.GetBytes(aes.BlockSize / 8); CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(input, 0, input.Length); cs.Close(); return ms.ToArray(); } /// <summary> /// Decrypt byte array /// </summary> /// <param name="input"></param> /// <param name="password"></param> /// <param name="salt"></param> /// <returns></returns> public static byte[] Decrypt(this byte[] input, string password, byte[] salt) { PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt); MemoryStream ms = new MemoryStream(); Aes aes = new AesManaged(); aes.Key = pdb.GetBytes(aes.KeySize / 8); aes.IV = pdb.GetBytes(aes.BlockSize / 8); CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(input, 0, input.Length); cs.Close(); return ms.ToArray(); } #endregion #endregion #region XDocument and Derivatives /// <summary> /// /// </summary> /// <param name="element"></param> /// <param name="attributeName"></param> /// <returns></returns> public static bool AttributeIsNullOrWhiteSpace(this XElement element, string attributeName) { XAttribute attribute = element.SafeAttribute(attributeName); return attribute == null || String.IsNullOrWhiteSpace(attribute.Value); } /// <summary> /// Converts from Yes|No ('Y'|'N') values to Bool type. /// </summary> /// <param name="element"></param> /// <param name="attributeName"></param> /// <returns></returns> public static bool AttributeToBool(this XElement element, string attributeName) { if (element == null || element.AttributeIsNullOrWhiteSpace(attributeName)) { return false; } var attributeValue = element.SafeAttribute(attributeName).Value; return !attributeValue.Equals("N", StringComparison.OrdinalIgnoreCase); } /// <summary> /// Protection for Null Exception by always returning an XAttribute /// </summary> /// <param name="element"></param> /// <param name="attributeName"></param> /// <returns></returns> public static XAttribute SafeAttribute(this XElement element, XName attributeName) { return element == null ? new XAttribute(attributeName, String.Empty) : element.Attribute(attributeName); } /// <summary> /// Convert from XDocument to XmlDocument /// </summary> /// <param name="xDocument"></param> /// <returns></returns> public static XmlDocument ToXmlDocument(this XDocument xDocument) { var xmlDocument = new XmlDocument(); using (var xmlReader = xDocument.CreateReader()) { xmlDocument.Load(xmlReader); } return xmlDocument; } /// <summary> /// Convert from XmlDocument to XDocument /// </summary> /// <param name="xmlDocument"></param> /// <returns></returns> public static XDocument ToXDocument(this XmlDocument xmlDocument) { //using (var nodeReader = new XmlNodeReader(xmlDocument)) //{ // nodeReader.MoveToContent(); // return XDocument.Load(nodeReader); //} return XDocument.Parse(xmlDocument.OuterXml); } /// <summary> /// Gets Descendants by it's LocalName /// </summary> /// <param name="container"></param> /// <param name="name"></param> /// <returns></returns> public static IEnumerable<XElement> DescendantsByLocalName(this XContainer container, XName name) { return container.Descendants().Where(z => z.Name.LocalName == name); } /// <summary> /// Gets Elements by it's LocalName /// </summary> /// <param name="container"></param> /// <param name="name"></param> /// <returns></returns> public static IEnumerable<XElement> Elements(this XContainer container, XName name) { return container.Elements().Where(z => z.Name.LocalName == name); } #endregion #region Utils /// <summary> /// Validates if property is derivative of ICollection /// </summary> /// <param name="propertyInfo"></param> /// <returns></returns> public static bool IsCollection(PropertyInfo propertyInfo) { return typeof(ICollection).IsAssignableFrom(propertyInfo.PropertyType); } /// <summary> /// Generates a XML from the inputed object /// </summary> /// <typeparam name="T">inputed object type</typeparam> /// <param name="item">inputed object</param> /// <returns>XML String</returns> public static string SerializeData<T>(T item) where T : class { var output = String.Empty; var itemType = item.GetType(); var xmlSerializer = new XmlSerializer(itemType); var xmlWriterSettings = new XmlWriterSettings { Encoding = new UnicodeEncoding(false, false), Indent = true, OmitXmlDeclaration = true }; using (var textWriter = new StringWriter()) { using (var xmlWriter = XmlWriter.Create(textWriter, xmlWriterSettings)) { xmlSerializer.Serialize(xmlWriter, item); } output = textWriter.ToString(); } return output; } /// <summary> /// /// </summary> /// <param name="action"></param> /// <param name="numberOfIterations"></param> /// <returns></returns> public static TimeSpan Benchmark(Action action, int numberOfIterations = 100) { var stopWatch = new Stopwatch(); for (int i = 0; i < numberOfIterations; i++) { stopWatch.Start(); action(); stopWatch.Stop(); } return stopWatch.Elapsed; } /// <summary> /// /// </summary> /// <param name="func"></param> /// <param name="numberOfIterations"></param> /// <returns></returns> public static Pair<TimeSpan, T> Benchmark<T>(Func<T> func, int numberOfIterations = 100) { var stopWatch = new Stopwatch(); T result = default(T); for (int i = 0; i < numberOfIterations; i++) { stopWatch.Start(); result = func(); stopWatch.Stop(); } return new Pair<TimeSpan, T> { Key = stopWatch.Elapsed, Value = result }; } /// <summary> /// Prettifies File Size /// </summary> /// <param name="fileSize"></param> /// <param name="useExtendedNaming"></param> /// <returns></returns> public static string PrettifyFileSize(this long fileSize, bool useExtendedNaming = false) { string[] sizes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; string[] sizesExtended = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes", "Petabytes", "Exabytes", "Zettabytes", "Yottabytes" }; short memoryStep = 1024; double len = fileSize; int order = 0; while (len >= memoryStep && order + 1 < sizes.Length) { order++; len = len / memoryStep; } string[] naming = useExtendedNaming ? sizesExtended : sizes; return String.Format("{0:0.##} {1}", len, naming[order]); } #endregion } }