ConfigProperty.cs 3.6 KB

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