345 lines
14 KiB
C#
345 lines
14 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Globalization;
|
|
using System.Security.Cryptography;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Text.Json;
|
|
|
|
namespace ExtensionMethods {
|
|
public static class StringExtensions {
|
|
|
|
/// <summary>
|
|
/// SQL Like implementation
|
|
/// </summary>
|
|
/// <param name="text"></param>
|
|
/// <param name="wildcardedText"></param>
|
|
/// <returns></returns>
|
|
public static bool Like(this string text, string wildcardedText) {
|
|
text = text ?? "";
|
|
wildcardedText = wildcardedText ?? "";
|
|
|
|
return Regex.IsMatch(text, wildcardedText.WildcardToRegular(), RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts wildcarded string tgo regex
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
private static string WildcardToRegular(this string value) =>
|
|
$"^{Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*")}$";
|
|
|
|
/// <summary>
|
|
/// VB.Net Left implementation
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="count"></param>
|
|
/// <returns></returns>
|
|
public static string Left(this string s, int count) {
|
|
return s.Substring(0, count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// VB.Net Right implementation
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="count"></param>
|
|
/// <returns></returns>
|
|
public static string Right(this string s, int count) {
|
|
return s.Substring(s.Length - count, count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// VB.Net Mid implementation
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="index"></param>
|
|
/// <param name="count"></param>
|
|
/// <returns></returns>
|
|
public static string Mid(this string s, int index, int count) {
|
|
return s.Substring(index, count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// VB.Net ToInteger implementation
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
public static int ToInteger(this string s) {
|
|
var integerValue = 0;
|
|
int.TryParse(s, out integerValue);
|
|
return integerValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// VB.Net IsInteger implementations
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
public static bool IsInteger(this string s) => new Regex("^-[0-9]+$|^[0-9]+$").Match(s).Success;
|
|
|
|
public static StringBuilder Prepend(this StringBuilder sb, string content) => sb.Insert(0, content);
|
|
|
|
public static T ToEnum<T>(this string input) where T : struct {
|
|
var result = default(T);
|
|
|
|
if (string.IsNullOrWhiteSpace(input))
|
|
return result;
|
|
|
|
bool parseSucceeded = Enum.TryParse(input, true, out result);
|
|
|
|
if (parseSucceeded)
|
|
return result;
|
|
|
|
// se fallisce il parse per valore proviamo ad usare il DisplayAttribute.Name
|
|
var enumType = typeof(T);
|
|
|
|
foreach (T enumItem in Enum.GetValues(enumType)) {
|
|
var att = enumType.GetMember(enumItem.ToString())[0]
|
|
.GetCustomAttributes(typeof(DisplayAttribute), false).SingleOrDefault();
|
|
|
|
var displayName = ((DisplayAttribute)att).GetName();
|
|
|
|
if (input.Equals(displayName, StringComparison.InvariantCultureIgnoreCase))
|
|
return enumItem;
|
|
}
|
|
throw new NotSupportedException($"Cannot parse the value '{input}' for {enumType}");
|
|
}
|
|
|
|
public static T? ToNullableEnum<T>(this string input) where T : struct => !string.IsNullOrWhiteSpace(input) ? input.ToEnum<T>() : default(T?);
|
|
|
|
public static string ToNull(this string s) => !string.IsNullOrWhiteSpace(s) ? s : null;
|
|
|
|
public static string NullIfEmptyString(this string s) => s.ToNull();
|
|
|
|
public static long ToLong(this string s) {
|
|
if (!long.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out long result))
|
|
result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default;
|
|
return result;
|
|
}
|
|
|
|
public static long? ToNullableLong(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToLong() : default(long?);
|
|
|
|
public static int ToInt(this string s) {
|
|
if (!int.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out int result))
|
|
result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default;
|
|
return result;
|
|
}
|
|
|
|
public static int? ToNullableInt(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToInt() : default(int?);
|
|
|
|
public static uint ToUint(this string s) {
|
|
if (!uint.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out uint result))
|
|
result = !string.IsNullOrWhiteSpace(s) ? (uint)s.ToUpper().GetHashCode() : default;
|
|
return result;
|
|
}
|
|
|
|
public static uint? ToNullableUint(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToUint() : default(uint?);
|
|
|
|
public static decimal ToDecimal(this string s) {
|
|
if (!int.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out int result))
|
|
result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default;
|
|
return result;
|
|
}
|
|
|
|
public static decimal? ToNullableDecimal(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDecimal() : default(decimal?);
|
|
|
|
public static double ToDouble(this string s) {
|
|
if (!double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out double result))
|
|
result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default;
|
|
return result;
|
|
}
|
|
|
|
public static double? ToNullableDouble(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDouble() : default(double?);
|
|
|
|
#region DateTime
|
|
public static DateTime ToDate(this string s, string[] formats) {
|
|
return DateTime.TryParseExact(s, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime datetime)
|
|
? DateTime.SpecifyKind(datetime, DateTimeKind.Utc)
|
|
: throw new ApplicationException($"the date [{s}] is not in required format: [{formats[0]}]");
|
|
}
|
|
|
|
public static DateTime ToDate(this string s) => s.ToDate(new string[] { "dd/MM/yyyy" });
|
|
|
|
public static DateTime? ToNullableDate(this string s) => !string.IsNullOrEmpty(s) ? s.ToDate() : default(DateTime?);
|
|
|
|
public static DateTime? ToNullableDate(this string s, string[] formats) => !string.IsNullOrEmpty(s) ? s.ToDate(formats) : default(DateTime?);
|
|
|
|
public static DateTime ToDateTime(this string s, string[] formats) {
|
|
if (s == "Now")
|
|
return DateTime.Now;
|
|
else if (s == "Today")
|
|
return DateTime.Today;
|
|
|
|
DateTime result;
|
|
|
|
if (!DateTime.TryParseExact(s, formats, new DateTimeFormatInfo(), DateTimeStyles.None, out result))
|
|
throw new ApplicationException(string.Format("unable to parse exact date from value [{0}] with formats [{1}]", s, string.Join(", ", formats)));
|
|
|
|
return DateTime.SpecifyKind(result, DateTimeKind.Utc);
|
|
}
|
|
|
|
public static DateTime ToDateTime(this string s) => s.ToDateTime(new string[] { "dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss", "dd/MM/yyyy HH:mm", "yyyy-MM-dd'T'HH:mm:ss'Z'" });
|
|
|
|
public static DateTime? ToNullableDateTime(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDateTime() : default(DateTime?);
|
|
|
|
public static DateTime? ToNullableDateTime(this string s, string[] formats) => !string.IsNullOrWhiteSpace(s) ? s.ToDateTime(formats) : default(DateTime?);
|
|
#endregion
|
|
public static bool ToBool(this string s) => new string[] { "ok", "yes", "y", "true", "1" }.Any(x => string.Equals(x, s, StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
public static bool? ToNullableBool(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToBool() : default(bool?);
|
|
|
|
public static Guid ToGuid(this string text) {
|
|
if (Guid.TryParse(text, out var value))
|
|
return value;
|
|
|
|
using var md5 = MD5.Create();
|
|
byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(text.ToUpper()));
|
|
return new Guid(hash);
|
|
}
|
|
|
|
public static Guid? ToNullableGuid(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToGuid() : default(Guid?);
|
|
|
|
public static string[] StringSplit(this string s, char c) {
|
|
return s.Split(c).Select(x => x.Trim()).ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
public static string ToTitle(this string s) {
|
|
if (!string.IsNullOrWhiteSpace(s)) {
|
|
var tmp = s.ToCharArray();
|
|
tmp[0] = char.ToUpper(tmp[0]);
|
|
s = new string(tmp);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
public static IEnumerable<Uri> ExtractUrls(this string s) {
|
|
var urls = new List<Uri>();
|
|
|
|
foreach (Match match in Regex.Matches(s, @"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])")) {
|
|
if (Uri.TryCreate(match.Value, UriKind.Absolute, out Uri uri)) {
|
|
urls.Add(uri);
|
|
}
|
|
}
|
|
|
|
return urls.Distinct();
|
|
}
|
|
|
|
public static string Format(this string s, params object[] args) => string.Format(s, args);
|
|
|
|
/// <summary>
|
|
/// Adds elipsis
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="length"></param>
|
|
/// <returns></returns>
|
|
public static string Excerpt(this string s, int length = 60) =>
|
|
string.IsNullOrEmpty(s) || s.Length < length ? s : s.Substring(0, length - 3) + "...";
|
|
|
|
/// <summary>
|
|
/// Converts JSON string to object
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
public static T? ToObject<T>(this string s) => JsonSerializer.Deserialize<T>(s, new JsonSerializerOptions {
|
|
PropertyNameCaseInsensitive = true
|
|
});
|
|
|
|
/// <summary>
|
|
/// Adapted from .Net [EmailAddres] model validation attribute
|
|
/// https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/EmailAddressAttribute.cs,17f9a2457ddc8d93
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
public static bool IsValidEmail(this string? s) {
|
|
Regex CreateRegEx() {
|
|
const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
|
|
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
|
|
|
// Set explicit regex match timeout, sufficient enough for email parsing
|
|
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
|
|
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
|
|
|
|
try {
|
|
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
|
|
return new Regex(pattern, options, matchTimeout);
|
|
}
|
|
}
|
|
catch {
|
|
// Fallback on error
|
|
}
|
|
|
|
// Legacy fallback (without explicit match timeout)
|
|
return new Regex(pattern, options);
|
|
}
|
|
|
|
// This attribute provides server-side email validation equivalent to jquery validate,
|
|
// and therefore shares the same regular expression. See unit tests for examples.
|
|
var _regex = CreateRegEx();
|
|
|
|
if (s == null)
|
|
return false;
|
|
|
|
return s != null && _regex.Match(s).Length > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert HTML string to Plain Text without external libraries
|
|
/// https://docs.microsoft.com/en-us/answers/questions/594274/convert-html-text-to-plain-text-in-c.html
|
|
/// </summary>
|
|
/// <param name="htmlCode"></param>
|
|
/// <returns></returns>
|
|
public static string HTMLToPlainText(this string htmlCode) {
|
|
|
|
// Remove new lines since they are not visible in HTML
|
|
htmlCode = htmlCode.Replace("\n", " ");
|
|
|
|
// Remove tab spaces
|
|
htmlCode = htmlCode.Replace("\t", " ");
|
|
|
|
// Remove multiple white spaces from HTML
|
|
htmlCode = Regex.Replace(htmlCode, "\\s+", " ");
|
|
|
|
// Remove HEAD tag
|
|
htmlCode = Regex.Replace(htmlCode, "<head.*?</head>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
|
|
|
// Remove any JavaScript
|
|
htmlCode = Regex.Replace(htmlCode, "<script.*?</script>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
|
|
|
// Replace special characters like &, <, >, " etc.
|
|
StringBuilder sbHTML = new StringBuilder(htmlCode);
|
|
|
|
// Note: There are many more special characters, these are just
|
|
// most common. You can add new characters in this arrays if needed
|
|
string[] OldWords = {" ", "&", """, "<", ">", "®", "©", "•", "™","'"};
|
|
string[] NewWords = { " ", "&", "\"", "<", ">", "®", "©", "•", "™", "\'" };
|
|
|
|
for (int i = 0; i < OldWords.Length; i++) {
|
|
sbHTML.Replace(OldWords[i], NewWords[i]);
|
|
}
|
|
|
|
// Check if there are line breaks (<br>) or paragraph (<p>)
|
|
sbHTML.Replace("<br>", "\n<br>");
|
|
sbHTML.Replace("<br ", "\n<br ");
|
|
sbHTML.Replace("<p ", "\n<p ");
|
|
|
|
// Finally, remove all HTML tags and return plain text
|
|
var plainText = Regex.Replace(sbHTML.ToString(), "<[^>]*>", "");
|
|
|
|
// Remove external new lines
|
|
plainText = plainText.Trim();
|
|
|
|
return plainText;
|
|
}
|
|
}
|
|
} |