From d9c495f322e36962aabe3fbe1075409adc49913d Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Wed, 5 Oct 2022 18:17:21 +0200 Subject: C#: Cleanup and sync `IsValid*` StringExtensions with core - Renamed `IsValidInteger` to `IsValidInt`. - Added `IsValidFileName`. - Added `IsValidHexNumber`. - Added support for IPv6 to `IsValidIPAddress`. - Added `ValidateNodeName`. - Updated the documentation of the `IsValid*` methods. --- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 181 ++++++++++++++++++--- 1 file changed, 159 insertions(+), 22 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 4988910fbd..92ea24f90b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Security; using System.Text; using System.Text.RegularExpressions; @@ -939,19 +940,94 @@ namespace Godot return instance.IsSubsequenceOf(text, caseSensitive: false); } + private static readonly char[] _invalidFileNameCharacters = { ':', '/', '\\', '?', '*', '"', '|', '%', '<', '>' }; + + /// + /// Returns if this string is free from characters that + /// aren't allowed in file names. + /// + /// The string to check. + /// If the string contains a valid file name. + public static bool IsValidFileName(this string instance) + { + var stripped = instance.Trim(); + if (instance != stripped) + return false; + + if (string.IsNullOrEmpty(stripped)) + return false; + + return instance.IndexOfAny(_invalidFileNameCharacters) == -1; + } + /// - /// Check whether the string contains a valid . + /// Returns if this string contains a valid . + /// This is inclusive of integers, and also supports exponents. /// + /// + /// + /// GD.Print("1.7".IsValidFloat()) // Prints "True" + /// GD.Print("24".IsValidFloat()) // Prints "True" + /// GD.Print("7e3".IsValidFloat()) // Prints "True" + /// GD.Print("Hello".IsValidFloat()) // Prints "False" + /// + /// /// The string to check. /// If the string contains a valid floating point number. public static bool IsValidFloat(this string instance) { - float f; - return float.TryParse(instance, out f); + return float.TryParse(instance, out _); + } + + /// + /// Returns if this string contains a valid hexadecimal number. + /// If is , then a validity of the + /// hexadecimal number is determined by 0x prefix, for instance: 0xDEADC0DE. + /// + /// The string to check. + /// If the string must contain the 0x prefix to be valid. + /// If the string contains a valid hexadecimal number. + public static bool IsValidHexNumber(this string instance, bool withPrefix = false) + { + if (string.IsNullOrEmpty(instance)) + return false; + + int from = 0; + if (instance.Length != 1 && instance[0] == '+' || instance[0] == '-') + { + from++; + } + + if (withPrefix) + { + if (instance.Length < 3) + return false; + if (instance[from] != '0' || instance[from + 1] != 'x') + return false; + from += 2; + } + + for (int i = from; i < instance.Length; i++) + { + char c = instance[i]; + if (IsHexDigit(c)) + continue; + + return false; + } + + return true; + + static bool IsHexDigit(char c) + { + return char.IsDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } } /// - /// Check whether the string contains a valid color in HTML notation. + /// Returns if this string contains a valid color in hexadecimal + /// HTML notation. Other HTML notations such as named colors or hsl() aren't + /// considered valid by this method and will return . /// /// The string to check. /// If the string contains a valid HTML color. @@ -961,10 +1037,17 @@ namespace Godot } /// - /// Check whether the string is a valid identifier. As is common in - /// programming languages, a valid identifier may contain only letters, - /// digits and underscores (_) and the first character may not be a digit. + /// Returns if this string is a valid identifier. + /// A valid identifier may contain only letters, digits and underscores (_) + /// and the first character may not be a digit. /// + /// + /// + /// GD.Print("good_ident_1".IsValidIdentifier()) // Prints "True" + /// GD.Print("1st_bad_ident".IsValidIdentifier()) // Prints "False" + /// GD.Print("bad_ident_#2".IsValidIdentifier()) // Prints "False" + /// + /// /// The string to check. /// If the string contains a valid identifier. public static bool IsValidIdentifier(this string instance) @@ -992,38 +1075,73 @@ namespace Godot } /// - /// Check whether the string contains a valid integer. + /// Returns if this string contains a valid . /// + /// + /// + /// GD.Print("7".IsValidInt()) // Prints "True" + /// GD.Print("14.6".IsValidInt()) // Prints "False" + /// GD.Print("L".IsValidInt()) // Prints "False" + /// GD.Print("+3".IsValidInt()) // Prints "True" + /// GD.Print("-12".IsValidInt()) // Prints "True" + /// + /// /// The string to check. /// If the string contains a valid integer. - public static bool IsValidInteger(this string instance) + public static bool IsValidInt(this string instance) { - int f; - return int.TryParse(instance, out f); + return int.TryParse(instance, out _); } /// - /// Check whether the string contains a valid IP address. + /// Returns if this string contains only a well-formatted + /// IPv4 or IPv6 address. This method considers reserved IP addresses such as + /// 0.0.0.0 as valid. /// /// The string to check. /// If the string contains a valid IP address. public static bool IsValidIPAddress(this string instance) { - // TODO: Support IPv6 addresses - string[] ip = instance.Split("."); + if (instance.Contains(':')) + { + string[] ip = instance.Split(':'); - if (ip.Length != 4) - return false; + for (int i = 0; i < ip.Length; i++) + { + string n = ip[i]; + if (n.Length == 0) + continue; + + if (n.IsValidHexNumber(withPrefix: false)) + { + long nint = n.HexToInt(); + if (nint < 0 || nint > 0xffff) + return false; + + continue; + } - for (int i = 0; i < ip.Length; i++) + if (!n.IsValidIPAddress()) + return false; + } + } + else { - string n = ip[i]; - if (!n.IsValidInteger()) - return false; + string[] ip = instance.Split('.'); - int val = n.ToInt(); - if (val < 0 || val > 255) + if (ip.Length != 4) return false; + + for (int i = 0; i < ip.Length; i++) + { + string n = ip[i]; + if (!n.IsValidInt()) + return false; + + int val = n.ToInt(); + if (val < 0 || val > 255) + return false; + } } return true; @@ -1745,6 +1863,25 @@ namespace Godot return Uri.EscapeDataString(instance); } + private const string _uniqueNodePrefix = "%"; + private static readonly string[] _invalidNodeNameCharacters = { ".", ":", "@", "/", "\"", _uniqueNodePrefix }; + + /// + /// Removes any characters from the string that are prohibited in + /// names (. : @ / "). + /// + /// The string to sanitize. + /// The string sanitized as a valid node name. + public static string ValidateNodeName(this string instance) + { + string name = instance.Replace(_invalidNodeNameCharacters[0], ""); + for (int i = 1; i < _invalidNodeNameCharacters.Length; i++) + { + name = name.Replace(_invalidNodeNameCharacters[i], ""); + } + return name; + } + /// /// Returns a copy of the string with special characters escaped using the XML standard. /// -- cgit v1.2.3