// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Nova.Utilities
{
/// <summary>
/// Static helper methods for the <see cref="string"/> class.
/// </summary>
public static class StringUtil
{
#region /* STATIC HELPER METHODS */
/// <summary>
/// Check if the specified string is empty, blank, or null.
/// </summary>
/// <param name="thisStr">The string to check.</param>
/// <returns>True if string is empty, blank, or null.</returns>
public static bool IsEmpty(string thisStr)
{
return (thisStr == null || thisStr.Trim() == "");
}
/// <summary>
/// Check if the specified string is NOT empty, blank, or null.
/// </summary>
/// <param name="thisStr">The string to check.</param>
/// <returns>True if string is NOT empty, blank, or null.</returns>
public static bool NotEmpty(string thisStr)
{
return !(thisStr == null || thisStr.Trim() == "");
}
/// <summary>
/// Return null if the string is empty, otherwise leave it unchanged.
/// </summary>
/// <param name="thisStr">The string to check.</param>
/// <returns>Null if the string is empty, otherwise the unchanged string.</returns>
public static string EmptyAsNull(string thisStr)
{
return (thisStr.Trim() == "" ? null : thisStr);
}
/// <summary>
/// Get the length of the specified string, or 0 if it's null.
/// </summary>
/// <param name="thisStr">The string to check.</param>
/// <returns>String length or 0 if null.</returns>
public static int NNLength(string thisStr)
{
return (thisStr != null ? thisStr.Length : 0);
}
/// <summary>
/// Compare if one string equals another, ignoring case and leading/trailing whitespace, and
/// treating null and empty strings as equivalent.
/// </summary>
/// <param name="thisStr">The string to be compared.</param>
/// <param name="str">The string to be compared to.</param>
/// <returns>True if the strings are equal, otherwise false.</returns>
public static bool NNEqualsIgnoreCase(string thisStr, string str)
{
return string.Equals((thisStr ?? "").Trim(), (str ?? "").Trim(), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Check if the string contains the specified character, returning false if the string is null.
/// </summary>
/// <param name="thisStr">The string being operated on.</param>
/// <param name="ch">The character being searched for.</param>
/// <returns>True if the string contains the specified character, otherwise false.</returns>
public static bool Contains(string thisStr, char ch)
{
if (thisStr != null)
return (thisStr.IndexOf(ch) >= 0);
return false;
}
/// <summary>
/// Check if the string contains the specified substring using a case-insensitive compare, returning false if the string is null.
/// </summary>
/// <param name="thisStr">The string being operated on.</param>
/// <param name="subString">The sub-string being searched for.</param>
/// <returns>True if the string contains the specified substring, otherwise false.</returns>
public static bool ContainsIgnoreCase(string thisStr, string subString)
{
if (thisStr != null)
return (thisStr.IndexOf(subString, StringComparison.CurrentCultureIgnoreCase) >= 0);
return false;
}
/// <summary>
/// Return the number of consecutive specified chars at the specified starting index.
/// </summary>
/// <param name="thisStr">The string being operated on.</param>
/// <param name="ch">The desired char.</param>
/// <param name="startIndex">The starting index.</param>
/// <returns>The number of chars found.</returns>
public static int CharCount(string thisStr, char ch, int startIndex)
{
int count = 0;
if (thisStr != null)
{
int length = thisStr.Length;
for (int i = startIndex; i < length && thisStr[i] == ch; ++i)
++count;
}
return count;
}
/// <summary>
/// Convert all runs of spaces and tabs to a single space, and trim all leading and trailing whitespace.
/// </summary>
/// <param name="thisStr">The string to be normalized.</param>
/// <returns>The normalized string. An empty string is returned if the input string is null.</returns>
public static string NormalizeWhitespace(string thisStr)
{
return Regex.Replace((thisStr ?? "").Trim(), "[ \t]+", " ");
}
/// <summary>
/// Append one string to another, using an optional separator string if the destination string isn't empty.
/// Treats both destination and source strings as empty if null.
/// </summary>
/// <param name="thisStr">The string to append to.</param>
/// <param name="separator">The optional separator string to use if the destination string isn't empty.</param>
/// <param name="str">The string to be appended.</param>
/// <returns>The new string.</returns>
public static string Append(string thisStr, string separator, string str)
{
return (string.IsNullOrEmpty(thisStr) ? str : (thisStr + separator + str));
}
/// <summary>
/// Format the specified collection into a single string using the specified separating
/// string to separate each item. Null items are converted to empty strings.
/// </summary>
/// <param name="thisCollection">Collection to be converted to a string.</param>
/// <param name="separator">Separating string to be inserted between items.</param>
/// <returns>The collection contents formatted as a string.</returns>
public static string ToString(IEnumerable thisCollection, string separator)
{
string result = "";
if (thisCollection != null)
{
foreach (object obj in thisCollection)
{
if (obj != null)
result = Append(result, separator, obj.ToString());
}
}
return result;
}
/// <summary>
/// Check if the specified string array contains the specified string.
/// </summary>
/// <param name="thisArray">The array to be searched.</param>
/// <param name="value">The string value to search for.</param>
/// <returns>True if the string array contains the specified value.</returns>
public static bool Contains(string[] thisArray, string value)
{
bool contains = false;
if (thisArray != null)
{
foreach (string str in thisArray)
{
if (str == value)
{
contains = true;
break;
}
}
}
return contains;
}
/// <summary>
/// Convert a string to an int, returning a default value if the string is null or isn't a valid integer value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails.</param>
/// <returns>The parsed int value.</returns>
public static int ParseInt(string thisStr, int defaultValue)
{
int val;
return (int.TryParse(thisStr, out val) ? val : defaultValue);
}
/// <summary>
/// Convert a string to an int, defaulting to 0 if the string is null or isn't a valid integer value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <returns>The parsed value.</returns>
public static int ParseInt(string thisStr)
{
return ParseInt(thisStr, 0);
}
/// <summary>
/// Convert a string to a bool, returning a default value if the string is null or isn't a valid bool value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails.</param>
/// <returns>The parsed bool value.</returns>
public static bool ParseBool(string thisStr, bool defaultValue)
{
bool result = defaultValue;
if (thisStr != null)
{
if (NNEqualsIgnoreCase(thisStr, "true") || thisStr.Trim() == "1" || NNEqualsIgnoreCase(thisStr, "yes") || NNEqualsIgnoreCase(thisStr, "y"))
result = true;
else if (NNEqualsIgnoreCase(thisStr, "false") || thisStr.Trim() == "0" || NNEqualsIgnoreCase(thisStr, "no") || NNEqualsIgnoreCase(thisStr, "n"))
result = false;
}
return result;
}
/// <summary>
/// Convert a string to a bool, returning false if the string is null or isn't a valid bool value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <returns>The parsed bool value.</returns>
public static bool ParseBool(string thisStr)
{
return ParseBool(thisStr, false);
}
/// <summary>
/// Convert a string to a double, returning a default value if the string is null or isn't a valid double value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails.</param>
/// <returns>The parsed double value.</returns>
public static double ParseDouble(string thisStr, double defaultValue)
{
double val;
return (double.TryParse(thisStr, out val) ? val : defaultValue);
}
/// <summary>
/// Convert a string to a double, defaulting to 0 if the string is null or isn't a valid double value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <returns>The parsed value.</returns>
public static double ParseDouble(string thisStr)
{
return ParseDouble(thisStr, 0);
}
/// <summary>
/// Convert a string to a DateTime, returning a default value if the string is null or isn't a valid DateTime value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails.</param>
/// <returns>The parsed DateTime value.</returns>
public static DateTime ParseDateTime(string thisStr, DateTime defaultValue)
{
DateTime val;
return (DateTime.TryParse(thisStr, out val) ? val : defaultValue);
}
/// <summary>
/// Convert a string to a DateTime, returning DateTime.MinValue if the string is null or isn't a valid DateTime value.
/// </summary>
/// <param name="thisStr">The string to parse.</param>
/// <returns>The parsed DateTime value.</returns>
public static DateTime ParseDateTime(string thisStr)
{
return ParseDateTime(thisStr, DateTime.MinValue);
}
/// <summary>
/// Convert a string to an enum value, returning a default value if the string is null or isn't a valid enum value.
/// </summary>
/// <typeparam name="T">The type of the enum.</typeparam>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails.</param>
/// <returns>The parsed enum value.</returns>
public static T ParseEnum<T>(string thisStr, T defaultValue) where T : struct
{
#if !TARGET_FRAMEWORK_3_5
T result;
return (Enum.TryParse(thisStr, out result) ? result : defaultValue);
#else
T result = defaultValue;
if (thisStr != null && thisStr.Count > 0)
{
try
{
result = (T)Enum.Parse(typeof(T), thisStr, true);
}
catch { }
}
return result;
#endif
}
/// <summary>
/// Convert a string to a generic value.
/// </summary>
/// <typeparam name="T">The generic type.</typeparam>
/// <param name="thisStr">The string to parse.</param>
/// <returns>The generic value</returns>
public static T Parse<T>(string thisStr) where T : IConvertible
{
return (T)Convert.ChangeType(thisStr, typeof(T));
}
/// <summary>
/// Convert a string to a generic value.
/// </summary>
/// <typeparam name="T">The generic type.</typeparam>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValue">The value to return if parsing fails</param>
/// <returns>The generic value</returns>
public static T Parse<T>(string thisStr, T defaultValue) where T : IConvertible
{
T result = defaultValue;
if (!string.IsNullOrEmpty(thisStr))
{
try
{
result = (T)Convert.ChangeType(thisStr, typeof(T));
}
catch { }
}
return result;
}
/// <summary>
/// Convert a comma delimited string to an array of generic values
/// </summary>
/// <typeparam name="T">The generic type.</typeparam>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValues">The values to return if parsing fails</param>
/// <param name="delim">The delimiter for fields in the string</param>
/// <returns>An array of generic values</returns>
public static T[] ParseArray<T>(string thisStr, T[] defaultValues, char delim) where T : IConvertible
{
T[] results = defaultValues;
if (!string.IsNullOrEmpty(thisStr))
{
try
{
IEnumerable<T> fields = Enumerable.Select<string, T>(thisStr.Split(delim), delegate(string text) { return Parse<T>(text); });
results = Enumerable.ToArray(fields);
}
catch { }
}
return results;
}
/// <summary>
/// Convert a comma delimited string to an array of generic values
/// </summary>
/// <typeparam name="T">The generic type.</typeparam>
/// <param name="thisStr">The string to parse.</param>
/// <param name="defaultValues">The values to return if parsing fails</param>
/// <returns>An array of generic values </returns>
public static T[] ParseArray<T>(string thisStr, T[] defaultValues) where T : IConvertible
{
return ParseArray(thisStr, defaultValues, ',');
}
/// <summary>
/// Expand any $(NAME) or %NAME% macros in the specified string with environment variable values.
/// </summary>
public static string ExpandEnvironmentMacros(string value)
{
if (!string.IsNullOrEmpty(value))
{
Regex regex = new Regex(@"%(\w+)%|\$\((\w+)\)");
MatchCollection matches = regex.Matches(value);
if (matches.Count > 0)
{
string result = null;
int index = 0;
for (int i = 0; i < matches.Count; ++i)
{
Match match = matches[i];
int skippedLength = match.Index - index;
result += value.Substring(index, skippedLength);
index += skippedLength;
for (int j = 1; j < match.Groups.Count; ++j)
{
Group group = match.Groups[j];
if (group.Length > 0)
{
string macro = group.Value;
result += Environment.GetEnvironmentVariable(macro);
}
}
index += match.Length;
}
result += value.Substring(index);
return result;
}
}
return value;
}
/// <summary>
/// Convert all unicode escape sequences in the specified string that represent letters or digits into chars.
/// </summary>
/// <param name="source">The string to be converted.</param>
/// <returns>A new string with unicode escapes converted to chars.</returns>
public static string ConvertUnicodeEscapes(string source)
{
StringBuilder result = null;
int i, start = 0;
for (i = 0; i < source.Length; )
{
if (source[i] == '\\')
{
// Check for "\uXXXX" or "\UXXXXXXXX"
int len = 0;
if (source[i + 1] == 'u')
len = 4;
else if (source[i + 1] == 'U')
len = 8;
if (len > 0)
{
try
{
if (CheckHex(source, i + 2, len))
{
// Convert 16-bit values to chars, or 32-bit values to surrogates (double chars)
if (result == null)
result = new StringBuilder();
result.Append(source, start, i - start);
string hex = source.Substring(i + 2, len);
if (len == 4)
{
ushort u16 = ushort.Parse(hex, NumberStyles.HexNumber);
result.Append((char)u16);
}
else
{
int u32 = int.Parse(hex, NumberStyles.HexNumber);
result.Append(char.ConvertFromUtf32(u32));
}
i += 2 + len;
start = i;
continue;
}
}
catch { }
}
}
++i;
}
return (result == null ? source : result.Append(source, start, i - start).ToString());
}
private static bool CheckHex(string str, int offset, int len)
{
for (int i = offset; i < offset + len; ++i)
{
if (!Uri.IsHexDigit(str[i]))
return false;
}
return true;
}
#endregion
}
}