ConfigProperty.cs 3.7 KB

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