#nullable enable using System; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; 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 { /// /// Describes the property. /// public PropertyInfo? PropertyInfo { get; set; } /// /// 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; 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; } 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) { PropertyValue = DeepMemberwiseCopy (source, PropertyValue); } else { PropertyValue = source; } return PropertyValue; } /// /// Retrieves (using reflection) the value of the static property described in /// into . /// /// public object? RetrieveValue () => PropertyValue = PropertyInfo!.GetValue (null); /// /// Applies the to the property described by . /// /// public bool Apply () { if (PropertyValue != null) { try { if (PropertyInfo?.GetValue (null) != null) { PropertyInfo?.SetValue (null, 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 var 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); } catch (ArgumentException ae) { throw new JsonException ($"Error Applying Configuration Change ({PropertyInfo?.Name}): {ae.Message}", ae); } } return PropertyValue != null; } }