ConfigProperty.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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 static property described by <see cref="PropertyInfo"/>.</summary>
  31. /// <returns></returns>
  32. public bool Apply ()
  33. {
  34. try
  35. {
  36. if (PropertyInfo?.GetValue (null) is { })
  37. {
  38. var val = DeepMemberWiseCopy (PropertyValue, PropertyInfo?.GetValue (null));
  39. PropertyInfo?.SetValue (null, val);
  40. }
  41. }
  42. catch (TargetInvocationException tie)
  43. {
  44. // Check if there is an inner exception
  45. if (tie.InnerException is { })
  46. {
  47. // Handle the inner exception separately without catching the outer exception
  48. Exception? innerException = tie.InnerException;
  49. // Handle the inner exception here
  50. throw new JsonException (
  51. $"Error Applying Configuration Change: {innerException.Message}",
  52. innerException
  53. );
  54. }
  55. // Handle the outer exception or rethrow it if needed
  56. throw new JsonException ($"Error Applying Configuration Change: {tie.Message}", tie);
  57. }
  58. catch (ArgumentException ae)
  59. {
  60. throw new JsonException (
  61. $"Error Applying Configuration Change ({PropertyInfo?.Name}): {ae.Message}",
  62. ae
  63. );
  64. }
  65. return PropertyValue != null;
  66. }
  67. /// <summary>
  68. /// Helper to get either the Json property named (specified by [JsonPropertyName(name)] or the actual property
  69. /// name.
  70. /// </summary>
  71. /// <param name="pi"></param>
  72. /// <returns></returns>
  73. public static string GetJsonPropertyName (PropertyInfo pi)
  74. {
  75. var attr = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute;
  76. return attr?.Name ?? pi.Name;
  77. }
  78. /// <summary>
  79. /// Retrieves (using reflection) the value of the static property described in <see cref="PropertyInfo"/> into
  80. /// <see cref="PropertyValue"/>.
  81. /// </summary>
  82. /// <returns></returns>
  83. public object? RetrieveValue () { return PropertyValue = PropertyInfo!.GetValue (null); }
  84. /// <summary>
  85. /// Updates (using reflection) <see cref="PropertyValue"/> with the value in <paramref name="source"/> using a deep memberwise copy.
  86. /// </summary>
  87. /// <param name="source"></param>
  88. /// <returns></returns>
  89. /// <exception cref="ArgumentException"></exception>
  90. internal object? UpdateValueFrom (object source)
  91. {
  92. if (source is null)
  93. {
  94. return PropertyValue;
  95. }
  96. Type? ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType);
  97. if (source.GetType () != PropertyInfo!.PropertyType && ut is { } && source.GetType () != ut)
  98. {
  99. throw new ArgumentException (
  100. $"The source object ({
  101. PropertyInfo!.DeclaringType
  102. }.{
  103. PropertyInfo!.Name
  104. }) is not of type {
  105. PropertyInfo!.PropertyType
  106. }."
  107. );
  108. }
  109. if (PropertyValue is { })
  110. {
  111. PropertyValue = DeepMemberWiseCopy (source, PropertyValue);
  112. }
  113. else
  114. {
  115. PropertyValue = source;
  116. }
  117. return PropertyValue;
  118. }
  119. }