using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; namespace Avalonia.Xaml.Interactivity; /// /// A helper class that enables converting values specified in markup (strings) to their object representation. /// [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] internal static class TypeConverterHelper { /// /// Converts string representation of a value to its object representation. /// /// The value to convert. /// The destination type. /// Object representation of the string value. /// destinationType cannot be null. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "DynamicallyAccessedMembers handles most of the problems.")] public static object? Convert(string value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type destinationType) { if (destinationType is null) { throw new ArgumentNullException(nameof(destinationType)); } var destinationTypeFullName = destinationType.FullName; if (destinationTypeFullName is null) { return null; } var scope = GetScope(destinationTypeFullName); // Value types in the "System" namespace must be special cased due to a bug in the xaml compiler if (string.Equals(scope, "System", StringComparison.Ordinal)) { if (string.Equals(destinationTypeFullName, typeof(string).FullName, StringComparison.Ordinal)) { return value; } if (string.Equals(destinationTypeFullName, typeof(bool).FullName, StringComparison.Ordinal)) { return bool.Parse(value); } if (string.Equals(destinationTypeFullName, typeof(int).FullName, StringComparison.Ordinal)) { return int.Parse(value, CultureInfo.InvariantCulture); } if (string.Equals(destinationTypeFullName, typeof(double).FullName, StringComparison.Ordinal)) { return double.Parse(value, CultureInfo.InvariantCulture); } } try { if (destinationType.BaseType == typeof(Enum)) return Enum.Parse(destinationType, value); if (destinationType.GetInterfaces().Any(t => t == typeof(IConvertible))) { return (value as IConvertible).ToType(destinationType, CultureInfo.InvariantCulture); } var converter = TypeDescriptor.GetConverter(destinationType); return converter.ConvertFromInvariantString(value); } catch (ArgumentException) { // not an enum } catch (InvalidCastException) { // not able to convert to anything } return null; } private static string GetScope(string name) { var indexOfLastPeriod = name.LastIndexOf('.'); #if !NET6_0_OR_GREATER if (indexOfLastPeriod != name.Length - 1) { return name.Substring(0, indexOfLastPeriod); } return name; #else return indexOfLastPeriod != name.Length - 1 ? name[..indexOfLastPeriod] : name; #endif } }