ConfigProperty.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #nullable enable
  2. using System.Reflection;
  3. using System.Text.Json;
  4. using System.Text.Json.Serialization;
  5. namespace Terminal.Gui;
  6. /// <summary>
  7. /// Holds a property's value and the <see cref="PropertyInfo"/> that allows <see cref="ConfigurationManager"/> to
  8. /// get and set the property's value.
  9. /// </summary>
  10. /// <remarks>
  11. /// Configuration properties must be <see langword="public"/> and <see langword="static"/> and have the
  12. /// <see cref="SerializableConfigurationProperty"/> attribute. If the type of the property requires specialized JSON
  13. /// serialization, a <see cref="JsonConverter"/> must be provided using the <see cref="JsonConverterAttribute"/>
  14. /// attribute.
  15. /// </remarks>
  16. public class ConfigProperty
  17. {
  18. /// <summary>Describes the property.</summary>
  19. public PropertyInfo? PropertyInfo { get; set; }
  20. /// <summary>
  21. /// Holds the property's value as it was either read from the class's implementation or from a config file. If the
  22. /// property has not been set (e.g. because no configuration file specified a value), this will be
  23. /// <see langword="null"/>.
  24. /// </summary>
  25. /// <remarks>
  26. /// On <see langword="set"/>, performs a sparse-copy of the new value to the existing value (only copies elements
  27. /// of the object that are non-null).
  28. /// </remarks>
  29. public object? PropertyValue { get; set; }
  30. /// <summary>Applies the <see cref="PropertyValue"/> to the property described by <see cref="PropertyInfo"/>.</summary>
  31. /// <returns></returns>
  32. public bool Apply ()
  33. {
  34. if (PropertyValue is { })
  35. {
  36. try
  37. {
  38. if (PropertyInfo?.GetValue (null) is { })
  39. {
  40. PropertyInfo?.SetValue (null, DeepMemberWiseCopy (PropertyValue, PropertyInfo?.GetValue (null)));
  41. }
  42. }
  43. catch (TargetInvocationException tie)
  44. {
  45. // Check if there is an inner exception
  46. if (tie.InnerException is { })
  47. {
  48. // Handle the inner exception separately without catching the outer exception
  49. Exception? innerException = tie.InnerException;
  50. // Handle the inner exception here
  51. throw new JsonException (
  52. $"Error Applying Configuration Change: {innerException.Message}",
  53. innerException
  54. );
  55. }
  56. // Handle the outer exception or rethrow it if needed
  57. throw new JsonException ($"Error Applying Configuration Change: {tie.Message}", tie);
  58. }
  59. catch (ArgumentException ae)
  60. {
  61. throw new JsonException (
  62. $"Error Applying Configuration Change ({PropertyInfo?.Name}): {ae.Message}",
  63. ae
  64. );
  65. }
  66. }
  67. return PropertyValue != null;
  68. }
  69. /// <summary>
  70. /// Helper to get either the Json property named (specified by [JsonPropertyName(name)] or the actual property
  71. /// name.
  72. /// </summary>
  73. /// <param name="pi"></param>
  74. /// <returns></returns>
  75. public static string GetJsonPropertyName (PropertyInfo pi)
  76. {
  77. var attr = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute;
  78. return attr?.Name ?? pi.Name;
  79. }
  80. /// <summary>
  81. /// Retrieves (using reflection) the value of the static property described in <see cref="PropertyInfo"/> into
  82. /// <see cref="PropertyValue"/>.
  83. /// </summary>
  84. /// <returns></returns>
  85. public object? RetrieveValue () { return PropertyValue = PropertyInfo!.GetValue (null); }
  86. internal object? UpdateValueFrom (object source)
  87. {
  88. if (source is null)
  89. {
  90. return PropertyValue;
  91. }
  92. Type? ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType);
  93. if (source.GetType () != PropertyInfo!.PropertyType && ut is { } && source.GetType () != ut)
  94. {
  95. throw new ArgumentException (
  96. $"The source object ({
  97. PropertyInfo!.DeclaringType
  98. }.{
  99. PropertyInfo!.Name
  100. }) is not of type {
  101. PropertyInfo!.PropertyType
  102. }."
  103. );
  104. }
  105. if (PropertyValue is { })
  106. {
  107. PropertyValue = DeepMemberWiseCopy (source, PropertyValue);
  108. }
  109. else
  110. {
  111. PropertyValue = source;
  112. }
  113. return PropertyValue;
  114. }
  115. }