using System; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; #nullable enable namespace Terminal.Gui; /// /// Holds a property's value and the that allows /// to get and set the property's value. /// /// /// Configuration properties must be and /// and have the /// attribute. If the type of the property requires specialized JSON serialization, /// a must be provided using /// the attribute. /// public class ConfigProperty { private object? propertyValue; /// /// Describes the property. /// public PropertyInfo? PropertyInfo { get; set; } /// /// Helper to get either the Json property named (specified by [JsonPropertyName(name)] /// or the actual property name. /// /// /// public static string GetJsonPropertyName (PropertyInfo pi) { var jpna = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute; return jpna?.Name ?? pi.Name; } /// /// Holds the property's value as it was either read from the class's implementation or from a config file. /// If the property has not been set (e.g. because no configuration file specified a value), /// this will be . /// /// /// On , performs a sparse-copy of the new value to the existing value (only copies elements of /// the object that are non-null). /// public object? PropertyValue { get => propertyValue; set { propertyValue = value; } } internal object? UpdateValueFrom (object source) { if (source == null) { return PropertyValue; } var ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType); if (source.GetType () != PropertyInfo!.PropertyType && (ut != null && source.GetType () != ut)) { throw new ArgumentException ($"The source object ({PropertyInfo!.DeclaringType}.{PropertyInfo!.Name}) is not of type {PropertyInfo!.PropertyType}."); } if (PropertyValue != null && source != null) { PropertyValue = ConfigurationManager.DeepMemberwiseCopy (source, PropertyValue); } else { PropertyValue = source; } return PropertyValue; } /// /// Retrieves (using reflection) the value of the static property described in /// into . /// /// public object? RetrieveValue () { return PropertyValue = PropertyInfo!.GetValue (null); } /// /// Applies the to the property described by . /// /// public bool Apply () { if (PropertyValue != null) { try { PropertyInfo?.SetValue (null, ConfigurationManager.DeepMemberwiseCopy (PropertyValue, PropertyInfo?.GetValue (null))); } catch (TargetInvocationException tie) { // Check if there is an inner exception if (tie.InnerException != null) { // Handle the inner exception separately without catching the outer exception Exception innerException = tie.InnerException; // Handle the inner exception here throw new JsonException ($"Error Applying Configuration Change: {innerException.Message}", innerException); } // Handle the outer exception or rethrow it if needed throw new JsonException ($"Error Applying Configuration Change: {tie.Message}", tie); } } return PropertyValue != null; } }