RaUI/Source/ryControls/HtmlRenderer/Core/Utils/CommonUtils.cs
zilinsoft 3262955f2f ### 2023-11-07更新
------
#### RaUIV4    V4.0.2311.0701
- *.[全新]整合了MyDb、ryControls、MyDb_MySQL等dll文件到RaUI一个项目。
- *.[新增]新增ApkOp类,可以轻松获取APK信息。
- *.[新增]新增JsonExt扩展类,让Json操作更简单。
- *.[新增]新增WebP类,可以支持webp格式的图片。
- *.[改进]ryQuickSQL中的AddField方法改为自动替换已存在的同名值。
- *.[修复]ryQuickSQL中的AddFieldCalc方法无法正常计算的BUG。
2023-11-07 16:37:53 +08:00

493 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
namespace TheArtOfDev.HtmlRenderer.Core.Utils
{
internal delegate void ActionInt<in T>(T obj);
internal delegate void ActionInt<in T1, in T2>(T1 arg1, T2 arg2);
internal delegate void ActionInt<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
/// <summary>
/// Utility methods for general stuff.
/// </summary>
internal static class CommonUtils
{
#region Fields and Consts
/// <summary>
/// Table to convert numbers into roman digits
/// </summary>
private static readonly string[,] _romanDigitsTable = new[,]
{
{ "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" },
{ "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" },
{ "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" },
{
"", "M", "MM", "MMM", "M(V)", "(V)", "(V)M",
"(V)MM", "(V)MMM", "M(X)"
}
};
private static readonly string[,] _hebrewDigitsTable = new[,]
{
{ "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט" },
{ "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ" },
{ "ק", "ר", "ש", "ת", "תק", "תר", "תש", "תת", "תתק", }
};
private static readonly string[,] _georgianDigitsTable = new[,]
{
{ "ა", "ბ", "გ", "დ", "ე", "ვ", "ზ", "ჱ", "თ" },
{ "ი", "პ", "ლ", "მ", "ნ", "ჲ", "ო", "პ", "ჟ" },
{ "რ", "ს", "ტ", "ჳ", "ფ", "ქ", "ღ", "", "შ" }
};
private static readonly string[,] _armenianDigitsTable = new[,]
{
{ "Ա", "Բ", "Գ", "Դ", "Ե", "Զ", "Է", "Ը", "Թ" },
{ "Ժ", "Ի", "Լ", "Խ", "Ծ", "Կ", "Հ", "Ձ", "Ղ" },
{ "Ճ", "Մ", "Յ", "Ն", "Շ", "Ո", "Չ", "Պ", "Ջ" }
};
private static readonly string[] _hiraganaDigitsTable = new[]
{
"あ", "ぃ", "ぅ", "ぇ", "ぉ", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た", "ち", "つ", "て", "と", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み", "む", "め", "も", "ゃ", "ゅ", "ょ", "ら", "り", "る", "れ", "ろ", "ゎ", "ゐ", "ゑ", "を", "ん"
};
private static readonly string[] _satakanaDigitsTable = new[]
{
"ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヰ", "ヱ", "ヲ", "ン"
};
#endregion
/// <summary>
/// Check if the given char is of Asian range.
/// </summary>
/// <param name="ch">the character to check</param>
/// <returns>true - Asian char, false - otherwise</returns>
public static bool IsAsianCharecter(char ch)
{
return ch >= 0x4e00 && ch <= 0xFA2D;
}
/// <summary>
/// Check if the given char is a digit character (0-9) and (0-9, a-f for HEX)
/// </summary>
/// <param name="ch">the character to check</param>
/// <param name="hex">optional: is hex digit check</param>
/// <returns>true - is digit, false - not a digit</returns>
public static bool IsDigit(char ch, bool hex = false)
{
return (ch >= '0' && ch <= '9') || (hex && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')));
}
/// <summary>
/// Convert the given char to digit.
/// </summary>
/// <param name="ch">the character to check</param>
/// <param name="hex">optional: is hex digit check</param>
/// <returns>true - is digit, false - not a digit</returns>
public static int ToDigit(char ch, bool hex = false)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (hex)
{
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
}
return 0;
}
/// <summary>
/// Get size that is max of <paramref name="size"/> and <paramref name="other"/> for width and height separately.
/// </summary>
public static RSize Max(RSize size, RSize other)
{
return new RSize(Math.Max(size.Width, other.Width), Math.Max(size.Height, other.Height));
}
/// <summary>
/// Get Uri object for the given path if it is valid uri path.
/// </summary>
/// <param name="path">the path to get uri for</param>
/// <returns>uri or null if not valid</returns>
public static Uri TryGetUri(string path)
{
try
{
if (Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute))
{
return new Uri(path);
}
}
catch
{ }
return null;
}
/// <summary>
/// Get the first value in the given dictionary.
/// </summary>
/// <typeparam name="TKey">the type of dictionary key</typeparam>
/// <typeparam name="TValue">the type of dictionary value</typeparam>
/// <param name="dic">the dictionary</param>
/// <param name="defaultValue">optional: the default value to return of no elements found in dictionary </param>
/// <returns>first element or default value</returns>
public static TValue GetFirstValueOrDefault<TKey, TValue>(IDictionary<TKey, TValue> dic, TValue defaultValue = default(TValue))
{
if (dic != null)
{
foreach (var value in dic)
return value.Value;
}
return defaultValue;
}
/// <summary>
/// Get file info object for the given path if it is valid file path.
/// </summary>
/// <param name="path">the path to get file info for</param>
/// <returns>file info or null if not valid</returns>
public static FileInfo TryGetFileInfo(string path)
{
try
{
return new FileInfo(path);
}
catch
{ }
return null;
}
/// <summary>
/// Get web client response content type.
/// </summary>
/// <param name="client">the web client to get the response content type from</param>
/// <returns>response content type or null</returns>
public static string GetResponseContentType(WebClient client)
{
foreach (string header in client.ResponseHeaders)
{
if (header.Equals("Content-Type", StringComparison.InvariantCultureIgnoreCase))
return client.ResponseHeaders[header];
}
return null;
}
/// <summary>
/// Gets the representation of the online uri on the local disk.
/// </summary>
/// <param name="imageUri">The online image uri.</param>
/// <returns>The path of the file on the disk.</returns>
public static FileInfo GetLocalfileName(Uri imageUri)
{
StringBuilder fileNameBuilder = new StringBuilder();
string absoluteUri = imageUri.AbsoluteUri;
int lastSlash = absoluteUri.LastIndexOf('/');
if (lastSlash == -1)
{
return null;
}
string uriUntilSlash = absoluteUri.Substring(0, lastSlash);
fileNameBuilder.Append(uriUntilSlash.GetHashCode().ToString());
fileNameBuilder.Append('_');
string restOfUri = absoluteUri.Substring(lastSlash + 1);
int indexOfParams = restOfUri.IndexOf('?');
if (indexOfParams == -1)
{
string ext = ".cache";
int indexOfDot = restOfUri.IndexOf('.');
if (indexOfDot > -1)
{
ext = restOfUri.Substring(indexOfDot);
restOfUri = restOfUri.Substring(0, indexOfDot);
}
fileNameBuilder.Append(restOfUri);
fileNameBuilder.Append(ext);
}
else
{
int indexOfDot = restOfUri.IndexOf('.');
if (indexOfDot == -1 || indexOfDot > indexOfParams)
{
//The uri is not for a filename
fileNameBuilder.Append(restOfUri);
fileNameBuilder.Append(".cache");
}
else if (indexOfParams > indexOfDot)
{
//Adds the filename without extension.
fileNameBuilder.Append(restOfUri, 0, indexOfDot);
//Adds the parameters
fileNameBuilder.Append(restOfUri, indexOfParams, restOfUri.Length - indexOfParams);
//Adds the filename extension.
fileNameBuilder.Append(restOfUri, indexOfDot, indexOfParams - indexOfDot);
}
}
var validFileName = GetValidFileName(fileNameBuilder.ToString());
if (validFileName.Length > 25)
{
validFileName = validFileName.Substring(0, 24) + validFileName.Substring(24).GetHashCode() + Path.GetExtension(validFileName);
}
return new FileInfo(Path.Combine(Path.GetTempPath(), validFileName));
}
/// <summary>
/// Get substring seperated by whitespace starting from the given idex.
/// </summary>
/// <param name="str">the string to get substring in</param>
/// <param name="idx">the index to start substring search from</param>
/// <param name="length">return the length of the found string</param>
/// <returns>the index of the substring, -1 if no valid sub-string found</returns>
public static int GetNextSubString(string str, int idx, out int length)
{
while (idx < str.Length && Char.IsWhiteSpace(str[idx]))
idx++;
if (idx < str.Length)
{
var endIdx = idx + 1;
while (endIdx < str.Length && !Char.IsWhiteSpace(str[endIdx]))
endIdx++;
length = endIdx - idx;
return idx;
}
length = 0;
return -1;
}
/// <summary>
/// Compare that the substring of <paramref name="str"/> is equal to <paramref name="str2"/>
/// Assume given substring is not empty and all indexes are valid!<br/>
/// </summary>
/// <returns>true - equals, false - not equals</returns>
public static bool SubStringEquals(string str, int idx, int length, string str2)
{
if (length == str2.Length && idx + length <= str.Length)
{
for (int i = 0; i < length; i++)
{
if (Char.ToLowerInvariant(str[idx + i]) != Char.ToLowerInvariant(str2[i]))
return false;
}
return true;
}
return false;
}
/// <summary>
/// Replaces invalid filename chars to '_'
/// </summary>
/// <param name="source">The possibly-not-valid filename</param>
/// <returns>A valid filename.</returns>
private static string GetValidFileName(string source)
{
string retVal = source;
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
foreach (var invalidFileNameChar in invalidFileNameChars)
{
retVal = retVal.Replace(invalidFileNameChar, '_');
}
return retVal;
}
/// <summary>
/// Convert number to alpha numeric system by the requested style (UpperAlpha, LowerRoman, Hebrew, etc.).
/// </summary>
/// <param name="number">the number to convert</param>
/// <param name="style">the css style to convert by</param>
/// <returns>converted string</returns>
public static string ConvertToAlphaNumber(int number, string style = CssConstants.UpperAlpha)
{
if (number == 0)
return string.Empty;
if (style.Equals(CssConstants.LowerGreek, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToGreekNumber(number);
}
else if (style.Equals(CssConstants.LowerRoman, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToRomanNumbers(number, true);
}
else if (style.Equals(CssConstants.UpperRoman, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToRomanNumbers(number, false);
}
else if (style.Equals(CssConstants.Armenian, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToSpecificNumbers(number, _armenianDigitsTable);
}
else if (style.Equals(CssConstants.Georgian, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToSpecificNumbers(number, _georgianDigitsTable);
}
else if (style.Equals(CssConstants.Hebrew, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToSpecificNumbers(number, _hebrewDigitsTable);
}
else if (style.Equals(CssConstants.Hiragana, StringComparison.InvariantCultureIgnoreCase) || style.Equals(CssConstants.HiraganaIroha, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToSpecificNumbers2(number, _hiraganaDigitsTable);
}
else if (style.Equals(CssConstants.Katakana, StringComparison.InvariantCultureIgnoreCase) || style.Equals(CssConstants.KatakanaIroha, StringComparison.InvariantCultureIgnoreCase))
{
return ConvertToSpecificNumbers2(number, _satakanaDigitsTable);
}
else
{
var lowercase = style.Equals(CssConstants.LowerAlpha, StringComparison.InvariantCultureIgnoreCase) || style.Equals(CssConstants.LowerLatin, StringComparison.InvariantCultureIgnoreCase);
return ConvertToEnglishNumber(number, lowercase);
}
}
/// <summary>
/// Convert the given integer into alphabetic numeric format (D, AU, etc.)
/// </summary>
/// <param name="number">the number to convert</param>
/// <param name="lowercase">is to use lowercase</param>
/// <returns>the roman number string</returns>
private static string ConvertToEnglishNumber(int number, bool lowercase)
{
var sb = string.Empty;
int alphStart = lowercase ? 97 : 65;
while (number > 0)
{
var n = number % 26 - 1;
if (n >= 0)
{
sb = (Char)(alphStart + n) + sb;
number = number / 26;
}
else
{
sb = (Char)(alphStart + 25) + sb;
number = (number - 1) / 26;
}
}
return sb;
}
/// <summary>
/// Convert the given integer into alphabetic numeric format (alpha, AU, etc.)
/// </summary>
/// <param name="number">the number to convert</param>
/// <returns>the roman number string</returns>
private static string ConvertToGreekNumber(int number)
{
var sb = string.Empty;
while (number > 0)
{
var n = number % 24 - 1;
if (n > 16)
n++;
if (n >= 0)
{
sb = (Char)(945 + n) + sb;
number = number / 24;
}
else
{
sb = (Char)(945 + 24) + sb;
number = (number - 1) / 25;
}
}
return sb;
}
/// <summary>
/// Convert the given integer into roman numeric format (II, VI, IX, etc.)
/// </summary>
/// <param name="number">the number to convert</param>
/// <param name="lowercase">if to use lowercase letters for roman digits</param>
/// <returns>the roman number string</returns>
private static string ConvertToRomanNumbers(int number, bool lowercase)
{
var sb = string.Empty;
for (int i = 1000, j = 3; i > 0; i /= 10, j--)
{
int digit = number / i;
sb += string.Format(_romanDigitsTable[j, digit]);
number -= digit * i;
}
return lowercase ? sb.ToLower() : sb;
}
/// <summary>
/// Convert the given integer into given alphabet numeric system.
/// </summary>
/// <param name="number">the number to convert</param>
/// <param name="alphabet">the alphabet system to use</param>
/// <returns>the number string</returns>
private static string ConvertToSpecificNumbers(int number, string[,] alphabet)
{
int level = 0;
var sb = string.Empty;
while (number > 0 && level < alphabet.GetLength(0))
{
var n = number % 10;
if (n > 0)
sb = alphabet[level, number % 10 - 1].ToString(CultureInfo.InvariantCulture) + sb;
number /= 10;
level++;
}
return sb;
}
/// <summary>
/// Convert the given integer into given alphabet numeric system.
/// </summary>
/// <param name="number">the number to convert</param>
/// <param name="alphabet">the alphabet system to use</param>
/// <returns>the number string</returns>
private static string ConvertToSpecificNumbers2(int number, string[] alphabet)
{
for (int i = 20; i > 0; i--)
{
if (number > 49 * i - i + 1)
number++;
}
var sb = string.Empty;
while (number > 0)
{
sb = alphabet[Math.Max(0, number % 49 - 1)].ToString(CultureInfo.InvariantCulture) + sb;
number /= 49;
}
return sb;
}
}
}