using UnityEngine; using System.Linq; namespace Crosstales { /// Various extension methods. public static class ExtensionMethods { #region Strings /// /// Extension method for strings. /// Converts a string to title case (first letter uppercase). /// /// String-instance. /// Converted string in title case. public static string CTToTitleCase(this string str) { if (str == null) throw new System.ArgumentNullException(nameof(str)); #if UNITY_WSA return toTitleCase(str); #else return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str.ToLower()); #endif } #if UNITY_WSA /// /// Converts to title case: each word starts with an upper case. /// private static string toTitleCase(string str) { if (str.Length == 0) return str; System.Text.StringBuilder result = new System.Text.StringBuilder(str); result[0] = char.ToUpper(result[0]); for (int ii = 1; ii < result.Length; ii++) { if (char.IsWhiteSpace(result[ii - 1])) result[ii] = char.ToUpper(result[ii]); else result[ii] = char.ToLower(result[ii]); } return result.ToString(); } #endif /// /// Extension method for strings. /// Reverses a string. /// /// String-instance. /// Reversed string. public static string CTReverse(this string str) { if (str == null) throw new System.ArgumentNullException(nameof(str)); char[] charArray = str.ToCharArray(); System.Array.Reverse(charArray); return new string(charArray); } /// /// Extension method for strings. /// Case insensitive 'Replace'. /// /// String-instance. /// String to replace. /// New replacement string. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// Replaced string. public static string CTReplace(this string str, string oldString, string newString, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); if (oldString == null) throw new System.ArgumentNullException(nameof(oldString)); if (newString == null) throw new System.ArgumentNullException(nameof(newString)); int index = str.IndexOf(oldString, comp); bool MatchFound = index >= 0; if (MatchFound) { str = str.Remove(index, oldString.Length); str = str.Insert(index, newString); } return str; } /// /// Extension method for strings. /// Case insensitive 'Equals'. /// /// String-instance. /// String to check. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// True if the string contains the given string. public static bool CTEquals(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); //if (toCheck == null) // throw new System.ArgumentNullException("toCheck"); return str.Equals(toCheck, comp); } /// /// Extension method for strings. /// Case insensitive 'Contains'. /// /// String-instance. /// String to check. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// True if the string contains the given string. public static bool CTContains(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); //if (toCheck == null) // throw new System.ArgumentNullException("toCheck"); return str.IndexOf(toCheck, comp) >= 0; } /// /// Extension method for strings. /// Contains any given string. /// /// String-instance. /// Search terms separated by the given split-character. /// Split-character (default: ' ', optional) /// True if the string contains any parts of the given string. public static bool CTContainsAny(this string str, string searchTerms, char splitChar = ' ') { if (str == null) throw new System.ArgumentNullException(nameof(str)); if (string.IsNullOrEmpty(searchTerms)) return true; char[] split = {splitChar}; return searchTerms.Split(split, System.StringSplitOptions.RemoveEmptyEntries).Any(searchTerm => str.CTContains(searchTerm)); } /// /// Extension method for strings. /// Contains all given strings. /// /// String-instance. /// Search terms separated by the given split-character. /// Split-character (default: ' ', optional) /// True if the string contains all parts of the given string. public static bool CTContainsAll(this string str, string searchTerms, char splitChar = ' ') { if (str == null) throw new System.ArgumentNullException(nameof(str)); if (string.IsNullOrEmpty(searchTerms)) return true; char[] split = {splitChar}; return searchTerms.Split(split, System.StringSplitOptions.RemoveEmptyEntries).All(searchTerm => str.CTContains(searchTerm)); } /// /// Extension method for strings. /// Checks if the string is numeric. /// /// String-instance. /// True if the string is numeric. public static bool CTisNumeric(this string str) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return double.TryParse(str, out double output); } /// /// Extension method for strings. /// Checks if the string is integer. /// /// String-instance. /// True if the string is integer. public static bool CTisInteger(this string str) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return !str.Contains(".") && long.TryParse(str, out long output); } /// /// Extension method for strings. /// Checks if the string starts with another string. /// /// String-instance. /// String to check. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// True if the string is integer. public static bool CTStartsWith(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return string.IsNullOrEmpty(toCheck) || str.StartsWith(toCheck, comp); } /// /// Extension method for strings. /// Checks if the string ends with another string. /// /// String-instance. /// String to check. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// True if the string is integer. public static bool CTEndsWith(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return string.IsNullOrEmpty(toCheck) || str.EndsWith(toCheck, comp); } /// /// Extension method for strings. /// Returns the index of the last occurence of a given string. /// /// String-instance. /// String for the index. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// The index of the last occurence of the given string if the string is integer. public static int CTLastIndexOf(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return string.IsNullOrEmpty(toCheck) ? 0 : str.LastIndexOf(toCheck, comp); } /// /// Extension method for strings. /// Returns the index of the first occurence of a given string. /// /// String-instance. /// String for the index. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// The index of the first occurence of the given string if the string is integer. public static int CTIndexOf(this string str, string toCheck, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return string.IsNullOrEmpty(toCheck) ? 0 : str.IndexOf(toCheck, comp); } /// /// Extension method for strings. /// Returns the index of the first occurence of a given string. /// /// String-instance. /// String for the index. /// Start index for the check. /// StringComparison-method (default: StringComparison.OrdinalIgnoreCase, optional) /// The index of the first occurence of the given string if the string is integer. public static int CTIndexOf(this string str, string toCheck, int startIndex, System.StringComparison comp = System.StringComparison.OrdinalIgnoreCase) { if (str == null) throw new System.ArgumentNullException(nameof(str)); return string.IsNullOrEmpty(toCheck) ? 0 : str.IndexOf(toCheck, startIndex, comp); } #endregion #region Arrays /// /// Extension method for Arrays. /// Shuffles an Array. /// /// Array-instance to shuffle. /// Seed for the PRNG (default: 0 (=standard), optional) public static void CTShuffle(this T[] array, int seed = 0) { if (array == null || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Random rnd = seed == 0 ? new System.Random() : new System.Random(seed); int n = array.Length; while (n > 1) { int k = rnd.Next(n--); T temp = array[n]; array[n] = array[k]; array[k] = temp; } } /// /// Extension method for Arrays. /// Dumps an array to a string. /// /// Array-instance to dump. /// Prefix for every element (default: empty, optional). /// Postfix for every element (default: empty, optional). /// String with lines for all array entries. public static string CTDump(this T[] array, string prefix = "", string postfix = "") { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (T element in array) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(prefix); sb.Append(element); sb.Append(postfix); } return sb.ToString(); } /// /// Extension method for Quaternion-Arrays. /// Dumps an array to a string. /// /// Quaternion-Array-instance to dump. /// String with lines for all array entries. public static string CTDump(this Quaternion[] array) { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Quaternion element in array) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); sb.Append(", "); sb.Append(element.w); } return sb.ToString(); } /// /// Extension method for Vector2-Arrays. /// Dumps an array to a string. /// /// Vector2-Array-instance to dump. /// String with lines for all array entries. public static string CTDump(this Vector2[] array) { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector2 element in array) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); } return sb.ToString(); } /// /// Extension method for Vector3-Arrays. /// Dumps an array to a string. /// /// Vector3-Array-instance to dump. /// String with lines for all array entries. public static string CTDump(this Vector3[] array) { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector3 element in array) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); } return sb.ToString(); } /// /// Extension method for Vector4-Arrays. /// Dumps an array to a string. /// /// Vector4-Array-instance to dump. /// String with lines for all array entries. public static string CTDump(this Vector4[] array) { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector4 element in array) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); sb.Append(", "); sb.Append(element.w); } return sb.ToString(); } /// /// Extension method for Arrays. /// Generates a string array with all entries (via ToString). /// /// Array-instance to ToString. /// String array with all entries (via ToString). public static string[] CTToString(this T[] array) { if (array == null) // || array.Length <= 0) throw new System.ArgumentNullException(nameof(array)); string[] result = new string[array.Length]; for (int ii = 0; ii < array.Length; ii++) { result[ii] = null == array[ii] ? "null" : array[ii].ToString(); } return result; } #endregion #region Lists /// /// Extension method for IList. /// Shuffles a List. /// /// IList-instance to shuffle. /// Seed for the PRNG (default: 0 (=standard), optional) public static void CTShuffle(this System.Collections.Generic.IList list, int seed = 0) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Random rnd = seed == 0 ? new System.Random() : new System.Random(seed); int n = list.Count; while (n > 1) { int k = rnd.Next(n--); T temp = list[n]; list[n] = list[k]; list[k] = temp; } } /// /// Extension method for IList. /// Dumps a list to a string. /// /// IList-instance to dump. /// Prefix for every element (default: empty, optional). /// Postfix for every element (default: empty, optional). /// String with lines for all list entries. public static string CTDump(this System.Collections.Generic.IList list, string prefix = "", string postfix = "") { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (T element in list) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(prefix); sb.Append(element); sb.Append(postfix); } return sb.ToString(); } /// /// Extension method for Quaternion-IList. /// Dumps a list to a string. /// /// Quaternion-IList-instance to dump. /// String with lines for all list entries. public static string CTDump(this System.Collections.Generic.IList list) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Quaternion element in list) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); sb.Append(", "); sb.Append(element.w); } return sb.ToString(); } /// /// Extension method for Vector2-IList. /// Dumps a list to a string. /// /// Vector2-IList-instance to dump. /// String with lines for all list entries. public static string CTDump(this System.Collections.Generic.IList list) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector2 element in list) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); } return sb.ToString(); } /// /// Extension method for Vector3-IList. /// Dumps a list to a string. /// /// Vector3-IList-instance to dump. /// String with lines for all list entries. public static string CTDump(this System.Collections.Generic.IList list) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector3 element in list) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); } return sb.ToString(); } /// /// Extension method for Vector4-IList. /// Dumps a list to a string. /// /// Vector4-IList-instance to dump. /// String with lines for all list entries. public static string CTDump(this System.Collections.Generic.IList list) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Vector4 element in list) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(element.x); sb.Append(", "); sb.Append(element.y); sb.Append(", "); sb.Append(element.z); sb.Append(", "); sb.Append(element.w); } return sb.ToString(); } /// /// Extension method for IList. /// Generates a string list with all entries (via ToString). /// /// IList-instance to ToString. /// String list with all entries (via ToString). public static System.Collections.Generic.List CTToString(this System.Collections.Generic.IList list) { if (list == null) throw new System.ArgumentNullException(nameof(list)); System.Collections.Generic.List result = new System.Collections.Generic.List(list.Count); result.AddRange(list.Select(element => null == element ? "null" : element.ToString())); return result; } #endregion #region Dictionaries /// /// Extension method for IDictionary. /// Dumps a dictionary to a string. /// /// IDictionary-instance to dump. /// Prefix for every element (default: empty, optional). /// Postfix for every element (default: empty, optional). /// String with lines for all dictionary entries. public static string CTDump(this System.Collections.Generic.IDictionary dict, string prefix = "", string postfix = "") { if (dict == null) throw new System.ArgumentNullException(nameof(dict)); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (System.Collections.Generic.KeyValuePair kvp in dict) { if (0 < sb.Length) sb.Append(System.Environment.NewLine); sb.Append(prefix); sb.Append("Key = "); sb.Append(kvp.Key); sb.Append(", Value = "); sb.Append(kvp.Value); sb.Append(postfix); } return sb.ToString(); } /// /// Extension method for IDictionary. /// Adds a dictionary to an existing one. /// /// IDictionary-instance. /// Dictionary to add. public static void CTAddRange(this System.Collections.Generic.IDictionary dict, System.Collections.Generic.IDictionary collection) { if (dict == null) throw new System.ArgumentNullException(nameof(dict)); if (collection == null) throw new System.ArgumentNullException(nameof(collection)); foreach (System.Collections.Generic.KeyValuePair item in collection) { if (!dict.ContainsKey(item.Key)) { dict.Add(item.Key, item.Value); } else { // handle duplicate key issue here Debug.LogWarning($"Duplicate key found: {item.Key}"); } } } #endregion #region Unity specific /// /// Extension method for Renderer. /// Determines if the renderer is visible from a certain camera. /// /// Renderer to test the visibility. /// Camera for the test. /// True if the renderer is visible by the given camera. public static bool CTIsVisibleFrom(this Renderer renderer, Camera camera) { if (renderer == null) throw new System.ArgumentNullException(nameof(renderer)); if (camera == null) throw new System.ArgumentNullException(nameof(camera)); Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera); return GeometryUtility.TestPlanesAABB(planes, renderer.bounds); } /// /// Extension method for Transform. /// Recursively searches all children of a parent transform for specific named transform /// /// Parent of the current children. /// Name of the transform. /// True if the renderer is visible by the given camera. public static Transform CTDeepSearch(Transform parent, string name) { if (parent == null) throw new System.ArgumentNullException(nameof(parent)); if (name == null) throw new System.ArgumentNullException(nameof(name)); Transform tf = parent.Find(name); if (tf != null) return tf; foreach (Transform child in parent) { tf = CTDeepSearch(child, name); if (tf != null) return tf; } return null; } #endregion #region Streams /// /// Extension method for Stream. /// Reads the full content of a Stream. /// /// Stream-instance to read. /// Buffer size in bytes (default: 16384, optional). /// Byte-array of the Stream content. public static byte[] CTReadFully(this System.IO.Stream input, int bufferSize = 16384) { if (input == null) throw new System.ArgumentNullException(nameof(input)); byte[] buffer = new byte[bufferSize]; using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { int read; while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, read); } return ms.ToArray(); } } #endregion /* /// /// Perform a deep Copy of the object. /// /// The type of object being copied. /// The object instance to copy. /// The copied object. public static T Clone(this T source) { if (!typeof(T).IsSerializable) { throw new ArgumentException("The type must be serializable.", "source"); } // Don't serialize a null object, simply return the default for that object if (Object.ReferenceEquals(source, null)) { return default(T); } IFormatter formatter = new BinaryFormatter(); Stream stream = new MemoryStream(); using (stream) { formatter.Serialize(stream, source); stream.Seek(0, SeekOrigin.Begin); return (T)formatter.Deserialize(stream); } } */ /* /// /// Clone a List with elememts containing a copy constructor. /// /// List-instance to clone. /// Clones list. public static List CTClone(this List listToClone) where T : ICopyable { List newList = new List(listToClone.Count); listToClone.ForEach((item) => { newList.Add(new T(item)); }); return newList; //return listToClone.Select(item => (T)item.Clone()).ToList(); } */ /* public static string[] CTToUppercase(string[] array) { if (array == null || array.Length <= 0) throw new ArgumentNullException("array"); string[] result = new string[array.Length]; for (int ii = 0; ii < array.Length; ii++) { result[ii] = array[ii].ToUpper(); } return result; } public static string[] CTToLowercase(string[] array) { if (array == null || array.Length <= 0) throw new ArgumentNullException("array"); string[] result = new string[array.Length]; for (int ii = 0; ii < array.Length; ii++) { result[ii] = array[ii].ToLower(); } return result; } */ } } // © 2016-2020 crosstales LLC (https://www.crosstales.com)